Merge commit '5b37636b77ffdb29b8314921071f842a566a3b54' into dev-release
Change-Id: Id2b9a5a47f2d47f16a4eed0cc2fcf29885627ea8
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
index e809804..25d24e38 100644
--- a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
+++ b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
@@ -388,6 +388,7 @@
object JvmCompatibility {
val sourceCompatibility = JavaVersion.VERSION_11
val targetCompatibility = JavaVersion.VERSION_11
+ val release = 11
}
object Versions {
@@ -398,7 +399,7 @@
const val guavaVersion = "32.1.2-jre"
const val javassist = "3.29.2-GA"
const val junitVersion = "4.13-beta-2"
- const val kotlinVersion = "1.9.0"
+ const val kotlinVersion = "1.9.20"
const val kotlinMetadataVersion = "2.0.0-Beta5"
const val mockito = "2.10.0"
const val smaliVersion = "3.0.3"
diff --git a/d8_r8/gradle.properties b/d8_r8/gradle.properties
index a82d85e..473e9a7 100644
--- a/d8_r8/gradle.properties
+++ b/d8_r8/gradle.properties
@@ -16,3 +16,28 @@
# Do not download any jdks or detect them. We provide them.
org.gradle.java.installations.auto-detect=false
org.gradle.java.installations.auto-download=false
+# Configure Java toolchains
+#
+# Would have liked to include third_party/openjdk/jdk8/linux-x86, but Gradle
+# does not fully recognize it as a JDK-8.
+#
+# Run
+# tools/gradle.py -q javaToolchains
+# to check the actual JDK's picked up by Gradle.
+#
+# NOTE: Gradle will ignore directories which does not exist, as will be the
+# case for JDK's for platforms different from the one this is running on
+# when using the default downloadDeps setup.
+org.gradle.java.installations.paths=\
+../third_party/openjdk/jdk-11/linux,\
+../third_party/openjdk/jdk-17/linux,\
+../third_party/openjdk/jdk-21/linux,\
+../third_party/openjdk/jdk-22/linux,\
+../third_party/openjdk/jdk-11/osx,\
+../third_party/openjdk/jdk-17/osx,\
+../third_party/openjdk/jdk-21/osx,\
+../third_party/openjdk/jdk-22/osx,\
+../third_party/openjdk/jdk-11/windows,\
+../third_party/openjdk/jdk-17/windows,\
+../third_party/openjdk/jdk-21/windows,\
+../third_party/openjdk/jdk-22/windows
diff --git a/d8_r8/keepanno/build.gradle.kts b/d8_r8/keepanno/build.gradle.kts
index 3d981f6..e58f672 100644
--- a/d8_r8/keepanno/build.gradle.kts
+++ b/d8_r8/keepanno/build.gradle.kts
@@ -13,6 +13,9 @@
}
sourceCompatibility = JvmCompatibility.sourceCompatibility
targetCompatibility = JvmCompatibility.targetCompatibility
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(JvmCompatibility.release)
+ }
withSourcesJar()
}
diff --git a/d8_r8/library_desugar/build.gradle.kts b/d8_r8/library_desugar/build.gradle.kts
index 4094e64..0211cb6 100644
--- a/d8_r8/library_desugar/build.gradle.kts
+++ b/d8_r8/library_desugar/build.gradle.kts
@@ -14,6 +14,9 @@
}
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(JvmCompatibility.release)
+ }
}
dependencies {
diff --git a/d8_r8/main/build.gradle.kts b/d8_r8/main/build.gradle.kts
index f365f9f..9eaf601 100644
--- a/d8_r8/main/build.gradle.kts
+++ b/d8_r8/main/build.gradle.kts
@@ -31,6 +31,9 @@
}
sourceCompatibility = JvmCompatibility.sourceCompatibility
targetCompatibility = JvmCompatibility.targetCompatibility
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(JvmCompatibility.release)
+ }
withSourcesJar()
}
diff --git a/d8_r8/resourceshrinker/build.gradle.kts b/d8_r8/resourceshrinker/build.gradle.kts
index 183d1ed..db14c2f 100644
--- a/d8_r8/resourceshrinker/build.gradle.kts
+++ b/d8_r8/resourceshrinker/build.gradle.kts
@@ -16,6 +16,9 @@
}
sourceCompatibility = JvmCompatibility.sourceCompatibility
targetCompatibility = JvmCompatibility.targetCompatibility
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(JvmCompatibility.release)
+ }
withSourcesJar()
}
@@ -47,7 +50,7 @@
kotlinOptions {
// We cannot use languageVersion.set(JavaLanguageVersion.of(8)) because gradle cannot figure
// out that the jdk is 1_8 and will try to download it.
- jvmTarget = "11"
+ jvmTarget = "${JvmCompatibility.release}"
}
}
diff --git a/d8_r8/test/build.gradle.kts b/d8_r8/test/build.gradle.kts
index 14b066a..7b2de4e 100644
--- a/d8_r8/test/build.gradle.kts
+++ b/d8_r8/test/build.gradle.kts
@@ -12,8 +12,11 @@
}
java {
- sourceCompatibility = JavaVersion.VERSION_17
- targetCompatibility = JavaVersion.VERSION_17
+ sourceCompatibility = JvmCompatibility.sourceCompatibility
+ targetCompatibility = JvmCompatibility.targetCompatibility
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(JvmCompatibility.release)
+ }
}
dependencies { }
diff --git a/d8_r8/test_modules/testbase/build.gradle.kts b/d8_r8/test_modules/testbase/build.gradle.kts
index 2d66a1c..336033b 100644
--- a/d8_r8/test_modules/testbase/build.gradle.kts
+++ b/d8_r8/test_modules/testbase/build.gradle.kts
@@ -24,6 +24,9 @@
// compatible with java toolchains.
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(JvmCompatibility.release)
+ }
}
// If we depend on keepanno by referencing the project source outputs we get an error regarding
diff --git a/d8_r8/test_modules/tests_bootstrap/build.gradle.kts b/d8_r8/test_modules/tests_bootstrap/build.gradle.kts
index f6120cc..7b900dc 100644
--- a/d8_r8/test_modules/tests_bootstrap/build.gradle.kts
+++ b/d8_r8/test_modules/tests_bootstrap/build.gradle.kts
@@ -23,6 +23,9 @@
// compatible with java toolchains.
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(JvmCompatibility.release)
+ }
}
val testbaseJavaCompileTask = projectTask("testbase", "compileJava")
diff --git a/d8_r8/test_modules/tests_java_11/build.gradle.kts b/d8_r8/test_modules/tests_java_11/build.gradle.kts
index 3c99b88..f0748b2 100644
--- a/d8_r8/test_modules/tests_java_11/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_11/build.gradle.kts
@@ -29,6 +29,7 @@
implementation(files(testbaseDepsJarTask.outputs.files.getSingleFile()))
implementation(testbaseJavaCompileTask.outputs.files)
implementation(mainCompileTask.outputs.files)
+ implementation(projectTask("main", "processResources").outputs.files)
}
// We just need to register the examples jars for it to be referenced by other modules.
diff --git a/d8_r8/test_modules/tests_java_17/build.gradle.kts b/d8_r8/test_modules/tests_java_17/build.gradle.kts
index 2079c40..d46f487 100644
--- a/d8_r8/test_modules/tests_java_17/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_17/build.gradle.kts
@@ -32,6 +32,7 @@
implementation(files(testbaseDepsJarTask.outputs.files.getSingleFile()))
implementation(testbaseJavaCompileTask.outputs.files)
implementation(mainCompileTask.outputs.files)
+ implementation(projectTask("main", "processResources").outputs.files)
}
// We just need to register the examples jars for it to be referenced by other modules.
diff --git a/d8_r8/test_modules/tests_java_21/build.gradle.kts b/d8_r8/test_modules/tests_java_21/build.gradle.kts
index afc55d0..403eb85 100644
--- a/d8_r8/test_modules/tests_java_21/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_21/build.gradle.kts
@@ -28,6 +28,7 @@
implementation(files(testbaseDepsJarTask.outputs.files.getSingleFile()))
implementation(testbaseJavaCompileTask.outputs.files)
implementation(mainCompileTask.outputs.files)
+ implementation(projectTask("main", "processResources").outputs.files)
}
// We just need to register the examples jars for it to be referenced by other modules.
diff --git a/d8_r8/test_modules/tests_java_22/build.gradle.kts b/d8_r8/test_modules/tests_java_22/build.gradle.kts
index 546d795..938e1cb 100644
--- a/d8_r8/test_modules/tests_java_22/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_22/build.gradle.kts
@@ -28,6 +28,7 @@
implementation(files(testbaseDepsJarTask.outputs.files.getSingleFile()))
implementation(testbaseJavaCompileTask.outputs.files)
implementation(mainCompileTask.outputs.files)
+ implementation(projectTask("main", "processResources").outputs.files)
}
tasks {
diff --git a/d8_r8/test_modules/tests_java_8/build.gradle.kts b/d8_r8/test_modules/tests_java_8/build.gradle.kts
index 779cd6c..31914df 100644
--- a/d8_r8/test_modules/tests_java_8/build.gradle.kts
+++ b/d8_r8/test_modules/tests_java_8/build.gradle.kts
@@ -21,11 +21,13 @@
srcDir(root.resolveAll("build", "generated", "test", "java"))
}
}
-
// We are using a new JDK to compile to an older language version, which is not directly
// compatible with java toolchains.
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(JvmCompatibility.release)
+ }
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/AnnotationPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/AnnotationPattern.java
index cfd4302..0327246 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/AnnotationPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/AnnotationPattern.java
@@ -69,7 +69,7 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern namePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern namePattern() default @ClassNamePattern(unqualifiedName = "");
/**
* Specify which retention policies must be set for the annotations.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ClassNamePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ClassNamePattern.java
index 58b9fe8..3c9e261 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ClassNamePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/ClassNamePattern.java
@@ -29,8 +29,8 @@
*
* <ul>
* <li>constant
- * <li>simpleName
- * <li>simpleNamePattern
+ * <li>unqualifiedName
+ * <li>unqualifiedNamePattern
* <li>packageName
* </ul>
*
@@ -45,8 +45,8 @@
*
* <ul>
* <li>name
- * <li>simpleName
- * <li>simpleNamePattern
+ * <li>unqualifiedName
+ * <li>unqualifiedNamePattern
* <li>packageName
* </ul>
*
@@ -55,38 +55,40 @@
Class<?> constant() default Object.class;
/**
- * Exact simple name of the class or interface.
+ * Exact and unqualified name of the class or interface.
*
- * <p>For example, the simple name of {@code com.example.MyClass} is {@code MyClass}.
+ * <p>For example, the unqualified name of {@code com.example.MyClass} is {@code MyClass}. Note
+ * that for inner classes a `$` will appear in the unqualified name,such as, {@code
+ * MyClass$MyInnerClass}.
*
- * <p>The default matches any simple name.
+ * <p>The default matches any unqualified name.
*
- * <p>Mutually exclusive with the following other properties defining class-simple-name:
+ * <p>Mutually exclusive with the following other properties defining class-unqualified-name:
*
* <ul>
- * <li>simpleNamePattern
+ * <li>unqualifiedNamePattern
* <li>name
* <li>constant
* </ul>
*/
- String simpleName() default "";
+ String unqualifiedName() default "";
/**
- * Define the simple-name pattern by a string pattern.
+ * Define the unqualified class-name pattern by a string pattern.
*
- * <p>The default matches any simple name.
+ * <p>The default matches any unqualified name.
*
- * <p>Mutually exclusive with the following other properties defining class-simple-name:
+ * <p>Mutually exclusive with the following other properties defining class-unqualified-name:
*
* <ul>
- * <li>simpleName
+ * <li>unqualifiedName
* <li>name
* <li>constant
* </ul>
*
- * @return The string pattern of the class simple name.
+ * @return The string pattern of the unqualified class name.
*/
- StringPattern simpleNamePattern() default @StringPattern(exact = "");
+ StringPattern unqualifiedNamePattern() default @StringPattern(exact = "");
/**
* Exact package name of the class or interface.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/InstanceOfPattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/InstanceOfPattern.java
index 591d750..975f144 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/InstanceOfPattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/InstanceOfPattern.java
@@ -31,5 +31,5 @@
boolean inclusive() default true;
/** Instances of classes matching the class-name pattern. */
- ClassNamePattern classNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern classNamePattern() default @ClassNamePattern(unqualifiedName = "");
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
index f92ab49..68adfaa 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepBinding.java
@@ -140,7 +140,7 @@
*
* @return The class-name pattern that defines the class.
*/
- ClassNamePattern classNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern classNamePattern() default @ClassNamePattern(unqualifiedName = "");
/**
* Define the instance-of pattern as classes that are instances of the fully qualified class name.
@@ -295,7 +295,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern classAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern classAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the member-annotated-by pattern by fully qualified class name.
@@ -355,7 +356,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern memberAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern memberAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the member-access pattern by matching on access flags.
@@ -422,7 +424,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern methodAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern methodAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the method-access pattern by matching on access flags.
@@ -604,7 +607,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern fieldAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern fieldAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the field-access pattern by matching on access flags.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
index 65d13d8..90b641e 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepCondition.java
@@ -102,7 +102,7 @@
*
* @return The class-name pattern that defines the class.
*/
- ClassNamePattern classNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern classNamePattern() default @ClassNamePattern(unqualifiedName = "");
/**
* Define the instance-of pattern as classes that are instances of the fully qualified class name.
@@ -257,7 +257,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern classAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern classAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the member pattern in full by a reference to a binding.
@@ -330,7 +331,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern memberAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern memberAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the member-access pattern by matching on access flags.
@@ -402,7 +404,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern methodAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern methodAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the method-access pattern by matching on access flags.
@@ -612,7 +615,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern fieldAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern fieldAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the field-access pattern by matching on access flags.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java
index aac3246..6a06f60 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepForApi.java
@@ -115,7 +115,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern memberAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern memberAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the member-access pattern by matching on access flags.
@@ -182,7 +183,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern methodAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern methodAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the method-access pattern by matching on access flags.
@@ -364,7 +366,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern fieldAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern fieldAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the field-access pattern by matching on access flags.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
index e7e8c74..04f1aac 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/KeepTarget.java
@@ -199,7 +199,7 @@
*
* @return The class-name pattern that defines the class.
*/
- ClassNamePattern classNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern classNamePattern() default @ClassNamePattern(unqualifiedName = "");
/**
* Define the instance-of pattern as classes that are instances of the fully qualified class name.
@@ -354,7 +354,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern classAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern classAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the member pattern in full by a reference to a binding.
@@ -427,7 +428,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern memberAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern memberAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the member-access pattern by matching on access flags.
@@ -499,7 +501,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern methodAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern methodAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the method-access pattern by matching on access flags.
@@ -709,7 +712,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern fieldAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern fieldAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the field-access pattern by matching on access flags.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/TypePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/TypePattern.java
index 2fa0ffd..47fdb1d 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/TypePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/TypePattern.java
@@ -62,5 +62,5 @@
* <li>constant
* </ul>
*/
- ClassNamePattern classNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern classNamePattern() default @ClassNamePattern(unqualifiedName = "");
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java
index 076a93c..4bf6253 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByNative.java
@@ -205,7 +205,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern memberAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern memberAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the member-access pattern by matching on access flags.
@@ -272,7 +273,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern methodAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern methodAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the method-access pattern by matching on access flags.
@@ -454,7 +456,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern fieldAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern fieldAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the field-access pattern by matching on access flags.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java
index cf2225d..4a0c692 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/annotations/UsedByReflection.java
@@ -205,7 +205,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern memberAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern memberAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the member-access pattern by matching on access flags.
@@ -272,7 +273,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern methodAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern methodAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the method-access pattern by matching on access flags.
@@ -454,7 +456,8 @@
*
* @return The class-name pattern that defines the annotation.
*/
- ClassNamePattern fieldAnnotatedByClassNamePattern() default @ClassNamePattern(simpleName = "");
+ ClassNamePattern fieldAnnotatedByClassNamePattern() default
+ @ClassNamePattern(unqualifiedName = "");
/**
* Define the field-access pattern by matching on access flags.
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassNameParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassNameParser.java
index e066873..538c8ca 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassNameParser.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassNameParser.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.keepanno.asm;
import com.android.tools.r8.keepanno.asm.ClassNameParser.ClassNameProperty;
-import com.android.tools.r8.keepanno.asm.ClassSimpleNameParser.ClassSimpleNameProperty;
+import com.android.tools.r8.keepanno.asm.ClassUnqualifiedNameParser.ClassUnqualifiedNameProperty;
import com.android.tools.r8.keepanno.asm.PackageNameParser.PackageNameProperty;
import com.android.tools.r8.keepanno.asm.TypeParser.TypeProperty;
import com.android.tools.r8.keepanno.ast.AnnotationConstants.ClassNamePattern;
@@ -85,23 +85,27 @@
getParsingContext().property(name).annotation(descriptor);
ClassNameParser fullNameParser = new ClassNameParser(parsingContext);
PackageNameParser packageParser = new PackageNameParser(parsingContext);
- ClassSimpleNameParser simpleNameParser = new ClassSimpleNameParser(parsingContext);
+ ClassUnqualifiedNameParser unqualifiedNameParser =
+ new ClassUnqualifiedNameParser(parsingContext);
fullNameParser.setProperty(ClassNamePattern.name, ClassNameProperty.NAME);
fullNameParser.setProperty(ClassNamePattern.constant, ClassNameProperty.CONSTANT);
packageParser.setProperty(ClassNamePattern.packageName, PackageNameProperty.NAME);
- simpleNameParser.setProperty(ClassNamePattern.simpleName, ClassSimpleNameProperty.NAME);
- simpleNameParser.setProperty(
- ClassNamePattern.simpleNamePattern, ClassSimpleNameProperty.PATTERN);
+ unqualifiedNameParser.setProperty(
+ ClassNamePattern.unqualifiedName, ClassUnqualifiedNameProperty.NAME);
+ unqualifiedNameParser.setProperty(
+ ClassNamePattern.unqualifiedNamePattern, ClassUnqualifiedNameProperty.PATTERN);
return new ParserVisitor(
parsingContext,
- ImmutableList.of(fullNameParser, packageParser, simpleNameParser),
+ ImmutableList.of(fullNameParser, packageParser, unqualifiedNameParser),
() -> {
if (fullNameParser.isDeclared()) {
- if (simpleNameParser.isDeclared() || packageParser.isDeclared()) {
+ if (unqualifiedNameParser.isDeclared() || packageParser.isDeclared()) {
throw parsingContext.error(
"Cannot specify both the full class name and its "
- + (simpleNameParser.isDeclared() ? "simple name" : "package"));
+ + (unqualifiedNameParser.isDeclared()
+ ? "unqualified name"
+ : "package"));
}
setValue.accept(fullNameParser.getValue());
return;
@@ -111,7 +115,7 @@
.setPackagePattern(
packageParser.getValueOrDefault(KeepPackagePattern.any()))
.setNamePattern(
- simpleNameParser.getValueOrDefault(
+ unqualifiedNameParser.getValueOrDefault(
KeepUnqualfiedClassNamePattern.any()))
.build());
});
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassSimpleNameParser.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassUnqualifiedNameParser.java
similarity index 79%
rename from src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassSimpleNameParser.java
rename to src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassUnqualifiedNameParser.java
index d65aa22..896fea9 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassSimpleNameParser.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/ClassUnqualifiedNameParser.java
@@ -1,34 +1,34 @@
-// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2024, 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.keepanno.asm;
-import com.android.tools.r8.keepanno.asm.ClassSimpleNameParser.ClassSimpleNameProperty;
+import com.android.tools.r8.keepanno.asm.ClassUnqualifiedNameParser.ClassUnqualifiedNameProperty;
import com.android.tools.r8.keepanno.asm.StringPatternParser.StringProperty;
import com.android.tools.r8.keepanno.ast.KeepUnqualfiedClassNamePattern;
import com.android.tools.r8.keepanno.ast.ParsingContext;
import java.util.function.Consumer;
import org.objectweb.asm.AnnotationVisitor;
-public class ClassSimpleNameParser
- extends PropertyParserBase<KeepUnqualfiedClassNamePattern, ClassSimpleNameProperty> {
+public class ClassUnqualifiedNameParser
+ extends PropertyParserBase<KeepUnqualfiedClassNamePattern, ClassUnqualifiedNameProperty> {
private final StringPatternParser parser;
- public ClassSimpleNameParser(ParsingContext parsingContext) {
+ public ClassUnqualifiedNameParser(ParsingContext parsingContext) {
super(parsingContext);
parser = new StringPatternParser(parsingContext);
}
- public enum ClassSimpleNameProperty {
+ public enum ClassUnqualifiedNameProperty {
NAME,
PATTERN
}
@Override
public boolean tryProperty(
- ClassSimpleNameProperty property,
+ ClassUnqualifiedNameProperty property,
String name,
Object value,
Consumer<KeepUnqualfiedClassNamePattern> setValue) {
@@ -47,7 +47,7 @@
@Override
AnnotationVisitor tryPropertyAnnotation(
- ClassSimpleNameProperty property,
+ ClassUnqualifiedNameProperty property,
String name,
String descriptor,
Consumer<KeepUnqualfiedClassNamePattern> setValue) {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
index 07789a5..9151b1b 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/asm/KeepEdgeWriter.java
@@ -503,7 +503,7 @@
v.visit(ClassNamePattern.packageName, packagePattern.getExactPackageAsString());
}
writeStringPattern(
- clazz.getNamePattern().asStringPattern(), ClassNamePattern.simpleNamePattern, v);
+ clazz.getNamePattern().asStringPattern(), ClassNamePattern.unqualifiedNamePattern, v);
});
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
index f529bd1..9b0ae29 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/AnnotationConstants.java
@@ -245,9 +245,9 @@
public static final String classNameGroup = "class-name";
public static final String name = "name";
public static final String constant = "constant";
- public static final String classSimpleNameGroup = "class-simple-name";
- public static final String simpleName = "simpleName";
- public static final String simpleNamePattern = "simpleNamePattern";
+ public static final String classUnqualifiedNameGroup = "class-unqualified-name";
+ public static final String unqualifiedName = "unqualifiedName";
+ public static final String unqualifiedNamePattern = "unqualifiedNamePattern";
public static final String packageName = "packageName";
}
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
index 9932654..e2ce313 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepEdge.java
@@ -81,7 +81,7 @@
* ::= any
* | PACKAGE_PATTERN UNQUALIFIED_CLASS_NAME_PATTERN
*
- * UNQUALIFIED_CLASS_NAME_PATTERN ::= any | exact simple-class-name
+ * UNQUALIFIED_CLASS_NAME_PATTERN ::= STRING_PATTERN
*
* INSTANCE_OF_PATTERN ::= INSTANCE_OF_PATTERN_INCLUSIVE | INSTANCE_OF_PATTERN_EXCLUSIVE
* INSTANCE_OF_PATTERN_INCLUSIVE ::= QUALIFIED_CLASS_NAME_PATTERN
@@ -128,6 +128,10 @@
*
* RETENTION_POLICY
* ::= RUNTIME | CLASS
+ *
+ * STRING_PATTERN
+ * ::= exact string-content
+ * | [prefix string-content] any [suffix string-content]
* </pre>
*/
public final class KeepEdge extends KeepDeclaration {
diff --git a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepUnqualfiedClassNamePattern.java b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepUnqualfiedClassNamePattern.java
index abb24be..a974f67 100644
--- a/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepUnqualfiedClassNamePattern.java
+++ b/src/keepanno/java/com/android/tools/r8/keepanno/ast/KeepUnqualfiedClassNamePattern.java
@@ -24,26 +24,26 @@
return new Builder();
}
- private final KeepStringPattern simpleNamePattern;
+ private final KeepStringPattern unqualifiedNamePattern;
- private KeepUnqualfiedClassNamePattern(KeepStringPattern simpleNamePattern) {
- this.simpleNamePattern = simpleNamePattern;
+ private KeepUnqualfiedClassNamePattern(KeepStringPattern unqualifiedNamePattern) {
+ this.unqualifiedNamePattern = unqualifiedNamePattern;
}
public boolean isAny() {
- return simpleNamePattern.isAny();
+ return unqualifiedNamePattern.isAny();
}
public boolean isExact() {
- return simpleNamePattern.isExact();
+ return unqualifiedNamePattern.isExact();
}
public String asExactString() {
- return simpleNamePattern.asExactString();
+ return unqualifiedNamePattern.asExactString();
}
public KeepStringPattern asStringPattern() {
- return simpleNamePattern;
+ return unqualifiedNamePattern;
}
@Override
@@ -55,17 +55,17 @@
return false;
}
KeepUnqualfiedClassNamePattern that = (KeepUnqualfiedClassNamePattern) o;
- return simpleNamePattern.equals(that.simpleNamePattern);
+ return unqualifiedNamePattern.equals(that.unqualifiedNamePattern);
}
@Override
public int hashCode() {
- return simpleNamePattern.hashCode();
+ return unqualifiedNamePattern.hashCode();
}
@Override
public String toString() {
- return simpleNamePattern.toString();
+ return unqualifiedNamePattern.toString();
}
public static class Builder {
diff --git a/src/main/java/com/android/tools/r8/D8CommandParser.java b/src/main/java/com/android/tools/r8/D8CommandParser.java
index a16189b..a6e5d5f 100644
--- a/src/main/java/com/android/tools/r8/D8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/D8CommandParser.java
@@ -396,6 +396,8 @@
if (globalsOutputPath != null) {
builder.setGlobalSyntheticsOutput(globalsOutputPath);
}
- return builder.setOutput(outputPath, outputMode);
+ builder.setOutput(outputPath, outputMode);
+ builder.setEnableExperimentalMissingLibraryApiModeling(true);
+ return builder;
}
}
diff --git a/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java b/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
index ccfe752..9b9989a 100644
--- a/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
+++ b/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
@@ -53,6 +53,7 @@
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.ReachabilitySensitiveValue;
import com.android.tools.r8.utils.SelfRetraceTest;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
@@ -266,7 +267,8 @@
DexEncodedField.EMPTY_ARRAY,
MethodCollectionFactory.empty(),
factory.getSkipNameValidationForTesting(),
- DexProgramClass::invalidChecksumRequest);
+ DexProgramClass::invalidChecksumRequest,
+ ReachabilitySensitiveValue.DISABLED);
}
private static void createAllApiStubs(
@@ -287,7 +289,7 @@
if (notModeledTypes.contains(libraryClass.getClassReference().getTypeName())) {
return;
}
- if (ApiReferenceStubber.isJavaType(libraryClass.getType(), factory)) {
+ if (ApiReferenceStubber.isNeverStubbedType(libraryClass.getType(), factory)) {
return;
}
KnownApiLevel knownApiLevel =
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 7cabfb6..851df61 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -465,7 +465,7 @@
new CfOpenClosedInterfacesAnalysis(appViewWithLiveness).run(executorService);
// TODO(b/225838009): Move higher up.
- LirConverter.enterLirSupportedPhase(appView, executorService);
+ LirConverter.enterLirSupportedPhase(appViewWithLiveness, executorService);
assert verifyNoJarApplicationReaders(appView.appInfo().classes());
assert appView.checkForTesting(() -> allReferencesAssignedApiLevel(appViewWithLiveness));
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
index d3fafc4..f663fd4 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -159,7 +159,7 @@
}
private void findReferencedLibraryClasses(DexType type, DexProgramClass context) {
- if (!type.isClassType() || isJavaType(type, appView.dexItemFactory())) {
+ if (!type.isClassType() || isNeverStubbedType(type, appView.dexItemFactory())) {
return;
}
DexClass clazz = appView.definitionFor(type);
@@ -182,10 +182,13 @@
}
}
- @SuppressWarnings("ReferenceEquality")
- public static boolean isJavaType(DexType type, DexItemFactory factory) {
+ public static boolean isNeverStubbedType(DexType type, DexItemFactory factory) {
+ return isJavaType(type, factory);
+ }
+
+ private static boolean isJavaType(DexType type, DexItemFactory factory) {
DexString typeDescriptor = type.getDescriptor();
- return type == factory.objectType
+ return type.isIdenticalTo(factory.objectType)
|| typeDescriptor.startsWith(factory.comSunDescriptorPrefix)
|| typeDescriptor.startsWith(factory.javaDescriptorPrefix)
|| typeDescriptor.startsWith(factory.javaxDescriptorPrefix)
@@ -201,7 +204,7 @@
ApiReferenceStubberEventConsumer eventConsumer) {
DexItemFactory factory = appView.dexItemFactory();
// Do not stub the anything starting with java (including the object type).
- if (isJavaType(libraryClass.getType(), factory)) {
+ if (isNeverStubbedType(libraryClass.getType(), factory)) {
return;
}
// Check if desugared library will bridge the type.
diff --git a/src/main/java/com/android/tools/r8/dex/DexParser.java b/src/main/java/com/android/tools/r8/dex/DexParser.java
index 5ddb8c1..22cf38f 100644
--- a/src/main/java/com/android/tools/r8/dex/DexParser.java
+++ b/src/main/java/com/android/tools/r8/dex/DexParser.java
@@ -73,6 +73,7 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.ReachabilitySensitiveValue;
import com.google.common.io.ByteStreams;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -900,7 +901,10 @@
virtualMethods,
dexItemFactory.getSkipNameValidationForTesting(),
checksumSupplier,
- null);
+ null,
+ // Interpreting reachability sensitivity from DEX inputs is not supported.
+ // The compiler does not support building IR from DEX with debug information.
+ ReachabilitySensitiveValue.DISABLED);
classCollection.accept(clazz); // Update the application object.
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/ClassKind.java b/src/main/java/com/android/tools/r8/graph/ClassKind.java
index 0e3e660..9d6df7a 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassKind.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassKind.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.MethodCollection.MethodCollectionFactory;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.synthesis.SyntheticMarker;
+import com.android.tools.r8.utils.ReachabilitySensitiveValue;
import java.util.List;
import java.util.function.Predicate;
@@ -38,7 +39,8 @@
virtualMethods,
skipNameValidationForTesting,
checksumSupplier,
- syntheticMarker) ->
+ syntheticMarker,
+ reachabilitySensitive) ->
new DexProgramClass(
type,
originKind,
@@ -60,6 +62,7 @@
MethodCollectionFactory.fromMethods(directMethods, virtualMethods),
skipNameValidationForTesting,
checksumSupplier,
+ reachabilitySensitive,
syntheticMarker),
DexClass::isProgramClass);
public static ClassKind<DexClasspathClass> CLASSPATH =
@@ -85,7 +88,8 @@
virtualMethods,
skipNameValidationForTesting,
checksumSupplier,
- syntheticMarker) ->
+ syntheticMarker,
+ unusedReachabilitySensitive) ->
new DexClasspathClass(
type,
kind,
@@ -130,7 +134,8 @@
virtualMethods,
skipNameValidationForTesting,
checksumSupplier,
- syntheticMarker) ->
+ syntheticMarker,
+ unusedReachabilitySensitive) ->
new DexLibraryClass(
type,
kind,
@@ -176,7 +181,8 @@
DexEncodedMethod[] virtualMethods,
boolean skipNameValidationForTesting,
ChecksumSupplier checksumSupplier,
- SyntheticMarker syntheticMarker);
+ SyntheticMarker syntheticMarker,
+ ReachabilitySensitiveValue reachabilitySensitive);
}
private final Factory<C> factory;
@@ -209,7 +215,8 @@
DexEncodedMethod[] virtualMethods,
boolean skipNameValidationForTesting,
ChecksumSupplier checksumSupplier,
- SyntheticMarker syntheticMarker) {
+ SyntheticMarker syntheticMarker,
+ ReachabilitySensitiveValue reachabilitySensitive) {
return factory.create(
type,
kind,
@@ -232,7 +239,8 @@
virtualMethods,
skipNameValidationForTesting,
checksumSupplier,
- syntheticMarker);
+ syntheticMarker,
+ reachabilitySensitive);
}
public boolean isOfKind(DexClass clazz) {
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 5f72db6..bdea90a 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -302,6 +302,7 @@
return true;
}
+ @Override
public FieldTypeSignature getGenericSignature() {
return genericSignature;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
index f7cc258..0365247 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMember.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.graph;
import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.graph.GenericSignature.DexDefinitionSignature;
import com.android.tools.r8.ir.optimize.info.MemberOptimizationInfo;
import com.android.tools.r8.kotlin.KotlinMemberLevelInfo;
import java.util.function.Consumer;
@@ -40,6 +41,8 @@
public abstract void clearKotlinInfo();
+ public abstract DexDefinitionSignature<?> getGenericSignature();
+
public abstract void clearGenericSignature();
public DexType getHolderType() {
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 6c86c27..bd27dfc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -1372,6 +1372,7 @@
}
}
+ @Override
public MethodTypeSignature getGenericSignature() {
return genericSignature;
}
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 c43043c..a7e37bc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -809,8 +809,11 @@
public final DexType annotationCovariantReturnTypes =
createStaticallyKnownType(
"Ldalvik/annotation/codegen/CovariantReturnType$CovariantReturnTypes;");
+
+ public final String annotationReachabilitySensitiveDesc =
+ "Ldalvik/annotation/optimization/ReachabilitySensitive;";
public final DexType annotationReachabilitySensitive =
- createStaticallyKnownType("Ldalvik/annotation/optimization/ReachabilitySensitive;");
+ createStaticallyKnownType(annotationReachabilitySensitiveDesc);
private static final String METAFACTORY_METHOD_NAME = "metafactory";
private static final String METAFACTORY_ALT_METHOD_NAME = "altMetafactory";
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 68bc137..5b0aec4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -21,7 +21,7 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.synthesis.SyntheticMarker;
import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.OptionalBool;
+import com.android.tools.r8.utils.ReachabilitySensitiveValue;
import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.structural.Ordered;
import com.android.tools.r8.utils.structural.StructuralItem;
@@ -54,7 +54,7 @@
private CfVersion initialClassFileVersion = null;
private boolean deprecated = false;
private KotlinClassLevelInfo kotlinInfo = getNoKotlinInfo();
- private OptionalBool reachabilitySensitive = OptionalBool.unknown();
+ private final ReachabilitySensitiveValue reachabilitySensitive;
private final ChecksumSupplier checksumSupplier;
@@ -81,6 +81,7 @@
MethodCollectionFactory methodCollectionFactory,
boolean skipNameValidationForTesting,
ChecksumSupplier checksumSupplier,
+ ReachabilitySensitiveValue reachabilitySensitive,
SyntheticMarker syntheticMarker) {
super(
sourceFile,
@@ -106,6 +107,7 @@
this.originKind = originKind;
this.checksumSupplier = checksumSupplier;
this.syntheticMarker = syntheticMarker;
+ this.reachabilitySensitive = reachabilitySensitive;
}
public DexProgramClass(
@@ -128,7 +130,8 @@
DexEncodedField[] instanceFields,
MethodCollectionFactory methodCollectionFactory,
boolean skipNameValidationForTesting,
- ChecksumSupplier checksumSupplier) {
+ ChecksumSupplier checksumSupplier,
+ ReachabilitySensitiveValue reachabilitySensitive) {
this(
type,
originKind,
@@ -150,6 +153,7 @@
methodCollectionFactory,
skipNameValidationForTesting,
checksumSupplier,
+ reachabilitySensitive,
null);
}
@@ -174,7 +178,8 @@
DexEncodedField.EMPTY_ARRAY,
MethodCollectionFactory.empty(),
false,
- DexProgramClass::invalidChecksumRequest);
+ DexProgramClass::invalidChecksumRequest,
+ ReachabilitySensitiveValue.DISABLED);
}
@Override
@@ -204,24 +209,12 @@
* that is the case, dead reference elimination is disabled and locals are kept alive for their
* entire scope.
*/
- public boolean getOrComputeReachabilitySensitive(AppView<?> appView) {
- if (reachabilitySensitive.isUnknown()) {
- reachabilitySensitive = OptionalBool.of(internalComputeReachabilitySensitive(appView));
- }
- return reachabilitySensitive.isTrue();
+ public boolean isReachabilitySensitive() {
+ return getReachabilitySensitiveValue().isEnabled();
}
- @SuppressWarnings("ReferenceEquality")
- private boolean internalComputeReachabilitySensitive(AppView<?> appView) {
- DexItemFactory dexItemFactory = appView.dexItemFactory();
- for (DexEncodedMember<?, ?> member : members()) {
- for (DexAnnotation annotation : member.annotations().annotations) {
- if (annotation.annotation.type == dexItemFactory.annotationReachabilitySensitive) {
- return true;
- }
- }
- }
- return false;
+ public ReachabilitySensitiveValue getReachabilitySensitiveValue() {
+ return reachabilitySensitive;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
index 1f97831..52ae66a 100644
--- a/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DirectMappedDexApplication.java
@@ -238,7 +238,11 @@
public void addProgramClassPotentiallyOverridingNonProgramClass(DexProgramClass clazz) {
addProgramClass(clazz);
pendingClasspathRemovalIfPresent.add(clazz.type);
- if (libraryClasses.containsKey(clazz.type)) {
+ // When java.lang.Record is added remove the library class, as this program class will
+ // eventually be renamed to com.android.tools.r8.RecordTag so there should not be a
+ // multi-resolution result when looking up java.lang.Record.
+ if (clazz.type.isIdenticalTo(options.dexItemFactory().recordType)
+ && libraryClasses.containsKey(clazz.type)) {
ensureMutableLibraryClassesMap();
libraryClasses.remove(clazz.type);
}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignature.java b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
index a339bde..29fc029 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -122,7 +122,7 @@
return EMPTY_TYPE_SIGNATURES;
}
- interface DexDefinitionSignature<T extends DexDefinition> {
+ public interface DexDefinitionSignature<T extends DexDefinition> {
default boolean isClassSignature() {
return false;
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 089194a..57b427e 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -48,10 +48,10 @@
import com.android.tools.r8.utils.FieldSignatureEquivalence;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.MethodSignatureEquivalence;
+import com.android.tools.r8.utils.ReachabilitySensitiveValue;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.collect.Iterables;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
@@ -258,7 +258,7 @@
private final List<DexEncodedMethod> directMethods = new ArrayList<>();
private final List<DexEncodedMethod> virtualMethods = new ArrayList<>();
private final Set<Wrapper<DexMethod>> methodSignatures = new HashSet<>();
- private boolean hasReachabilitySensitiveMethod = false;
+ private boolean hasReachabilitySensitiveMember = false;
private SyntheticMarker syntheticMarker = null;
public CreateDexClassVisitor(
@@ -492,7 +492,6 @@
addAnnotation(DexAnnotation.createAnnotationDefaultAnnotation(
type, defaultAnnotations, application.getFactory()));
}
- checkReachabilitySensitivity();
checkRecord();
T clazz =
classKind.create(
@@ -517,7 +516,8 @@
virtualMethods.toArray(DexEncodedMethod.EMPTY_ARRAY),
application.getFactory().getSkipNameValidationForTesting(),
getChecksumSupplier(classKind),
- syntheticMarker);
+ syntheticMarker,
+ ReachabilitySensitiveValue.fromBoolean(hasReachabilitySensitiveMember));
application.checkClassForMethodHandlesLookup(clazz, classKind);
InnerClassAttribute innerClassAttribute = clazz.getInnerClassAttributeForThisClass();
// A member class should not be a local or anonymous class.
@@ -599,33 +599,6 @@
}
}
- // If anything is marked reachability sensitive, all methods need to be parsed including
- // locals information. This propagates the reachability sensitivity bit so that if any field
- // or method is annotated, all methods get parsed with locals information.
- private void checkReachabilitySensitivity() {
- if (hasReachabilitySensitiveMethod || hasReachabilitySensitiveField()) {
- for (DexEncodedMethod method : Iterables.concat(directMethods, virtualMethods)) {
- Code code = method.getCode();
- if (code != null && code.isCfCode()) {
- code.asLazyCfCode().markReachabilitySensitive();
- }
- }
- }
- }
-
- @SuppressWarnings("ReferenceEquality")
- private boolean hasReachabilitySensitiveField() {
- DexType reachabilitySensitive = application.getFactory().annotationReachabilitySensitive;
- for (DexEncodedField field : Iterables.concat(instanceFields, staticFields)) {
- for (DexAnnotation annotation : field.annotations().annotations) {
- if (annotation.annotation.type == reachabilitySensitive) {
- return true;
- }
- }
- }
- return false;
- }
-
private void addDefaultAnnotation(String name, DexValue value) {
if (defaultAnnotations == null) {
defaultAnnotations = new ArrayList<>();
@@ -718,6 +691,11 @@
parsingContext,
parent.application::addKeepDeclaration);
}
+
+ // Check for reachability sensitive annotation.
+ parent.hasReachabilitySensitiveMember |=
+ parent.application.getFactory().annotationReachabilitySensitiveDesc.equals(desc);
+
return createAnnotationVisitor(
desc, visible, getAnnotations(), parent.application, DexAnnotation::new);
}
@@ -881,6 +859,11 @@
parsingContext,
parent.application::addKeepDeclaration);
}
+
+ // Check for reachability sensitive annotation.
+ parent.hasReachabilitySensitiveMember |=
+ parent.application.getFactory().annotationReachabilitySensitiveDesc.equals(desc);
+
return createAnnotationVisitor(
desc, visible, getAnnotations(), parent.application, DexAnnotation::new);
}
@@ -1030,7 +1013,6 @@
.build();
Wrapper<DexMethod> signature = MethodSignatureEquivalence.get().wrap(method);
if (parent.methodSignatures.add(signature)) {
- parent.hasReachabilitySensitiveMethod |= isReachabilitySensitive();
if (flags.isStatic() || flags.isConstructor() || flags.isPrivate()) {
parent.directMethods.add(dexMethod);
} else {
@@ -1049,18 +1031,6 @@
}
}
- @SuppressWarnings("ReferenceEquality")
- private boolean isReachabilitySensitive() {
- DexType reachabilitySensitive =
- parent.application.getFactory().annotationReachabilitySensitive;
- for (DexAnnotation annotation : getAnnotations()) {
- if (annotation.annotation.type == reachabilitySensitive) {
- return true;
- }
- }
- return false;
- }
-
private List<DexAnnotation> getAnnotations() {
if (annotations == null) {
annotations = new ArrayList<>();
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index c41f8df..34c1b21 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -73,6 +73,7 @@
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ReachabilitySensitiveValue;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.RetracerForCodePrinting;
import com.android.tools.r8.utils.StringDiagnostic;
@@ -119,12 +120,6 @@
private JarApplicationReader application;
private CfCode code;
private ReparseContext context;
- private boolean reachabilitySensitive = false;
-
- public void markReachabilitySensitive() {
- assert code == null;
- reachabilitySensitive = true;
- }
@Override
public boolean isCfCode() {
@@ -158,11 +153,17 @@
private void internalParseCode() {
ReparseContext context = this.context;
JarApplicationReader application = this.application;
- assert application != null;
assert context != null;
+ assert application != null;
+ DexProgramClass programOwner = context.owner.asProgramClass();
+ ReachabilitySensitiveValue reachabilitySensitive =
+ programOwner != null
+ ? programOwner.getReachabilitySensitiveValue()
+ : ReachabilitySensitiveValue.DISABLED;
+ DebugParsingOptions parsingOptions = getParsingOptions(application, reachabilitySensitive);
// The ClassCodeVisitor is in charge of setting this.context to null.
try {
- parseCode(context, false);
+ parseCode(context, false, parsingOptions);
} catch (JsrEncountered e) {
for (Code code : context.codeList) {
code.asLazyCfCode().code = null;
@@ -170,7 +171,7 @@
code.asLazyCfCode().application = application;
}
try {
- parseCode(context, true);
+ parseCode(context, true, parsingOptions);
} catch (JsrEncountered e1) {
throw new Unreachable(e1);
}
@@ -204,9 +205,8 @@
}
}
- public void parseCode(ReparseContext context, boolean useJsrInliner) {
- DebugParsingOptions parsingOptions = getParsingOptions(application, reachabilitySensitive);
-
+ private void parseCode(
+ ReparseContext context, boolean useJsrInliner, DebugParsingOptions parsingOptions) {
ClassCodeVisitor classVisitor =
new ClassCodeVisitor(
context.owner,
@@ -301,7 +301,8 @@
@Override
public String toString() {
- return asCfCode().toString();
+ // Don't force parsing in toString as it causes unexpected behavior when debugging.
+ return code != null ? code.toString() : "<lazy-code>";
}
@Override
@@ -1177,7 +1178,7 @@
}
private static DebugParsingOptions getParsingOptions(
- JarApplicationReader application, boolean reachabilitySensitive) {
+ JarApplicationReader application, ReachabilitySensitiveValue reachabilitySensitive) {
// TODO(b/166841731): We should compute our own from the compressed format.
int parsingOptions =
application.options.canUseInputStackMaps()
@@ -1194,7 +1195,7 @@
configuration.isKeepParameterNames()
|| keep.localVariableTable
|| keep.localVariableTypeTable
- || reachabilitySensitive;
+ || reachabilitySensitive.isEnabled();
boolean lineInfo =
(keep.lineNumberTable || application.options.canUseNativeDexPcInsteadOfDebugInfo());
boolean methodParaeters = keep.methodParameters;
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
index 03758b2..54dc05d 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramMethod.java
@@ -180,8 +180,8 @@
return getDefinition().getKotlinInfo();
}
- public boolean getOrComputeReachabilitySensitive(AppView<?> appView) {
- return getHolder().getOrComputeReachabilitySensitive(appView);
+ public boolean isReachabilitySensitive() {
+ return getHolder().isReachabilitySensitive();
}
public void setCode(Code newCode, AppView<?> appView) {
@@ -202,7 +202,7 @@
if (appView.testing().noLocalsTableOnInput) {
return false;
}
- return appView.options().debug || getOrComputeReachabilitySensitive(appView);
+ return appView.options().debug || isReachabilitySensitive();
}
public ProgramMethod rewrittenWithLens(
diff --git a/src/main/java/com/android/tools/r8/graph/fixup/TreeFixerBase.java b/src/main/java/com/android/tools/r8/graph/fixup/TreeFixerBase.java
index ec65897..9f40e9f 100644
--- a/src/main/java/com/android/tools/r8/graph/fixup/TreeFixerBase.java
+++ b/src/main/java/com/android/tools/r8/graph/fixup/TreeFixerBase.java
@@ -113,7 +113,6 @@
return newProgramClasses;
}
- @SuppressWarnings("ReferenceEquality")
// Should remain private as the correctness of the fixup requires the lazy 'newProgramClasses'.
private DexProgramClass fixupClass(DexProgramClass clazz) {
DexProgramClass newClass =
@@ -137,7 +136,8 @@
DexEncodedField.EMPTY_ARRAY,
newHolder -> clazz.getMethodCollection().fixup(newHolder, this::fixupMethod),
dexItemFactory.getSkipNameValidationForTesting(),
- clazz.getChecksumSupplier());
+ clazz.getChecksumSupplier(),
+ clazz.getReachabilitySensitiveValue());
newClass.setInstanceFields(fixupFields(clazz.instanceFields()));
newClass.setStaticFields(fixupFields(clazz.staticFields()));
// Transfer properties that are not passed to the constructor.
@@ -151,7 +151,7 @@
newClass.setKotlinInfo(clazz.getKotlinInfo());
}
// If the class type changed, record the move in the lens.
- if (newClass.getType() != clazz.getType()) {
+ if (newClass.getType().isNotIdenticalTo(clazz.getType())) {
return recordClassChange(clazz, newClass);
}
return newClass;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java
index 63461b5..fe5ea8b 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKeepRules.java
@@ -9,7 +9,9 @@
import com.android.tools.r8.graph.DexEncodedMember;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.GenericSignature.DexDefinitionSignature;
import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy;
+import com.android.tools.r8.shaking.KeepInfo;
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Iterables;
@@ -33,9 +35,9 @@
private void processClass(DexProgramClass clazz) {
DexType type = clazz.getType();
- boolean pinHolder = keepInfo.getClassInfo(clazz).isPinned(options);
+ boolean pinHolder = isPinned(keepInfo.getClassInfo(clazz), clazz.getClassSignature());
for (DexEncodedMember<?, ?> member : clazz.members()) {
- if (keepInfo.getMemberInfo(member, clazz).isPinned(options)) {
+ if (isPinned(keepInfo.getMemberInfo(member, clazz), member.getGenericSignature())) {
pinHolder = true;
Iterables.addAll(
dontMergeTypes,
@@ -49,6 +51,13 @@
}
}
+ private boolean isPinned(KeepInfo<?, ?> keepInfo, DexDefinitionSignature<?> genericSignature) {
+ return keepInfo.isPinned(options)
+ || (genericSignature.hasSignature()
+ && !options.isForceProguardCompatibilityEnabled()
+ && !keepInfo.isSignatureRemovalAllowed(options));
+ }
+
@Override
public boolean canMerge(DexProgramClass program) {
return !dontMergeTypes.contains(program.getType());
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 0fbadd1..dba1c38 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
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldResolutionResult;
import com.android.tools.r8.graph.FieldResolutionResult.SingleFieldResolutionResult;
import com.android.tools.r8.graph.ProgramMethod;
@@ -197,15 +198,17 @@
if (clazz == null) {
return true;
}
- if (clazz.superType == null) {
- return false;
- }
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexEncodedMethod resolutionResult =
appInfo
.resolveMethodOnClassLegacy(clazz, dexItemFactory.objectMembers.finalize)
.getSingleTarget();
- return resolutionResult != null && resolutionResult.isProgramMethod(appView);
+ if (resolutionResult == null) {
+ return false;
+ }
+ DexType holderType = resolutionResult.getHolderType();
+ return holderType.isNotIdenticalTo(dexItemFactory.objectType)
+ && holderType.isNotIdenticalTo(dexItemFactory.enumType);
}
return mayHaveFinalizeMethodDirectlyOrIndirectly(appView, baseType.asClassType());
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldPut.java b/src/main/java/com/android/tools/r8/ir/code/FieldPut.java
index 488e60a..91e6d2d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldPut.java
@@ -12,6 +12,8 @@
public interface FieldPut {
+ BasicBlock getBlock();
+
DexField getField();
Position getPosition();
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 7f7653b..5077746 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
@@ -632,7 +632,7 @@
public boolean isConsistentSSABeforeTypesAreCorrectAllowingRedundantBlocks(AppView<?> appView) {
assert isConsistentGraph(appView, true);
- assert consistentBlockInstructions(appView, true);
+ assert consistentBlockInstructions(true);
assert consistentDefUseChains();
assert validThrowingInstructions();
assert noCriticalEdges();
@@ -689,7 +689,7 @@
assert consistentBlockNumbering();
assert consistentPredecessorSuccessors();
assert consistentCatchHandlers();
- assert consistentBlockInstructions(appView, ssa);
+ assert consistentBlockInstructions(ssa);
assert consistentMetadata();
assert verifyAllThrowingInstructionsHavePositions();
return true;
@@ -889,13 +889,11 @@
return true;
}
- private boolean consistentBlockInstructions(AppView<?> appView, boolean ssa) {
+ private boolean consistentBlockInstructions(boolean ssa) {
boolean argumentsAllowed = true;
for (BasicBlock block : blocks) {
assert block.consistentBlockInstructions(
- argumentsAllowed,
- options.debug || context().getOrComputeReachabilitySensitive(appView),
- ssa);
+ argumentsAllowed, options.debug || context().isReachabilitySensitive(), ssa);
argumentsAllowed = false;
}
return true;
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 15082ac..4d73577 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
@@ -87,7 +87,7 @@
public DeadInstructionResult canBeDeadCode(AppView<?> appView, IRCode code) {
InternalOptions options = appView.options();
if (options.debug
- || code.context().getOrComputeReachabilitySensitive(appView)
+ || code.context().isReachabilitySensitive()
|| !code.getConversionOptions().isGeneratingDex()) {
return DeadInstructionResult.notDead();
}
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 7bafe1b..a6f39aa 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
@@ -200,15 +200,6 @@
return operands;
}
- public boolean hasOperandThatMatches(Predicate<Value> predicate) {
- for (Value operand : operands) {
- if (predicate.test(operand)) {
- return true;
- }
- }
- return false;
- }
-
public void removeOperand(int index) {
removeOperand(index, null, alwaysFalse());
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index b95529b..8d03811 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -502,7 +502,7 @@
}
public boolean isDebugMode() {
- return appView.options().debug || getProgramMethod().getOrComputeReachabilitySensitive(appView);
+ return appView.options().debug || getProgramMethod().isReachabilitySensitive();
}
public Int2ReferenceSortedMap<BlockInfo> getCFG() {
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 733a207..7383048 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
@@ -522,7 +522,7 @@
previous = printMethod(code, "IR after disable assertions (SSA)", previous);
}
- boolean isDebugMode = options.debug || context.getOrComputeReachabilitySensitive(appView);
+ boolean isDebugMode = options.debug || context.isReachabilitySensitive();
assert !method.isProcessed() || !isDebugMode
: "Method already processed: "
+ context.toSourceString()
@@ -598,13 +598,6 @@
CheckNotNullConverter.runIfNecessary(appView, code);
previous = printMethod(code, "IR after disable assertions (SSA)", previous);
- if (identifierNameStringMarker != null) {
- timing.begin("Decouple identifier-name strings");
- identifierNameStringMarker.decoupleIdentifierNameStringsInMethod(code);
- timing.end();
- previous = printMethod(code, "IR after identifier-name strings (SSA)", previous);
- }
-
timing.begin("Run proto shrinking tasks");
appView.withGeneratedExtensionRegistryShrinker(shrinker -> shrinker.rewriteCode(method, code));
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
index c7f07e7..4a9e696 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LirConverter.java
@@ -27,8 +27,10 @@
import com.android.tools.r8.lightir.IR2LirConverter;
import com.android.tools.r8.lightir.LirCode;
import com.android.tools.r8.lightir.LirStrategy;
+import com.android.tools.r8.naming.IdentifierNameStringMarker;
import com.android.tools.r8.naming.RecordInvokeDynamicInvokeCustomRewriter;
import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ObjectUtils;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.ThreadUtils;
@@ -40,14 +42,16 @@
public class LirConverter {
public static void enterLirSupportedPhase(
- AppView<? extends AppInfoWithClassHierarchy> appView, ExecutorService executorService)
+ AppView<AppInfoWithLiveness> appView, ExecutorService executorService)
throws ExecutionException {
assert appView.testing().canUseLir(appView);
assert appView.testing().isPreLirPhase();
appView.testing().enterLirSupportedPhase();
CodeRewriterPassCollection codeRewriterPassCollection =
new CodeRewriterPassCollection(
- new ConstResourceNumberRewriter(appView), new StringSwitchConverter(appView));
+ new ConstResourceNumberRewriter(appView),
+ new StringSwitchConverter(appView),
+ new IdentifierNameStringMarker(appView));
// Convert code objects to LIR.
ThreadUtils.processItems(
appView.appInfo().classes(),
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java
index c56ddbf..44cf6e7 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java
@@ -76,7 +76,7 @@
}
protected boolean isDebugMode(ProgramMethod context) {
- return options.debug || context.getOrComputeReachabilitySensitive(appView);
+ return options.debug || context.isReachabilitySensitive();
}
protected abstract String getRewriterId();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java
index 8de8311..604299b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/EmulatedInterfaceApplicationRewriter.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.graph.MethodCollection.MethodCollectionFactory;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.utils.IterableUtils;
+import com.android.tools.r8.utils.ReachabilitySensitiveValue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
@@ -89,7 +90,8 @@
DexEncodedField.EMPTY_ARRAY,
MethodCollectionFactory.fromMethods(newDirectMethods, newVirtualMethods),
false,
- emulatedInterface.getChecksumSupplier());
+ emulatedInterface.getChecksumSupplier(),
+ ReachabilitySensitiveValue.DISABLED);
newEmulatedInterface.addExtraInterfaces(
getRewrittenInterfacesOfEmulatedInterface(emulatedInterface), appView.dexItemFactory());
return newEmulatedInterface;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
index c6d8ac4..b6804e7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ClassInitializerDefaultsOptimization.java
@@ -151,7 +151,7 @@
}
ProgramMethod context = code.context();
- if (context.getOrComputeReachabilitySensitive(appView)) {
+ if (context.isReachabilitySensitive()) {
return ClassInitializerDefaultsResult.empty();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
index b6e87c2..f5e2f36 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -3,11 +3,11 @@
// 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 com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ClassResolutionResult;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.CatchHandlers;
@@ -224,7 +224,8 @@
if (block.hasCatchHandlers()) {
if (block.canThrow()) {
if (appView.enableWholeProgramOptimizations()) {
- Collection<CatchHandler<BasicBlock>> deadCatchHandlers = getDeadCatchHandlers(block);
+ Collection<CatchHandler<BasicBlock>> deadCatchHandlers =
+ getDeadCatchHandlers(code, block);
if (!deadCatchHandlers.isEmpty()) {
for (CatchHandler<BasicBlock> catchHandler : deadCatchHandlers) {
catchHandler.target.unlinkCatchHandlerForGuard(catchHandler.guard);
@@ -249,10 +250,8 @@
return mayHaveIntroducedUnreachableBlocks;
}
- /**
- * Returns the catch handlers of the given block that are dead, if any.
- */
- private Collection<CatchHandler<BasicBlock>> getDeadCatchHandlers(BasicBlock block) {
+ /** Returns the catch handlers of the given block that are dead, if any. */
+ private Collection<CatchHandler<BasicBlock>> getDeadCatchHandlers(IRCode code, BasicBlock block) {
AppInfoWithLiveness appInfoWithLiveness = appView.appInfo().withLiveness();
ImmutableList.Builder<CatchHandler<BasicBlock>> builder = ImmutableList.builder();
CatchHandlers<BasicBlock> catchHandlers = block.getCatchHandlers();
@@ -275,11 +274,19 @@
continue;
}
- // We can exploit that a catch handler must be dead if its guard is never instantiated
- // directly or indirectly.
if (appInfoWithLiveness != null) {
- DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(guard));
- if (clazz != null && !appInfoWithLiveness.isInstantiatedDirectlyOrIndirectly(clazz)) {
+ ClassResolutionResult result =
+ appView.definitionForWithResolutionResult(guard, code.context().getContextClass());
+ if (!result.hasClassResolutionResult() || result.isMultipleClassResolutionResult()) {
+ // With a multi resolution result one of the results is a library class, so the guard
+ // cannot be removed.
+ continue;
+ }
+ // We can exploit that a catch handler must be dead if its guard is never instantiated
+ // directly or indirectly.
+ DexClass clazz = result.toSingleClassWithProgramOverLibrary();
+ if (clazz.isProgramClass()
+ && !appInfoWithLiveness.isInstantiatedDirectlyOrIndirectly(clazz.asProgramClass())) {
builder.add(new CatchHandler<>(guard, target));
continue;
}
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 337d90d..bc22ae8 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
@@ -17,6 +17,7 @@
import com.android.tools.r8.features.FeatureSplitBoundaryOptimizationUtils;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DefaultUseRegistryWithResult;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
@@ -480,6 +481,11 @@
return null;
}
+ if (isInliningBlockedDueToArrayClone(context, singleTarget, appView)) {
+ whyAreYouNotInliningReporter.reportUnsafeDueToArrayCloneCall();
+ return null;
+ }
+
// Make sure constructor inlining is legal.
if (singleTarget.getDefinition().isInstanceInitializer()
&& !canInlineInstanceInitializer(
@@ -624,6 +630,25 @@
return false;
}
+ public static boolean isInliningBlockedDueToArrayClone(
+ ProgramMethod context, ProgramMethod singleTarget, AppView<?> appView) {
+ if (!appView.options().canHaveArtArrayCloneFromInterfaceMethodBug()) {
+ return false;
+ }
+ if (!context.getHolder().isInterface()) {
+ return false;
+ }
+ return singleTarget.registerCodeReferencesWithResult(
+ new DefaultUseRegistryWithResult<>(appView, context, false) {
+ @Override
+ public void registerInvokeVirtual(DexMethod method) {
+ if (appView.dexItemFactory().isArrayClone(method)) {
+ setResult(true);
+ }
+ }
+ });
+ }
+
@Override
@SuppressWarnings("ReferenceEquality")
public boolean canInlineInstanceInitializer(
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 3d78982..4b9c547 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
@@ -160,7 +160,7 @@
}
ProgramMethod method = code.context();
DexEncodedMethod definition = method.getDefinition();
- if (definition.isClassInitializer() || method.getOrComputeReachabilitySensitive(appView)) {
+ if (definition.isClassInitializer() || method.isReachabilitySensitive()) {
return ConstraintWithTarget.NEVER;
}
KeepMethodInfo keepInfo = appView.getKeepInfo(method);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
index 23ae714..ab67530 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -432,9 +432,7 @@
// instruction has side-effects that can change the value of fields. If so, it must be
// handled above. If not, it can be safely added to the assert.
assert instruction.isArgument()
- || instruction.isArrayGet()
|| instruction.isArrayLength()
- || instruction.isArrayPut()
|| instruction.isAssume()
|| instruction.isBinop()
|| instruction.isCheckCast()
@@ -627,6 +625,10 @@
}
private void handleArrayGet(InstructionListIterator it, ArrayGet arrayGet) {
+ if (arrayGet.instructionInstanceCanThrow(appView, method)) {
+ // The read might not happen if the array get can throw.
+ activeState.clearMostRecentFieldWrites();
+ }
if (arrayGet.array().hasLocalInfo()) {
// The array may be modified through the debugger. Therefore subsequent reads of the same
// array slot may not read this local.
@@ -656,6 +658,12 @@
int index = arrayPut.indexOrDefault(-1);
MemberType memberType = arrayPut.getMemberType();
+ // If the instruction can throw, we can't use any previous field stores for store-after-store
+ // elimination.
+ if (arrayPut.instructionInstanceCanThrow(appView, method)) {
+ activeState.clearMostRecentFieldWrites();
+ }
+
// An array-put instruction can potentially write the given array slot on all arrays because
// of
// aliases.
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 6359da4..403719e 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
@@ -57,6 +57,7 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.optimize.AffectedValues;
+import com.android.tools.r8.ir.optimize.DefaultInliningOracle;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.Inliner.InliningInfo;
import com.android.tools.r8.ir.optimize.InliningOracle;
@@ -1340,6 +1341,9 @@
// Using -allowaccessmodification mitigates this.
return false;
}
+ if (DefaultInliningOracle.isInliningBlockedDueToArrayClone(method, singleTarget, appView)) {
+ return false;
+ }
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java
index 1f116b4..cb7523f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/NopWhyAreYouNotInliningReporter.java
@@ -137,4 +137,7 @@
public boolean unsetReasonHasBeenReportedFlag() {
return true;
}
+
+ @Override
+ public void reportUnsafeDueToArrayCloneCall() {}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
index b333817..ca2d508 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
@@ -127,4 +127,6 @@
int numberOfMonitorEnterValuesAfterInlining, int threshold);
public abstract boolean unsetReasonHasBeenReportedFlag();
+
+ public abstract void reportUnsafeDueToArrayCloneCall();
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
index 23391b7..8a04363 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporterImpl.java
@@ -297,4 +297,10 @@
reasonHasBeenReported = false;
return true;
}
+
+ @Override
+ public void reportUnsafeDueToArrayCloneCall() {
+ report(
+ "would lead to unsupported resolution of array clone() from within an interface method.");
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/phis/EffectivelyTrivialPhiOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/phis/EffectivelyTrivialPhiOptimization.java
index bf3bc86..514ed67 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/phis/EffectivelyTrivialPhiOptimization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/phis/EffectivelyTrivialPhiOptimization.java
@@ -182,7 +182,10 @@
return new SingleValueOrValue(worklist.getSeenSet());
}
if (foundDifferentOperandValuesWithSameAbstractValue) {
- if (representativeOperandAbstractValue.isSingleValue()) {
+ if (representativeOperandAbstractValue.isSingleValue()
+ && representativeOperandAbstractValue
+ .asSingleValue()
+ .isMaterializableInContext(appView, code.context())) {
return new SingleValueOrValue(
worklist.getSeenSet(), representativeOperandAbstractValue.asSingleValue());
}
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 2acd209..c099750 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
@@ -233,7 +233,7 @@
// and we do not actually want locals information in the output.
if (options().debug) {
computeDebugInfo(code, blocks, liveIntervals, this, liveAtEntrySets);
- } else if (code.context().getOrComputeReachabilitySensitive(appView)) {
+ } else if (code.context().isReachabilitySensitive()) {
InstructionListIterator it = code.instructionListIterator();
while (it.hasNext()) {
Instruction instruction = it.next();
@@ -1661,7 +1661,7 @@
// Set all free positions for possible registers to max integer.
RegisterPositions freePositions = new RegisterPositionsImpl(registerConstraint + 1);
- if ((options().debug || code.context().getOrComputeReachabilitySensitive(appView))
+ if ((options().debug || code.context().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
@@ -2712,7 +2712,7 @@
}
}
}
- if (appView.options().debug || code.context().getOrComputeReachabilitySensitive(appView)) {
+ if (appView.options().debug || code.context().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/kotlin/KotlinClassInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
index 0480ba2..0314b43 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinClassInfo.java
@@ -129,11 +129,11 @@
KmClass kmClass = metadata.getKmClass();
Map<String, DexEncodedField> fieldMap = new HashMap<>();
for (DexEncodedField field : hostClass.fields()) {
- fieldMap.put(toJvmFieldSignature(field.getReference()).asString(), field);
+ fieldMap.put(toJvmFieldSignature(field.getReference()).toString(), field);
}
Map<String, DexEncodedMethod> methodMap = new HashMap<>();
for (DexEncodedMethod method : hostClass.methods()) {
- methodMap.put(toJvmMethodSignature(method.getReference()).asString(), method);
+ methodMap.put(toJvmMethodSignature(method.getReference()).toString(), method);
}
ImmutableList.Builder<KotlinConstructorInfo> notBackedConstructors = ImmutableList.builder();
KotlinMetadataMembersTracker originalMembersWithKotlinInfo =
@@ -143,7 +143,7 @@
KotlinConstructorInfo.create(kmConstructor, factory, reporter);
JvmMethodSignature signature = JvmExtensionsKt.getSignature(kmConstructor);
if (signature != null) {
- DexEncodedMethod method = methodMap.get(signature.asString());
+ DexEncodedMethod method = methodMap.get(signature.toString());
if (method != null) {
method.setKotlinMemberInfo(constructorInfo);
originalMembersWithKotlinInfo.add(method.getReference());
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
index b435950..21f5469 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinDeclarationContainerInfo.java
@@ -70,10 +70,10 @@
}
KotlinFunctionInfo kotlinFunctionInfo =
KotlinFunctionInfo.create(kmFunction, factory, reporter);
- DexEncodedMethod method = methodSignatureMap.get(signature.asString());
+ DexEncodedMethod method = methodSignatureMap.get(signature.toString());
if (method == null) {
notBackedFunctions.add(kotlinFunctionInfo);
- if (!isValidMethodDescriptor(signature.getDesc())) {
+ if (!isValidMethodDescriptor(signature.getDescriptor())) {
// TODO(b/155536535): Enable this assert.
// appView
// .options()
@@ -98,7 +98,7 @@
boolean hasBacking = false;
if (propertyProcessor.fieldSignature() != null) {
DexEncodedField field =
- fieldSignatureMap.get(propertyProcessor.fieldSignature().asString());
+ fieldSignatureMap.get(propertyProcessor.fieldSignature().toString());
if (field != null) {
hasBacking = true;
field.setKotlinMemberInfo(kotlinPropertyInfo);
@@ -107,7 +107,7 @@
}
if (propertyProcessor.getterSignature() != null) {
DexEncodedMethod method =
- methodSignatureMap.get(propertyProcessor.getterSignature().asString());
+ methodSignatureMap.get(propertyProcessor.getterSignature().toString());
if (method != null) {
hasBacking = true;
keepIfAccessorInline(kmProperty.getGetterFlags(), method, keepByteCode);
@@ -118,7 +118,7 @@
}
if (propertyProcessor.setterSignature() != null) {
DexEncodedMethod method =
- methodSignatureMap.get(propertyProcessor.setterSignature().asString());
+ methodSignatureMap.get(propertyProcessor.setterSignature().toString());
if (method != null) {
hasBacking = true;
keepIfAccessorInline(kmProperty.getGetterFlags(), method, keepByteCode);
@@ -130,7 +130,7 @@
if (propertyProcessor.syntheticMethodForAnnotationsSignature() != null) {
DexEncodedMethod method =
methodSignatureMap.get(
- propertyProcessor.syntheticMethodForAnnotationsSignature().asString());
+ propertyProcessor.syntheticMethodForAnnotationsSignature().toString());
if (method != null) {
hasBacking = true;
method.setKotlinMemberInfo(
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java
index bcf19a1..2c5598d 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmFieldSignatureInfo.java
@@ -34,7 +34,7 @@
}
return new KotlinJvmFieldSignatureInfo(
fieldSignature.getName(),
- KotlinTypeReference.fromDescriptor(fieldSignature.getDesc(), factory));
+ KotlinTypeReference.fromDescriptor(fieldSignature.getDescriptor(), factory));
}
boolean rewrite(Consumer<JvmFieldSignature> consumer, DexEncodedField field, AppView<?> appView) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
index 687156e..917f242 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinJvmMethodSignatureInfo.java
@@ -51,7 +51,7 @@
return null;
}
String name = methodSignature.getName();
- String descriptor = methodSignature.getDesc();
+ String descriptor = methodSignature.getDescriptor();
if (!KotlinMetadataUtils.isValidMethodDescriptor(descriptor)) {
// If the method descriptor is invalid, keep it as invalid.
return new KotlinJvmMethodSignatureInfo(methodSignature.getName(), descriptor);
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
index 09f6a41..77673c0 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinLambdaInfo.java
@@ -41,7 +41,7 @@
JvmMethodSignature signature = JvmExtensionsKt.getSignature(lambda.function);
if (signature != null) {
for (DexEncodedMethod method : clazz.methods()) {
- if (toJvmMethodSignature(method.getReference()).asString().equals(signature.asString())) {
+ if (toJvmMethodSignature(method.getReference()).toString().equals(signature.toString())) {
method.setKotlinMemberInfo(kotlinFunctionInfo);
return new KotlinLambdaInfo(kotlinFunctionInfo, true);
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
index 6e532a3..c2f31ef 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataUtils.java
@@ -24,14 +24,11 @@
import java.util.List;
import java.util.function.Consumer;
import kotlin.Metadata;
-import kotlin.metadata.KmExtensionType;
import kotlin.metadata.KmProperty;
-import kotlin.metadata.KmPropertyExtensionVisitor;
-import kotlin.metadata.KmPropertyVisitor;
+import kotlin.metadata.jvm.JvmExtensionsKt;
import kotlin.metadata.jvm.JvmFieldSignature;
+import kotlin.metadata.jvm.JvmMetadataVersion;
import kotlin.metadata.jvm.JvmMethodSignature;
-import kotlin.metadata.jvm.JvmPropertyExtensionVisitor;
-import kotlin.metadata.jvm.KotlinClassMetadata;
public class KotlinMetadataUtils {
@@ -105,49 +102,25 @@
JvmMethodSignature methodSignature, int intArguments) {
return new JvmMethodSignature(
methodSignature.getName() + "$default",
- methodSignature.getDesc().replace(")", "I".repeat(intArguments) + "Ljava/lang/Object;)"));
+ methodSignature
+ .getDescriptor()
+ .replace(")", "I".repeat(intArguments) + "Ljava/lang/Object;)"));
}
static class KmPropertyProcessor {
- private JvmFieldSignature fieldSignature = null;
+ private final JvmFieldSignature fieldSignature;
// Custom getter via @get:JvmName("..."). Otherwise, null.
- private JvmMethodSignature getterSignature = null;
+ private final JvmMethodSignature getterSignature;
// Custom getter via @set:JvmName("..."). Otherwise, null.
- private JvmMethodSignature setterSignature = null;
- private JvmMethodSignature syntheticMethodForAnnotationsSignature = null;
+ private final JvmMethodSignature setterSignature;
+ private final JvmMethodSignature syntheticMethodForAnnotationsSignature;
KmPropertyProcessor(KmProperty kmProperty) {
- kmProperty.accept(
- new KmPropertyVisitor() {
- @Override
- @SuppressWarnings("ReferenceEquality")
- 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;
- }
-
- @Override
- public void visitSyntheticMethodForAnnotations(JvmMethodSignature signature) {
- assert syntheticMethodForAnnotationsSignature == null : signature.asString();
- syntheticMethodForAnnotationsSignature = signature;
- }
- };
- }
- });
+ fieldSignature = JvmExtensionsKt.getFieldSignature(kmProperty);
+ getterSignature = JvmExtensionsKt.getGetterSignature(kmProperty);
+ setterSignature = JvmExtensionsKt.getSetterSignature(kmProperty);
+ syntheticMethodForAnnotationsSignature =
+ JvmExtensionsKt.getSyntheticMethodForAnnotations(kmProperty);
}
JvmFieldSignature fieldSignature() {
@@ -248,7 +221,12 @@
}
static int[] getCompatibleKotlinInfo() {
- return KotlinClassMetadata.COMPATIBLE_METADATA_VERSION;
+ // The kotlin metadata changelog recommends:
+ // "Main migration path here is to replace KotlinClassMetadata.COMPATIBLE_METADATA_VERSION
+ // with new value with the same meaning: JvmMetadataVersion.LATEST_STABLE_SUPPORTED."
+ // The inspection error "Usage of Kotlin internal declaration from different module" does not
+ // prevent the code to work correctly.
+ return JvmMetadataVersion.LATEST_STABLE_SUPPORTED.toIntArray();
}
static <TKm> TKm consume(TKm tKm, Consumer<TKm> consumer) {
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
index 15883d2..24c5af9 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinMetadataWriter.java
@@ -15,6 +15,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -87,9 +88,7 @@
indent,
"Metadata.Class",
sb,
- newIndent -> {
- KotlinMetadataWriter.appendKmClass(newIndent, sb, kMetadata.getKmClass());
- });
+ newIndent -> KotlinMetadataWriter.appendKmClass(newIndent, sb, kMetadata.getKmClass()));
return sb.toString();
}
@@ -100,9 +99,7 @@
indent,
"Metadata.FileFacade",
sb,
- newIndent -> {
- KotlinMetadataWriter.appendKmPackage(newIndent, sb, kMetadata.getKmPackage());
- });
+ newIndent -> KotlinMetadataWriter.appendKmPackage(newIndent, sb, kMetadata.getKmPackage()));
return sb.toString();
}
@@ -144,9 +141,8 @@
newIndent,
"function",
sb,
- nextIndent -> {
- KotlinMetadataWriter.appendKmFunction(nextIndent, sb, kmLambda.function);
- });
+ nextIndent ->
+ KotlinMetadataWriter.appendKmFunction(nextIndent, sb, kmLambda.function));
} else {
KotlinMetadataWriter.appendKeyValue(newIndent, "function", sb, "null");
}
@@ -216,70 +212,62 @@
indent,
"functions",
sb,
- newIndent -> {
- appendKmList(
- newIndent,
- "KmFunction",
- sb,
- container.getFunctions().stream()
- .sorted(
- Comparator.comparing(
- kmFunction -> JvmExtensionsKt.getSignature(kmFunction).asString()))
- .collect(Collectors.toList()),
- (nextIndent, kmFunction) -> {
- appendKmFunction(nextIndent, sb, kmFunction);
- });
- });
+ newIndent ->
+ appendKmList(
+ newIndent,
+ "KmFunction",
+ sb,
+ container.getFunctions().stream()
+ .sorted(
+ Comparator.comparing(
+ kmFunction ->
+ Objects.toString(JvmExtensionsKt.getSignature(kmFunction))))
+ .collect(Collectors.toList()),
+ (nextIndent, kmFunction) -> appendKmFunction(nextIndent, sb, kmFunction)));
appendKeyValue(
indent,
"properties",
sb,
- newIndent -> {
- appendKmList(
- newIndent,
- "KmProperty",
- sb,
- container.getProperties().stream()
- .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);
- });
- });
+ newIndent ->
+ appendKmList(
+ newIndent,
+ "KmProperty",
+ sb,
+ container.getProperties().stream()
+ .sorted(
+ Comparator.comparing(
+ kmProperty -> {
+ JvmMethodSignature signature =
+ JvmExtensionsKt.getGetterSignature(kmProperty);
+ if (signature != null) {
+ return signature.toString();
+ }
+ signature = JvmExtensionsKt.getSetterSignature(kmProperty);
+ if (signature != null) {
+ return signature.toString();
+ }
+ JvmFieldSignature fieldSignature =
+ JvmExtensionsKt.getFieldSignature(kmProperty);
+ if (fieldSignature != null) {
+ return fieldSignature.toString();
+ }
+ return kmProperty.getName();
+ }))
+ .collect(Collectors.toList()),
+ (nextIndent, kmProperty) -> appendKmProperty(nextIndent, sb, kmProperty)));
appendKeyValue(
indent,
"typeAliases",
sb,
- newIndent -> {
- appendKmList(
- newIndent,
- "KmTypeAlias",
- sb,
- container.getTypeAliases().stream()
- .sorted(Comparator.comparing(KmTypeAlias::getName))
- .collect(Collectors.toList()),
- (nextIndent, kmTypeAlias) -> {
- appendTypeAlias(nextIndent, sb, kmTypeAlias);
- });
- });
+ newIndent ->
+ appendKmList(
+ newIndent,
+ "KmTypeAlias",
+ sb,
+ container.getTypeAliases().stream()
+ .sorted(Comparator.comparing(KmTypeAlias::getName))
+ .collect(Collectors.toList()),
+ (nextIndent, kmTypeAlias) -> appendTypeAlias(nextIndent, sb, kmTypeAlias)));
}
public static void appendKmPackage(String indent, StringBuilder sb, KmPackage kmPackage) {
@@ -289,16 +277,13 @@
indent,
"localDelegatedProperties",
sb,
- nextIndent -> {
- appendKmList(
- nextIndent,
- "KmProperty",
- sb,
- JvmExtensionsKt.getLocalDelegatedProperties(kmPackage),
- (nextNextIndent, kmProperty) -> {
- appendKmProperty(nextNextIndent, sb, kmProperty);
- });
- });
+ nextIndent ->
+ appendKmList(
+ nextIndent,
+ "KmProperty",
+ sb,
+ JvmExtensionsKt.getLocalDelegatedProperties(kmPackage),
+ (nextNextIndent, kmProperty) -> appendKmProperty(nextNextIndent, sb, kmProperty)));
}
public static void appendKmClass(String indent, StringBuilder sb, KmClass kmClass) {
@@ -309,23 +294,18 @@
indent,
"typeParameters",
sb,
- newIndent -> {
- appendTypeParameters(newIndent, sb, kmClass.getTypeParameters());
- });
+ newIndent -> appendTypeParameters(newIndent, sb, kmClass.getTypeParameters()));
appendKeyValue(
indent,
"superTypes",
sb,
- newIndent -> {
- appendKmList(
- newIndent,
- "KmType",
- sb,
- kmClass.getSupertypes(),
- (nextIndent, kmType) -> {
- appendKmType(nextIndent, sb, kmType);
- });
- });
+ newIndent ->
+ appendKmList(
+ newIndent,
+ "KmType",
+ sb,
+ kmClass.getSupertypes(),
+ (nextIndent, kmType) -> appendKmType(nextIndent, sb, kmType)));
if (kmClass.getInlineClassUnderlyingPropertyName() != null) {
appendKeyValue(
indent,
@@ -338,9 +318,7 @@
indent,
"inlineClassUnderlyingType",
sb,
- nextIndent -> {
- appendKmType(nextIndent, sb, kmClass.getInlineClassUnderlyingType());
- });
+ nextIndent -> appendKmType(nextIndent, sb, kmClass.getInlineClassUnderlyingType()));
}
String companionObject = kmClass.getCompanionObject();
appendKeyValue(
@@ -364,16 +342,13 @@
indent,
"localDelegatedProperties",
sb,
- nextIndent -> {
- appendKmList(
- nextIndent,
- "KmProperty",
- sb,
- JvmExtensionsKt.getLocalDelegatedProperties(kmClass),
- (nextNextIndent, kmProperty) -> {
- appendKmProperty(nextNextIndent, sb, kmProperty);
- });
- });
+ nextIndent ->
+ appendKmList(
+ nextIndent,
+ "KmProperty",
+ sb,
+ JvmExtensionsKt.getLocalDelegatedProperties(kmClass),
+ (nextNextIndent, kmProperty) -> appendKmProperty(nextNextIndent, sb, kmProperty)));
appendKmVersionRequirement(indent, sb, kmClass.getVersionRequirements());
appendKeyValue(
indent,
@@ -388,11 +363,9 @@
.sorted(
Comparator.comparing(
kmConstructor ->
- JvmExtensionsKt.getSignature(kmConstructor).asString()))
+ Objects.toString(JvmExtensionsKt.getSignature(kmConstructor))))
.collect(Collectors.toList()),
- (nextIndent, constructor) -> {
- appendKmConstructor(nextIndent, sb, constructor);
- }));
+ (nextIndent, constructor) -> appendKmConstructor(nextIndent, sb, constructor)));
appendKeyValue(
indent,
"contextReceiverTypes",
@@ -422,8 +395,7 @@
nextIndent ->
appendValueParameters(nextIndent, sb, constructor.getValueParameters()));
JvmMethodSignature signature = JvmExtensionsKt.getSignature(constructor);
- appendKeyValue(
- newIndent, "signature", sb, signature != null ? signature.asString() : "null");
+ appendKeyValue(newIndent, "signature", sb, Objects.toString(signature));
appendKmVersionRequirement(newIndent, sb, constructor.getVersionRequirements());
});
}
@@ -465,9 +437,7 @@
newIndent,
"contract",
sb,
- nextIndent -> {
- appendKmContract(nextIndent, sb, contract);
- });
+ nextIndent -> appendKmContract(nextIndent, sb, contract));
}
appendKeyValue(
newIndent,
@@ -481,8 +451,7 @@
function.getContextReceiverTypes(),
(nextIndent, kmType) -> appendKmType(nextIndent, sb, kmType)));
JvmMethodSignature signature = JvmExtensionsKt.getSignature(function);
- appendKeyValue(
- newIndent, "signature", sb, signature != null ? signature.asString() : "null");
+ appendKeyValue(newIndent, "signature", sb, Objects.toString(signature));
appendKeyValue(
newIndent,
"lambdaClassOriginName",
@@ -535,39 +504,25 @@
(nextIndent, kmType) -> appendKmType(nextIndent, sb, kmType)));
appendKeyValue(newIndent, "jvmFlags", sb, JvmExtensionsKt.getJvmFlags(kmProperty) + "");
JvmFieldSignature fieldSignature = JvmExtensionsKt.getFieldSignature(kmProperty);
- appendKeyValue(
- newIndent,
- "fieldSignature",
- sb,
- fieldSignature != null ? fieldSignature.asString() : "null");
+ appendKeyValue(newIndent, "fieldSignature", sb, Objects.toString(fieldSignature));
JvmMethodSignature getterSignature = JvmExtensionsKt.getGetterSignature(kmProperty);
- appendKeyValue(
- newIndent,
- "getterSignature",
- sb,
- getterSignature != null ? getterSignature.asString() : "null");
+ appendKeyValue(newIndent, "getterSignature", sb, Objects.toString(getterSignature));
JvmMethodSignature setterSignature = JvmExtensionsKt.getSetterSignature(kmProperty);
- appendKeyValue(
- newIndent,
- "setterSignature",
- sb,
- setterSignature != null ? setterSignature.asString() : "null");
+ appendKeyValue(newIndent, "setterSignature", sb, Objects.toString(setterSignature));
JvmMethodSignature syntheticMethodForAnnotations =
JvmExtensionsKt.getSyntheticMethodForAnnotations(kmProperty);
appendKeyValue(
newIndent,
"syntheticMethodForAnnotations",
sb,
- syntheticMethodForAnnotations != null
- ? syntheticMethodForAnnotations.asString()
- : "null");
+ Objects.toString(syntheticMethodForAnnotations));
JvmMethodSignature syntheticMethodForDelegate =
JvmExtensionsKt.getSyntheticMethodForAnnotations(kmProperty);
appendKeyValue(
newIndent,
"syntheticMethodForDelegate",
sb,
- syntheticMethodForDelegate != null ? syntheticMethodForDelegate.asString() : "null");
+ Objects.toString(syntheticMethodForDelegate));
});
}
@@ -587,16 +542,14 @@
newIndent,
"arguments",
sb,
- nextIndent -> {
- appendKmList(
- nextIndent,
- "KmTypeProjection",
- sb,
- kmType.getArguments(),
- (nextNextIndent, kmTypeProjection) -> {
- appendKmTypeProjection(nextNextIndent, sb, kmTypeProjection);
- });
- });
+ nextIndent ->
+ appendKmList(
+ nextIndent,
+ "KmTypeProjection",
+ sb,
+ kmType.getArguments(),
+ (nextNextIndent, kmTypeProjection) ->
+ appendKmTypeProjection(nextNextIndent, sb, kmTypeProjection)));
appendKeyValue(
newIndent,
"abbreviatedType",
@@ -613,42 +566,39 @@
newIndent,
"flexibleTypeUpperBound",
sb,
- nextIndent -> {
- appendKmSection(
- newIndent,
- "FlexibleTypeUpperBound",
- sb,
- nextNextIndent -> {
- appendKeyValue(
- nextNextIndent,
- "typeFlexibilityId",
- sb,
- flexibleTypeUpperBound.getTypeFlexibilityId());
- appendKeyValue(
- nextNextIndent,
- "type",
- sb,
- nextNextNextIndent ->
- appendKmType(
- nextNextNextIndent, sb, flexibleTypeUpperBound.getType()));
- });
- });
+ nextIndent ->
+ appendKmSection(
+ newIndent,
+ "FlexibleTypeUpperBound",
+ sb,
+ nextNextIndent -> {
+ appendKeyValue(
+ nextNextIndent,
+ "typeFlexibilityId",
+ sb,
+ flexibleTypeUpperBound.getTypeFlexibilityId());
+ appendKeyValue(
+ nextNextIndent,
+ "type",
+ sb,
+ nextNextNextIndent ->
+ appendKmType(
+ nextNextNextIndent, sb, flexibleTypeUpperBound.getType()));
+ }));
}
appendKeyValue(newIndent, "raw", sb, JvmExtensionsKt.isRaw(kmType) + "");
appendKeyValue(
newIndent,
"annotations",
sb,
- nextIndent -> {
- appendKmList(
- nextIndent,
- "KmAnnotion",
- sb,
- JvmExtensionsKt.getAnnotations(kmType),
- (nextNextIndent, kmAnnotation) -> {
- appendKmAnnotation(nextNextIndent, sb, kmAnnotation);
- });
- });
+ nextIndent ->
+ appendKmList(
+ nextIndent,
+ "KmAnnotion",
+ sb,
+ JvmExtensionsKt.getAnnotations(kmType),
+ (nextNextIndent, kmAnnotation) ->
+ appendKmAnnotation(nextNextIndent, sb, kmAnnotation)));
});
}
@@ -663,9 +613,7 @@
newIndent,
"type",
sb,
- nextIndent -> {
- appendKmType(nextIndent, sb, projection.getType());
- });
+ nextIndent -> appendKmType(nextIndent, sb, projection.getType()));
if (projection.getVariance() != null) {
appendKeyValue(newIndent, "variance", sb, projection.getVariance().name());
}
@@ -679,9 +627,7 @@
"KmValueParameter",
sb,
valueParameters,
- (newIndent, parameter) -> {
- appendValueParameter(newIndent, sb, parameter);
- });
+ (newIndent, parameter) -> appendValueParameter(newIndent, sb, parameter));
}
private static void appendValueParameter(
@@ -701,16 +647,12 @@
newIndent,
"type",
sb,
- nextIndent -> {
- appendKmType(nextIndent, sb, valueParameter.getType());
- });
+ nextIndent -> appendKmType(nextIndent, sb, valueParameter.getType()));
appendKeyValue(
newIndent,
"varargElementType",
sb,
- nextIndent -> {
- appendKmType(nextIndent, sb, valueParameter.getVarargElementType());
- });
+ nextIndent -> appendKmType(nextIndent, sb, valueParameter.getVarargElementType()));
});
}
@@ -721,9 +663,7 @@
"KmTypeParameter",
sb,
typeParameters,
- (newIndent, parameter) -> {
- appendTypeParameter(newIndent, sb, parameter);
- });
+ (newIndent, parameter) -> appendTypeParameter(newIndent, sb, parameter));
}
private static void appendTypeParameter(
@@ -741,30 +681,25 @@
newIndent,
"upperBounds",
sb,
- nextIndent -> {
- appendKmList(
- nextIndent,
- "KmType",
- sb,
- typeParameter.getUpperBounds(),
- (nextNextIndent, kmType) -> {
- appendKmType(nextNextIndent, sb, kmType);
- });
- });
+ nextIndent ->
+ appendKmList(
+ nextIndent,
+ "KmType",
+ sb,
+ typeParameter.getUpperBounds(),
+ (nextNextIndent, kmType) -> appendKmType(nextNextIndent, sb, kmType)));
appendKeyValue(
newIndent,
"extensions",
sb,
- nextIndent -> {
- appendKmList(
- nextIndent,
- "KmAnnotion",
- sb,
- JvmExtensionsKt.getAnnotations(typeParameter),
- (nextNextIndent, kmAnnotation) -> {
- appendKmAnnotation(nextNextIndent, sb, kmAnnotation);
- });
- });
+ nextIndent ->
+ appendKmList(
+ nextIndent,
+ "KmAnnotion",
+ sb,
+ JvmExtensionsKt.getAnnotations(typeParameter),
+ (nextNextIndent, kmAnnotation) ->
+ appendKmAnnotation(nextNextIndent, sb, kmAnnotation)));
});
}
@@ -778,39 +713,31 @@
newIndent,
"annotations",
sb,
- nextIndent -> {
- appendKmList(
- nextIndent,
- "KmAnnotation",
- sb,
- kmTypeAlias.getAnnotations(),
- (nextNextIndent, kmAnnotation) -> {
- appendKmAnnotation(nextNextIndent, sb, kmAnnotation);
- });
- });
+ nextIndent ->
+ appendKmList(
+ nextIndent,
+ "KmAnnotation",
+ sb,
+ kmTypeAlias.getAnnotations(),
+ (nextNextIndent, kmAnnotation) ->
+ appendKmAnnotation(nextNextIndent, sb, kmAnnotation)));
appendKeyValue(
newIndent,
"expandedType",
sb,
- nextIndent -> {
- appendKmType(nextIndent, sb, kmTypeAlias.expandedType);
- });
+ nextIndent -> appendKmType(nextIndent, sb, kmTypeAlias.expandedType));
appendKeyValue(newIndent, "flags", sb, kmTypeAlias.getFlags() + "");
appendKeyValue(newIndent, "name", sb, kmTypeAlias.getName());
appendKeyValue(
newIndent,
"typeParameters",
sb,
- nextIndent -> {
- appendTypeParameters(nextIndent, sb, kmTypeAlias.getTypeParameters());
- });
+ nextIndent -> appendTypeParameters(nextIndent, sb, kmTypeAlias.getTypeParameters()));
appendKeyValue(
newIndent,
"underlyingType",
sb,
- nextIndent -> {
- appendKmType(nextIndent, sb, kmTypeAlias.underlyingType);
- });
+ nextIndent -> appendKmType(nextIndent, sb, kmTypeAlias.underlyingType));
appendKmVersionRequirement(newIndent, sb, kmTypeAlias.getVersionRequirements());
});
}
@@ -834,21 +761,19 @@
"{ key: String, value: KmAnnotationArgument<?> }",
sb,
arguments.keySet(),
- (nextNextIndent, key) -> {
- appendKmSection(
- nextNextIndent,
- "",
- sb,
- nextNextNextIndent -> {
- appendKeyValue(
- nextNextNextIndent,
- key,
- sb,
- nextNextNextNextIndent -> {
- appendKmArgument(nextNextNextIndent, sb, arguments.get(key));
- });
- });
- });
+ (nextNextIndent, key) ->
+ appendKmSection(
+ nextNextIndent,
+ "",
+ sb,
+ nextNextNextIndent ->
+ appendKeyValue(
+ nextNextNextIndent,
+ key,
+ sb,
+ nextNextNextNextIndent ->
+ appendKmArgument(
+ nextNextNextIndent, sb, arguments.get(key)))));
});
});
}
@@ -862,9 +787,7 @@
"ArrayValue",
sb,
value,
- (newIndent, annoArg) -> {
- appendKmArgument(newIndent, sb, annoArg);
- });
+ (newIndent, annoArg) -> appendKmArgument(newIndent, sb, annoArg));
} else {
sb.append(annotationArgument.toString());
}
@@ -876,38 +799,37 @@
indent,
"versionRequirements",
sb,
- newIndent -> {
- appendKmList(
- newIndent,
- "KmVersionRequirement",
- sb,
- kmVersionRequirements,
- (nextIndent, kmVersionRequirement) -> {
- appendKmSection(
- nextIndent,
- "KmVersionRequirement",
- sb,
- nextNextIndent -> {
- appendKeyValue(nextNextIndent, "kind", sb, kmVersionRequirement.kind.name());
- appendKeyValue(
- nextNextIndent, "level", sb, kmVersionRequirement.level.name());
- appendKeyValue(
- nextNextIndent,
- "errorCode",
- sb,
- kmVersionRequirement.getErrorCode() == null
- ? "null"
- : kmVersionRequirement.getErrorCode().toString());
- appendKeyValue(
- nextNextIndent, "message", sb, kmVersionRequirement.getMessage());
- appendKeyValue(
- nextNextIndent,
- "version",
- sb,
- kmVersionRequirement.getVersion().toString());
- });
- });
- });
+ newIndent ->
+ appendKmList(
+ newIndent,
+ "KmVersionRequirement",
+ sb,
+ kmVersionRequirements,
+ (nextIndent, kmVersionRequirement) ->
+ appendKmSection(
+ nextIndent,
+ "KmVersionRequirement",
+ sb,
+ nextNextIndent -> {
+ appendKeyValue(
+ nextNextIndent, "kind", sb, kmVersionRequirement.kind.name());
+ appendKeyValue(
+ nextNextIndent, "level", sb, kmVersionRequirement.level.name());
+ appendKeyValue(
+ nextNextIndent,
+ "errorCode",
+ sb,
+ kmVersionRequirement.getErrorCode() == null
+ ? "null"
+ : kmVersionRequirement.getErrorCode().toString());
+ appendKeyValue(
+ nextNextIndent, "message", sb, kmVersionRequirement.getMessage());
+ appendKeyValue(
+ nextNextIndent,
+ "version",
+ sb,
+ kmVersionRequirement.getVersion().toString());
+ })));
}
private static void appendKmContract(String indent, StringBuilder sb, KmContract contract) {
@@ -915,19 +837,18 @@
indent,
"KmContract",
sb,
- newIndent -> {
- appendKeyValue(
- newIndent,
- "effects",
- sb,
- nextIndent ->
- appendKmList(
- nextIndent,
- "KmEffect",
- sb,
- contract.getEffects(),
- (nextNextIndent, effect) -> appendKmEffect(nextNextIndent, sb, effect)));
- });
+ newIndent ->
+ appendKeyValue(
+ newIndent,
+ "effects",
+ sb,
+ nextIndent ->
+ appendKmList(
+ nextIndent,
+ "KmEffect",
+ sb,
+ contract.getEffects(),
+ (nextNextIndent, effect) -> appendKmEffect(nextNextIndent, sb, effect))));
}
private static void appendKmEffect(String indent, StringBuilder sb, KmEffect effect) {
@@ -946,16 +867,14 @@
newIndent,
"constructorArguments",
sb,
- nextIndent -> {
- appendKmList(
- nextIndent,
- "KmEffectExpression",
- sb,
- effect.getConstructorArguments(),
- (nextNextIndent, expression) -> {
- appendKmEffectExpression(nextNextIndent, sb, expression);
- });
- });
+ nextIndent ->
+ appendKmList(
+ nextIndent,
+ "KmEffectExpression",
+ sb,
+ effect.getConstructorArguments(),
+ (nextNextIndent, expression) ->
+ appendKmEffectExpression(nextNextIndent, sb, expression)));
KmEffectExpression conclusion = effect.getConclusion();
if (conclusion == null) {
appendKeyValue(newIndent, "conclusion", sb, "null");
@@ -995,37 +914,31 @@
newIndent,
"isInstanceType",
sb,
- nextIndent -> {
- appendKmType(nextIndent, sb, expression.isInstanceType());
- });
+ nextIndent -> appendKmType(nextIndent, sb, expression.isInstanceType()));
appendKeyValue(
newIndent,
"andArguments",
sb,
- nextIndent -> {
- appendKmList(
- nextIndent,
- "KmEffectExpression",
- sb,
- expression.getAndArguments(),
- (nextNextIndent, expr) -> {
- appendKmEffectExpression(nextNextIndent, sb, expr);
- });
- });
+ nextIndent ->
+ appendKmList(
+ nextIndent,
+ "KmEffectExpression",
+ sb,
+ expression.getAndArguments(),
+ (nextNextIndent, expr) ->
+ appendKmEffectExpression(nextNextIndent, sb, expr)));
appendKeyValue(
newIndent,
"orArguments",
sb,
- nextIndent -> {
- appendKmList(
- nextIndent,
- "KmEffectExpression",
- sb,
- expression.getOrArguments(),
- (nextNextIndent, expr) -> {
- appendKmEffectExpression(nextNextIndent, sb, expr);
- });
- });
+ nextIndent ->
+ appendKmList(
+ nextIndent,
+ "KmEffectExpression",
+ sb,
+ expression.getOrArguments(),
+ (nextNextIndent, expr) ->
+ appendKmEffectExpression(nextNextIndent, sb, expr)));
});
}
}
diff --git a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
index c4cb846..2771257 100644
--- a/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
+++ b/src/main/java/com/android/tools/r8/kotlin/KotlinPackageInfo.java
@@ -45,11 +45,11 @@
Consumer<DexEncodedMethod> keepByteCode) {
Map<String, DexEncodedField> fieldMap = new HashMap<>();
for (DexEncodedField field : clazz.fields()) {
- fieldMap.put(toJvmFieldSignature(field.getReference()).asString(), field);
+ fieldMap.put(toJvmFieldSignature(field.getReference()).toString(), field);
}
Map<String, DexEncodedMethod> methodMap = new HashMap<>();
for (DexEncodedMethod method : clazz.methods()) {
- methodMap.put(toJvmMethodSignature(method.getReference()).asString(), method);
+ methodMap.put(toJvmMethodSignature(method.getReference()).toString(), method);
}
KotlinMetadataMembersTracker originalMembersWithKotlinInfo =
new KotlinMetadataMembersTracker(appView);
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
index 14a837a..cdec5d9 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierNameStringMarker.java
@@ -9,6 +9,7 @@
import static com.android.tools.r8.naming.IdentifierNameStringUtils.isClassNameComparison;
import static com.android.tools.r8.naming.IdentifierNameStringUtils.isReflectionMethod;
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
@@ -23,7 +24,7 @@
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.DexItemBasedConstString;
-import com.android.tools.r8.ir.code.FieldInstruction;
+import com.android.tools.r8.ir.code.FieldPut;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
@@ -32,16 +33,19 @@
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
+import com.android.tools.r8.ir.conversion.passes.CodeRewriterPass;
+import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult;
import com.android.tools.r8.naming.dexitembasedstring.ClassNameComputationInfo;
import com.android.tools.r8.naming.identifiernamestring.IdentifierNameStringLookupResult;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.TextPosition;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.Streams;
import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
-import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;
@@ -50,16 +54,33 @@
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
-public class IdentifierNameStringMarker {
+public class IdentifierNameStringMarker extends CodeRewriterPass<AppInfoWithLiveness> {
- private final AppView<AppInfoWithLiveness> appView;
private final Object2BooleanMap<DexMember<?, ?>> identifierNameStrings;
public IdentifierNameStringMarker(AppView<AppInfoWithLiveness> appView) {
- this.appView = appView;
+ super(appView);
this.identifierNameStrings = appView.appInfo().identifierNameStrings;
}
+ @Override
+ protected CodeRewriterResult rewriteCode(
+ IRCode code,
+ MethodProcessor methodProcessor,
+ MethodProcessingContext methodProcessingContext) {
+ return decoupleIdentifierNameStringsInBlocks(code, null);
+ }
+
+ @Override
+ protected boolean shouldRewriteCode(IRCode code, MethodProcessor methodProcessor) {
+ return code.metadata().mayHaveConstString();
+ }
+
+ @Override
+ protected String getRewriterId() {
+ return "IdentifierNameStringMarker";
+ }
+
public void decoupleIdentifierNameStringsInFields(
ExecutorService executorService) throws ExecutionException {
ThreadUtils.processItems(
@@ -83,21 +104,18 @@
return;
}
DexString original = staticValue.getValue();
- DexReference itemBasedString = inferMemberOrTypeFromNameString(appView, original);
+ DexReference itemBasedString = inferMemberOrTypeFromNameString(appView(), original);
if (itemBasedString != null) {
encodedField.setStaticValue(
new DexItemBasedValueString(itemBasedString, ClassNameComputationInfo.none()));
}
}
- public void decoupleIdentifierNameStringsInMethod(IRCode code) {
- decoupleIdentifierNameStringsInBlocks(code, null);
- assert code.isConsistentSSA(appView);
- }
-
- public void decoupleIdentifierNameStringsInBlocks(IRCode code, Set<BasicBlock> blocks) {
+ public CodeRewriterResult decoupleIdentifierNameStringsInBlocks(
+ IRCode code, Set<BasicBlock> blocks) {
+ CodeRewriterResult result = CodeRewriterResult.NO_CHANGE;
if (!code.metadata().mayHaveConstString()) {
- return;
+ return result;
}
ListIterator<BasicBlock> blockIterator = code.listIterator();
while (blockIterator.hasNext()) {
@@ -119,56 +137,69 @@
// ...
// v_n' <- DexItemBasedString("Lx/y/z;") // decoupled
// this.fld <- v_n' // fieldPut
- if (instruction.isStaticPut() || instruction.isInstancePut()) {
- iterator =
- decoupleIdentifierNameStringForFieldPutInstruction(
- code, blockIterator, iterator, instruction.asFieldInstruction());
+ if (instruction.isFieldPut()) {
+ FieldPut fieldPut = instruction.asFieldPut();
+ DexReference itemBasedString = getItemBasedStringForFieldPut(code, fieldPut);
+ if (itemBasedString != null) {
+ iterator =
+ decoupleIdentifierNameStringForFieldPutInstruction(
+ code, blockIterator, iterator, fieldPut, itemBasedString);
+ result = CodeRewriterResult.HAS_CHANGED;
+ }
} else if (instruction.isInvokeMethod()) {
+ InvokeMethod invoke = instruction.asInvokeMethod();
iterator =
decoupleIdentifierNameStringForInvokeInstruction(
- code, blockIterator, iterator, instruction.asInvokeMethod());
+ code, blockIterator, iterator, invoke);
+ result = CodeRewriterResult.HAS_CHANGED;
}
}
}
+ return result;
+ }
+
+ private DexReference getItemBasedStringForFieldPut(IRCode code, FieldPut fieldPut) {
+ DexField field = fieldPut.getField();
+ if (!identifierNameStrings.containsKey(field)) {
+ return null;
+ }
+ Value in = fieldPut.value();
+ if (in.isDexItemBasedConstString()) {
+ return null;
+ }
+ if (!in.isConstString()) {
+ warnUndeterminedIdentifierIfNecessary(
+ field, code.context(), fieldPut.asFieldInstruction(), null);
+ return null;
+ }
+ DexString original = in.getConstInstruction().asConstString().getValue();
+ DexReference itemBasedString = inferMemberOrTypeFromNameString(appView(), original);
+ if (itemBasedString == null) {
+ warnUndeterminedIdentifierIfNecessary(
+ field, code.context(), fieldPut.asFieldInstruction(), original);
+ return null;
+ }
+ return itemBasedString;
}
private InstructionListIterator decoupleIdentifierNameStringForFieldPutInstruction(
IRCode code,
ListIterator<BasicBlock> blocks,
InstructionListIterator iterator,
- FieldInstruction instruction) {
- assert instruction.isInstancePut() || instruction.isStaticPut();
- FieldInstruction fieldPut = instruction.asFieldInstruction();
- DexField field = fieldPut.getField();
- if (!identifierNameStrings.containsKey(field)) {
- return iterator;
- }
- Value in = instruction.value();
- if (in.isDexItemBasedConstString()) {
- return iterator;
- }
- if (!in.isConstString()) {
- warnUndeterminedIdentifierIfNecessary(field, code.context(), instruction, null);
- return iterator;
- }
- DexString original = in.getConstInstruction().asConstString().getValue();
- DexReference itemBasedString = inferMemberOrTypeFromNameString(appView, original);
- if (itemBasedString == null) {
- warnUndeterminedIdentifierIfNecessary(field, code.context(), instruction, original);
- return iterator;
- }
+ FieldPut fieldPut,
+ DexReference itemBasedString) {
// Move the cursor back to $fieldPut
assert iterator.peekPrevious() == fieldPut;
iterator.previous();
// Prepare $decoupled just before $fieldPut
- Value newIn = code.createValue(in.getType(), in.getLocalInfo());
+ Value newIn = code.createValue(fieldPut.value().getType(), fieldPut.value().getLocalInfo());
DexItemBasedConstString decoupled =
new DexItemBasedConstString(newIn, itemBasedString, ClassNameComputationInfo.none());
decoupled.setPosition(fieldPut.getPosition());
// If the current block has catch handler, split into two blocks.
// Because const-string we're about to add is also a throwing instr, we need to split
// before adding it.
- BasicBlock block = instruction.getBlock();
+ BasicBlock block = fieldPut.getBlock();
BasicBlock blockWithFieldInstruction =
block.hasCatchHandlers() ? iterator.split(code, blocks) : block;
if (blockWithFieldInstruction != block) {
@@ -186,12 +217,13 @@
assert iterator.peekNext() == fieldPut;
iterator.next();
}
- if (instruction.isStaticPut()) {
- iterator.replaceCurrentInstruction(new StaticPut(newIn, field));
+ if (fieldPut.isStaticPut()) {
+ iterator.replaceCurrentInstruction(new StaticPut(newIn, fieldPut.getField()));
} else {
- assert instruction.isInstancePut();
- InstancePut instancePut = instruction.asInstancePut();
- iterator.replaceCurrentInstruction(new InstancePut(field, instancePut.object(), newIn));
+ assert fieldPut.isInstancePut();
+ InstancePut instancePut = fieldPut.asInstancePut();
+ iterator.replaceCurrentInstruction(
+ new InstancePut(fieldPut.getField(), instancePut.object(), newIn));
}
return iterator;
}
@@ -282,7 +314,7 @@
continue;
}
DexString original = in.getConstInstruction().asConstString().getValue();
- DexReference itemBasedString = inferMemberOrTypeFromNameString(appView, original);
+ DexReference itemBasedString = inferMemberOrTypeFromNameString(appView(), original);
if (itemBasedString == null) {
warnUndeterminedIdentifierIfNecessary(invokedMethod, code.context(), invoke, original);
continue;
@@ -320,16 +352,17 @@
}
}
}
- if (!Arrays.stream(changes).allMatch(Objects::isNull)) {
- List<Value> newIns =
- Streams.mapWithIndex(
- ins.stream(),
- (in, index) -> changes[(int) index] != null ? changes[(int) index] : in)
- .collect(Collectors.toList());
- iterator.replaceCurrentInstruction(
- Invoke.create(
- invoke.getType(), invokedMethod, invokedMethod.proto, invoke.outValue(), newIns));
+ if (ArrayUtils.none(changes, Objects::nonNull)) {
+ return iterator;
}
+ List<Value> newIns =
+ Streams.mapWithIndex(
+ ins.stream(),
+ (in, index) -> changes[(int) index] != null ? changes[(int) index] : in)
+ .collect(Collectors.toList());
+ iterator.replaceCurrentInstruction(
+ Invoke.create(
+ invoke.getType(), invokedMethod, invokedMethod.proto, invoke.outValue(), newIns));
return iterator;
}
@@ -372,8 +405,10 @@
}
Origin origin = method.getOrigin();
String kind = member.isDexField() ? "field" : "method";
- String originalMessage = original == null ? "what identifier string flows to "
- : "what '" + original.toString() + "' refers to, which flows to ";
+ String originalMessage =
+ original == null
+ ? "what identifier string flows to "
+ : "what '" + original + "' refers to, which flows to ";
String message =
"Cannot determine " + originalMessage + member.toSourceString()
+ " that is specified in -identifiernamestring rules."
diff --git a/src/main/java/com/android/tools/r8/naming/KotlinModuleSynthesizer.java b/src/main/java/com/android/tools/r8/naming/KotlinModuleSynthesizer.java
index 39d4bd8..cbaf559 100644
--- a/src/main/java/com/android/tools/r8/naming/KotlinModuleSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/naming/KotlinModuleSynthesizer.java
@@ -26,6 +26,7 @@
import java.util.Optional;
import kotlin.metadata.jvm.JvmMetadataVersion;
import kotlin.metadata.jvm.KmModule;
+import kotlin.metadata.jvm.KmPackageParts;
import kotlin.metadata.jvm.KotlinModuleMetadata;
/**
@@ -171,15 +172,14 @@
String rewrittenName = pair.getSecond();
multiClassPartToOriginal
.getOrDefault(originalName, Collections.emptyList())
- .forEach(
- classPart -> {
- newMultiFiles.put(classPart, rewrittenName);
- });
+ .forEach(classPart -> newMultiFiles.put(classPart, rewrittenName));
});
- kmModule.visitPackageParts(
- newPackage,
- newFacades.getOrDefault(newPackage, Collections.emptyList()),
- newMultiFiles);
+ kmModule
+ .getPackageParts()
+ .put(
+ newPackage,
+ new KmPackageParts(
+ newFacades.getOrDefault(newPackage, Collections.emptyList()), newMultiFiles));
}
return Optional.of(
DataEntryResource.fromBytes(
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
index e750309..80cf637 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/DefaultFieldValueJoiner.java
@@ -7,8 +7,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DefaultUseRegistryWithResult;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramField;
@@ -19,8 +17,6 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteArrayTypeValueState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteClassTypeValueState;
@@ -30,6 +26,7 @@
import com.android.tools.r8.optimize.argumentpropagation.codescanner.NonEmptyValueState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.MapUtils;
import com.android.tools.r8.utils.Pair;
import com.android.tools.r8.utils.ThreadUtils;
@@ -38,7 +35,6 @@
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.List;
@@ -48,7 +44,6 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
-import java.util.function.Predicate;
public class DefaultFieldValueJoiner {
@@ -74,9 +69,6 @@
// Find all the fields where we need to determine if each field read is guaranteed to be
// dominated by a write.
Map<DexProgramClass, List<ProgramField>> fieldsOfInterest = getFieldsOfInterest();
- if (fieldsOfInterest.isEmpty()) {
- return Collections.emptyMap();
- }
// If constructor inlining is disabled, then we focus on whether each instance initializer
// definitely assigns the given field before it is read. We do the same for final and static
@@ -102,12 +94,12 @@
// constructor inlining, we find all new-instance instructions (including subtype allocations)
// and check if the field is written on each allocation before it is possibly read.
analyzeNewInstanceInstructions(
- fieldsNotSubjectToInitializerAnalysis, fieldsWithLiveDefaultValue::add, executorService);
+ fieldsNotSubjectToInitializerAnalysis, fieldsWithLiveDefaultValue::add);
return updateFlowGraphs(fieldsWithLiveDefaultValue, executorService);
}
- protected Map<DexProgramClass, List<ProgramField>> getFieldsOfInterest() {
+ private Map<DexProgramClass, List<ProgramField>> getFieldsOfInterest() {
Map<DexProgramClass, List<ProgramField>> fieldsOfInterest = new IdentityHashMap<>();
for (DexProgramClass clazz : appView.appInfo().classes()) {
clazz.forEachProgramField(
@@ -248,95 +240,13 @@
private void analyzeNewInstanceInstructions(
Map<DexType, ProgramFieldSet> nonFinalInstanceFields,
- Consumer<ProgramField> liveDefaultValueConsumer,
- ExecutorService executorService)
- throws ExecutionException {
- // To simplify the analysis, we currently bail out for non-final classes.
- // TODO(b/296030319): Handle non-final classes.
- MapUtils.removeIf(
- nonFinalInstanceFields,
- (holderType, fields) -> {
- assert !fields.isEmpty();
- DexProgramClass holder = fields.iterator().next().getHolder();
- if (holder.isFinal() || !appView.appInfo().isInstantiatedIndirectly(holder)) {
- // When the class is not explicitly marked final, the class could in principle have
- // injected subclasses if it is pinned. However, none of the fields are pinned, so we
- // should be allowed to reason about the field assignments in the program.
- assert fields.stream()
- .allMatch(
- field -> appView.getKeepInfo(field).isValuePropagationAllowed(appView, field));
- return false;
- }
- fields.forEach(liveDefaultValueConsumer);
- return true;
- });
-
- // We analyze all allocations of the classes that declare one of the given fields.
- ThreadUtils.processMethods(
- appView,
- method ->
- analyzeNewInstanceInstructionsInMethod(
- nonFinalInstanceFields, liveDefaultValueConsumer, method),
- appView.options().getThreadingModule(),
- executorService);
- }
-
- private void analyzeNewInstanceInstructionsInMethod(
- Map<DexType, ProgramFieldSet> nonFinalInstanceFields,
- Consumer<ProgramField> liveDefaultValueConsumer,
- ProgramMethod method) {
- if (!maybeHasNewInstanceThatMatches(method, nonFinalInstanceFields::containsKey)) {
- return;
+ Consumer<ProgramField> liveDefaultValueConsumer) {
+ // Conservatively treat all fields as maybe read before written.
+ // TODO(b/296030319): Implement analysis by building IR for all methods that instantiate the
+ // relevant classes and analyzing the puts to the newly created instances.
+ for (ProgramField field : IterableUtils.flatten(nonFinalInstanceFields.values())) {
+ liveDefaultValueConsumer.accept(field);
}
- IRCode code = method.buildIR(appView, MethodConversionOptions.nonConverting());
- for (NewInstance newInstance : code.<NewInstance>instructions(Instruction::isNewInstance)) {
- ProgramFieldSet fieldsOfInterest = nonFinalInstanceFields.get(newInstance.getType());
- if (fieldsOfInterest == null) {
- continue;
- }
- FieldReadBeforeWriteDfsAnalysis analysis =
- new FieldReadBeforeWriteDfsAnalysis(appView, code, fieldsOfInterest, newInstance) {
-
- @Override
- public AnalysisContinuation acceptFieldMaybeReadBeforeWrite(ProgramField field) {
- // Remove this field from the `fieldsOfInterest`, so that we do not spend more time
- // analyzing it.
- if (fieldsOfInterest.remove(field)) {
- liveDefaultValueConsumer.accept(field);
- }
- return AnalysisContinuation.abortIf(fieldsOfInterest.isEmpty());
- }
- };
- analysis.run();
- if (fieldsOfInterest.isEmpty()) {
- nonFinalInstanceFields.remove(newInstance.getType());
- }
- }
- }
-
- private boolean maybeHasNewInstanceThatMatches(
- ProgramMethod method, Predicate<DexType> predicate) {
- Code code = method.getDefinition().getCode();
- if (code == null || code.isSharedCodeObject()) {
- return false;
- }
- if (code.isLirCode()) {
- return code.asLirCode()
- .hasConstantItemThatMatches(
- constant -> constant instanceof DexType && predicate.test((DexType) constant));
- }
- assert appView.isCfByteCodePassThrough(method);
- assert code.isCfCode();
- return method.registerCodeReferencesWithResult(
- new DefaultUseRegistryWithResult<>(appView, method, false) {
-
- @Override
- public void registerNewInstance(DexType type) {
- if (predicate.test(type)) {
- setResult(true);
- }
- }
- });
}
private Map<FlowGraph, Deque<FlowGraphNode>> updateFlowGraphs(
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysis.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysis.java
deleted file mode 100644
index 1fdfb27..0000000
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysis.java
+++ /dev/null
@@ -1,387 +0,0 @@
-// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.optimize.argumentpropagation.propagation;
-
-import static com.android.tools.r8.ir.code.Opcodes.ASSUME;
-import static com.android.tools.r8.ir.code.Opcodes.CHECK_CAST;
-import static com.android.tools.r8.ir.code.Opcodes.CONST_NUMBER;
-import static com.android.tools.r8.ir.code.Opcodes.CONST_STRING;
-import static com.android.tools.r8.ir.code.Opcodes.GOTO;
-import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_INTERFACE;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_SUPER;
-import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
-import static com.android.tools.r8.ir.code.Opcodes.RETURN;
-import static com.android.tools.r8.ir.code.Opcodes.THROW;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.ProgramField;
-import com.android.tools.r8.ir.code.Assume;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.BasicBlockInstructionIterator;
-import com.android.tools.r8.ir.code.CheckCast;
-import com.android.tools.r8.ir.code.ConstNumber;
-import com.android.tools.r8.ir.code.ConstString;
-import com.android.tools.r8.ir.code.Goto;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.InstancePut;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.code.InvokeMethod;
-import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.Phi;
-import com.android.tools.r8.ir.code.Return;
-import com.android.tools.r8.ir.code.Throw;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.TraversalContinuation;
-import com.android.tools.r8.utils.WorkList;
-import com.android.tools.r8.utils.collections.ProgramFieldSet;
-
-/**
- * Analysis that is given an allocation site (a {@link NewInstance} instruction) and a set of fields
- * that belong to that newly allocated instance.
- *
- * <p>The analysis computes the subset of the given fields which are maybe read before they are
- * written. The default value of these fields is potentially read, whereas the default value of the
- * complement field set are guaranteed to never be read.
- *
- * <p>The analysis works by exploring all possible paths starting from the given allocation site to
- * the normal and the exceptional exits of the method, keeping track of which fields are definitely
- * written before they are read and which fields have maybe been read.
- */
-public abstract class FieldReadBeforeWriteDfsAnalysis extends FieldReadBeforeWriteDfsAnalysisState {
-
- private final AppView<AppInfoWithLiveness> appView;
- private final IRCode code;
- private final DexItemFactory dexItemFactory;
- // The set of fields to consider. Note that this is a concurrent set and that the caller may
- // concurrently remove fields from the set. This may happen if we concurrently find a
- // read-before-write of one of the fields.
- private final ProgramFieldSet fields;
- private final WorkList<WorkItem> worklist = WorkList.newIdentityWorkList();
-
- private final FieldReadBeforeWriteDfsAnalysis self = this;
-
- public FieldReadBeforeWriteDfsAnalysis(
- AppView<AppInfoWithLiveness> appView,
- IRCode code,
- ProgramFieldSet fields,
- NewInstance newInstance) {
- super(newInstance);
- this.appView = appView;
- this.code = code;
- this.dexItemFactory = appView.dexItemFactory();
- this.fields = fields;
- }
-
- // Returns ABORT if all fields of interest are now maybe-read-before-written.
- // Otherwise returns CONTINUE.
- public abstract AnalysisContinuation acceptFieldMaybeReadBeforeWrite(ProgramField field);
-
- public void run() {
- worklist.addIfNotSeen(new InitialWorkItem());
- worklist.run(WorkItem::process);
- }
-
- public enum AnalysisContinuation {
- // Signals to abort the analysis completely (i.e., to break out of the DFS). This is used when
- // we've reported all fields as being maybe read before written.
- ABORT,
- // Signals to continue the current DFS.
- CONTINUE,
- // Signals that all fields have been written before they are read on the current program path,
- // meaning that the algorithm does not need to explore any further. The algorithm should instead
- // backtrack and explore any other program paths.
- BREAK;
-
- static AnalysisContinuation abortIf(boolean condition) {
- if (condition) {
- return ABORT;
- }
- return CONTINUE;
- }
-
- boolean isAbort() {
- return this == ABORT;
- }
-
- boolean isAbortOrContinue() {
- return isAbort() || isContinue();
- }
-
- boolean isBreak() {
- return this == BREAK;
- }
-
- boolean isContinue() {
- return this == CONTINUE;
- }
-
- TraversalContinuation<?, ?> toTraversalContinuation() {
- assert isAbortOrContinue();
- return TraversalContinuation.breakIf(isAbort());
- }
- }
-
- abstract class WorkItem {
-
- abstract TraversalContinuation<?, ?> process();
-
- void applyPhis(BasicBlock block) {
- // TODO(b/339210038): When adding support for non-linear control flow, we need to implement
- // backtracking of this (i.e., we should remove the out value from the object aliases again).
- for (Phi phi : block.getPhis()) {
- if (phi.hasOperandThatMatches(self::isMaybeInstance)) {
- addInstanceAlias(phi);
- }
- }
- }
-
- AnalysisContinuation applyInstructions(BasicBlockInstructionIterator instructionIterator) {
- while (instructionIterator.hasNext()) {
- Instruction instruction = instructionIterator.next();
- assert !instruction.hasOutValue() || !isMaybeInstance(instruction.outValue());
- AnalysisContinuation continuation;
- // TODO(b/339210038): Extend this to many other instructions, such as ConstClass,
- // InstanceOf, *Binop, etc.
- switch (instruction.opcode()) {
- case ASSUME:
- continuation = applyAssumeInstruction(instruction.asAssume());
- break;
- case CHECK_CAST:
- continuation = applyCheckCastInstruction(instruction.asCheckCast());
- break;
- case CONST_NUMBER:
- continuation = applyConstNumber(instruction.asConstNumber());
- break;
- case CONST_STRING:
- continuation = applyConstString(instruction.asConstString());
- break;
- case GOTO:
- continuation = applyGotoInstruction(instruction.asGoto());
- break;
- case INSTANCE_PUT:
- continuation = applyInstancePut(instruction.asInstancePut());
- break;
- case INVOKE_DIRECT:
- case INVOKE_INTERFACE:
- case INVOKE_STATIC:
- case INVOKE_SUPER:
- case INVOKE_VIRTUAL:
- continuation = applyInvokeMethodInstruction(instruction.asInvokeMethod());
- break;
- case RETURN:
- continuation = applyReturnInstruction(instruction.asReturn());
- break;
- case THROW:
- continuation = applyThrowInstruction(instruction.asThrow());
- break;
- default:
- continuation = applyUnhandledInstruction();
- break;
- }
- if (continuation.isAbort()) {
- return continuation;
- }
- if (continuation.isBreak()) {
- break;
- }
- }
- return AnalysisContinuation.CONTINUE;
- }
-
- // TODO(b/339210038): When adding support for non-linear control flow, we need to implement
- // backtracking of this (i.e., we should remove the out value from the object aliases again).
- private AnalysisContinuation applyAssumeInstruction(Assume assume) {
- if (isMaybeInstance(assume.src())) {
- addInstanceAlias(assume.outValue());
- }
- return AnalysisContinuation.CONTINUE;
- }
-
- // TODO(b/339210038): When adding support for non-linear control flow, we need to implement
- // backtracking of this (i.e., we should remove the out value from the object aliases again).
- private AnalysisContinuation applyCheckCastInstruction(CheckCast checkCast) {
- if (isMaybeInstance(checkCast.object())) {
- addInstanceAlias(checkCast.outValue());
- }
- // If the instance has escaped to the heap and this check-cast instruction throws, then it is
- // possible that the instance is retrieved from the heap and all fields are read.
- return markRemainingFieldsAsMaybeReadBeforeWrittenIfInstanceIsEscaped();
- }
-
- private AnalysisContinuation applyConstNumber(ConstNumber unusedConstNumber) {
- return AnalysisContinuation.CONTINUE;
- }
-
- private AnalysisContinuation applyConstString(ConstString unusedConstString) {
- return AnalysisContinuation.CONTINUE;
- }
-
- private AnalysisContinuation applyGotoInstruction(Goto gotoInstruction) {
- BasicBlock targetBlock = gotoInstruction.getTarget();
- if (isBlockOnStack(targetBlock)) {
- // Bail out in case of cycles.
- return markRemainingFieldsAsMaybeReadBeforeWritten();
- } else {
- // Continue exploration into the successor block.
- worklist.addIgnoringSeenSet(new ProcessBlockWorkItem(targetBlock));
- return AnalysisContinuation.CONTINUE;
- }
- }
-
- private AnalysisContinuation applyInstancePut(InstancePut instancePut) {
- // If the instance has escaped and this instance-put instruction can throw, then the program
- // can get the instance from the heap and read any field. Give up in this case.
- if (isEscaped() && instancePut.instructionInstanceCanThrow(appView, code.context())) {
- return markRemainingFieldsAsMaybeReadBeforeWritten();
- }
-
- // Record if this is a definite write to one of the fields of interest.
- if (isDefinitelyInstance(instancePut.object())) {
- ProgramField resolvedField =
- instancePut.resolveField(appView, code.context()).getProgramField();
- if (resolvedField != null && fields.contains(resolvedField)) {
- addWrittenBeforeRead(resolvedField);
- }
-
- // If all fields of interest are written before read, then stop the exploration of the
- // current program path (but continue to explore any program paths from previous unexplored
- // branches).
- if (fields.allMatch(self::isWrittenBeforeRead)) {
- return AnalysisContinuation.BREAK;
- }
- }
-
- // Record if the instance has escaped as a result of this instance-put.
- if (!isEscaped() && isMaybeInstance(instancePut.value())) {
- setEscaped(instancePut);
- }
- return AnalysisContinuation.CONTINUE;
- }
-
- private AnalysisContinuation applyInvokeMethodInstruction(InvokeMethod invoke) {
- // Allow calls to java.lang.Object.<init>().
- // TODO(b/339210038): Generalize this to other constructors.
- if (invoke.isInvokeConstructor(dexItemFactory)
- && isDefinitelyInstance(invoke.getFirstArgument())) {
- DexClassAndMethod resolvedMethod =
- invoke.resolveMethod(appView, code.context()).getResolutionPair();
- if (resolvedMethod != null
- && resolvedMethod
- .getReference()
- .isIdenticalTo(dexItemFactory.objectMembers.constructor)) {
- return AnalysisContinuation.CONTINUE;
- }
- }
-
- // Conservatively treat calls as reading any field if the receiver has escaped or is escaping.
- if (!isEscaped()
- && invoke.hasInValueThatMatches(self::isMaybeInstance)
- && invoke.instructionMayHaveSideEffects(appView, code.context())) {
- setEscaped(invoke);
- }
-
- if (isEscaped()) {
- return markRemainingFieldsAsMaybeReadBeforeWritten();
- }
-
- // Otherwise, this is a call to a method where none of the arguments is an alias of the
- // instance, and the instance has not escaped. Therefore, this call cannot read any of fields
- // from the instance.
- return AnalysisContinuation.CONTINUE;
- }
-
- private AnalysisContinuation applyReturnInstruction(Return unusedReturnInstruction) {
- return markRemainingFieldsAsMaybeReadBeforeWritten();
- }
-
- private AnalysisContinuation applyThrowInstruction(Throw unusedThrowInstruction) {
- return markRemainingFieldsAsMaybeReadBeforeWrittenIfInstanceIsEscaped();
- }
-
- private AnalysisContinuation applyUnhandledInstruction() {
- return markRemainingFieldsAsMaybeReadBeforeWritten();
- }
-
- AnalysisContinuation markRemainingFieldsAsMaybeReadBeforeWritten() {
- for (ProgramField field : fields) {
- if (!isWrittenBeforeRead(field)) {
- AnalysisContinuation continuation = acceptFieldMaybeReadBeforeWrite(field);
- assert continuation.isAbortOrContinue();
- if (continuation.isAbort()) {
- return continuation;
- }
- }
- }
- // At this point we could also CONTINUE, but we check if the fields of interest have become
- // empty as a result of concurrent modification.
- return AnalysisContinuation.abortIf(fields.isEmpty());
- }
-
- AnalysisContinuation markRemainingFieldsAsMaybeReadBeforeWrittenIfInstanceIsEscaped() {
- if (isEscaped()) {
- return markRemainingFieldsAsMaybeReadBeforeWritten();
- }
- return AnalysisContinuation.CONTINUE;
- }
- }
-
- class InitialWorkItem extends WorkItem {
-
- @Override
- TraversalContinuation<?, ?> process() {
- // We start the analysis from the unique constructor invoke instead of from the NewInstance
- // instruction, since no instructions before the constructor call can read any fields from the
- // uninitialized this.
- // TODO(b/339210038): In principle it may be possible for the NewInstance value to flow into a
- // phi before the unique constructor invoke. If this happens we would not record the phi as
- // an alias when starting the analysis from the invoke-direct.
- InvokeDirect uniqueConstructorInvoke =
- getNewInstance().getUniqueConstructorInvoke(dexItemFactory);
- if (uniqueConstructorInvoke == null) {
- return markRemainingFieldsAsMaybeReadBeforeWritten().toTraversalContinuation();
- }
- BasicBlock block = uniqueConstructorInvoke.getBlock();
- // TODO(b/339210038): Maybe allow exceptional control flow.
- if (block.hasCatchHandlers()) {
- return markRemainingFieldsAsMaybeReadBeforeWritten().toTraversalContinuation();
- }
- addBlockToStack(block);
- addInstanceAlias(getNewInstance().outValue());
- BasicBlockInstructionIterator instructionIterator = block.iterator(uniqueConstructorInvoke);
- // Start the analysis from the invoke-direct instruction. This is important if we can tell
- // that the constructor definitely writes some fields.
- instructionIterator.previous();
- return applyInstructions(instructionIterator).toTraversalContinuation();
- }
- }
-
- class ProcessBlockWorkItem extends WorkItem {
-
- private final BasicBlock block;
-
- ProcessBlockWorkItem(BasicBlock block) {
- this.block = block;
- }
-
- @Override
- TraversalContinuation<?, ?> process() {
- // TODO(b/339210038): Maybe allow exceptional control flow.
- if (block.hasCatchHandlers()) {
- return TraversalContinuation.breakIf(
- markRemainingFieldsAsMaybeReadBeforeWritten().isAbort());
- }
- addBlockToStack(block);
- applyPhis(block);
- AnalysisContinuation continuation = applyInstructions(block.iterator());
- assert continuation.isAbortOrContinue();
- return TraversalContinuation.breakIf(continuation.isAbort());
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysisState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysisState.java
deleted file mode 100644
index 7e24c03..0000000
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/FieldReadBeforeWriteDfsAnalysisState.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.optimize.argumentpropagation.propagation;
-
-import com.android.tools.r8.graph.ProgramField;
-import com.android.tools.r8.ir.code.BasicBlock;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.utils.collections.ProgramFieldSet;
-import com.google.common.collect.Sets;
-import java.util.Set;
-
-/**
- * The state we track during the field-maybe-read-before-write/field-never-read-before-written
- * analysis.
- */
-public class FieldReadBeforeWriteDfsAnalysisState {
-
- // The current allocation we are analyzing.
- private final NewInstance newInstance;
-
- // The first instruction on the current program path starting from the `newInstance` instruction
- // from which the `newInstance` value escapes.
- private Instruction escape = null;
-
- // The set of values that *may* be aliases of the `newInstance` value.
- private final Set<Value> instanceAliases = Sets.newIdentityHashSet();
-
- // The set of blocks on the current program path.
- private final Set<BasicBlock> stack = Sets.newIdentityHashSet();
-
- // The set of fields that are guaranteed to be written before they are read on the current program
- // path.
- private final ProgramFieldSet writtenBeforeRead = ProgramFieldSet.create();
-
- FieldReadBeforeWriteDfsAnalysisState(NewInstance newInstance) {
- this.newInstance = newInstance;
- }
-
- void addInstanceAlias(Value instanceAlias) {
- boolean changed = instanceAliases.add(instanceAlias);
- assert changed;
- }
-
- void addBlockToStack(BasicBlock block) {
- boolean changed = stack.add(block);
- assert changed;
- }
-
- void addWrittenBeforeRead(ProgramField field) {
- writtenBeforeRead.add(field);
- }
-
- NewInstance getNewInstance() {
- return newInstance;
- }
-
- boolean isBlockOnStack(BasicBlock block) {
- return stack.contains(block);
- }
-
- boolean isEscaped() {
- return escape != null;
- }
-
- boolean isDefinitelyInstance(Value value) {
- return value.getAliasedValue() == newInstance.outValue();
- }
-
- boolean isMaybeInstance(Value value) {
- return instanceAliases.contains(value);
- }
-
- boolean isWrittenBeforeRead(ProgramField field) {
- return writtenBeforeRead.contains(field);
- }
-
- void setEscaped(Instruction escape) {
- assert !isEscaped();
- this.escape = escape;
- }
-}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
index 81e3424..d62328d 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/propagation/InFlowPropagator.java
@@ -46,7 +46,7 @@
final AppView<AppInfoWithLiveness> appView;
final Set<DexProgramClass> classesWithSingleCallerInlinedInstanceInitializers;
final IRConverter converter;
- protected final FieldStateCollection fieldStates;
+ final FieldStateCollection fieldStates;
final MethodStateCollectionByReference methodStates;
public InFlowPropagator(
@@ -113,15 +113,12 @@
private Map<FlowGraph, Deque<FlowGraphNode>> includeDefaultValuesInFieldStates(
List<FlowGraph> flowGraphs, ExecutorService executorService) throws ExecutionException {
- DefaultFieldValueJoiner joiner = createDefaultFieldValueJoiner(flowGraphs);
+ DefaultFieldValueJoiner joiner =
+ new DefaultFieldValueJoiner(
+ appView, classesWithSingleCallerInlinedInstanceInitializers, fieldStates, flowGraphs);
return joiner.joinDefaultFieldValuesForFieldsWithReadBeforeWrite(executorService);
}
- protected DefaultFieldValueJoiner createDefaultFieldValueJoiner(List<FlowGraph> flowGraphs) {
- return new DefaultFieldValueJoiner(
- appView, classesWithSingleCallerInlinedInstanceInitializers, fieldStates, flowGraphs);
- }
-
private void processFlowGraphs(List<FlowGraph> flowGraphs, ExecutorService executorService)
throws ExecutionException {
ThreadUtils.processItems(
diff --git a/src/main/java/com/android/tools/r8/optimize/compose/ComposeMethodProcessor.java b/src/main/java/com/android/tools/r8/optimize/compose/ComposeMethodProcessor.java
index 22ed377..8d7498c 100644
--- a/src/main/java/com/android/tools/r8/optimize/compose/ComposeMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/optimize/compose/ComposeMethodProcessor.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethod;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
@@ -34,8 +33,6 @@
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
-import com.android.tools.r8.optimize.argumentpropagation.propagation.DefaultFieldValueJoiner;
-import com.android.tools.r8.optimize.argumentpropagation.propagation.FlowGraph;
import com.android.tools.r8.optimize.argumentpropagation.propagation.InFlowPropagator;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.IterableUtils;
@@ -45,8 +42,6 @@
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
-import java.util.Collections;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -97,22 +92,7 @@
InFlowPropagator inFlowPropagator =
new InFlowPropagator(
- appView, null, converter, codeScanner.getFieldStates(), codeScanner.getMethodStates()) {
-
- @Override
- protected DefaultFieldValueJoiner createDefaultFieldValueJoiner(
- List<FlowGraph> flowGraphs) {
- return new DefaultFieldValueJoiner(appView, null, fieldStates, flowGraphs) {
-
- @Override
- protected Map<DexProgramClass, List<ProgramField>> getFieldsOfInterest() {
- // We do not rely on the optimization of any fields in the Composable optimization
- // pass.
- return Collections.emptyMap();
- }
- };
- }
- };
+ appView, null, converter, codeScanner.getFieldStates(), codeScanner.getMethodStates());
inFlowPropagator.run(executorService);
ArgumentPropagatorOptimizationInfoPopulator optimizationInfoPopulator =
diff --git a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerScanner.java b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerScanner.java
index fa0bdfd..205bd4b 100644
--- a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerScanner.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.lightir.LirInstructionView;
import com.android.tools.r8.lightir.LirOpcodeUtils;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.AndroidApiLevelUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.ObjectUtils;
import com.android.tools.r8.utils.ThreadUtils;
@@ -47,7 +48,8 @@
singleCallerMethodCandidates.removeIf(
(callee, caller) ->
callee.getDefinition().isLibraryMethodOverride().isPossiblyTrue()
- || !appView.getKeepInfo(callee).isSingleCallerInliningAllowed(options));
+ || !appView.getKeepInfo(callee).isSingleCallerInliningAllowed(options)
+ || !AndroidApiLevelUtils.isApiSafeForInlining(caller, callee, appView.options()));
return traceInstructions(singleCallerMethodCandidates, executorService);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ComputeApiLevelUseRegistry.java b/src/main/java/com/android/tools/r8/shaking/ComputeApiLevelUseRegistry.java
index bd53f9a..b996483 100644
--- a/src/main/java/com/android/tools/r8/shaking/ComputeApiLevelUseRegistry.java
+++ b/src/main/java/com/android/tools/r8/shaking/ComputeApiLevelUseRegistry.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.shaking;
import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.ApiReferenceStubber;
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.dex.code.CfOrDexInstruction;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
@@ -131,7 +132,7 @@
@Override
public void registerTypeReference(DexType type) {
- // Type references are OK as long as we do not have a use on them
+ // Type references are OK as long as we do not have a use on them.
}
@Override
@@ -141,7 +142,9 @@
@Override
public void registerExceptionGuard(DexType guard) {
- setMaxApiReferenceLevel(guard);
+ // Type references as exception guard are OK for stubbed exception guards as they are always
+ // present at runtime.
+ setMaxApiReferenceLevelForGuard(guard);
}
@Override
@@ -170,6 +173,17 @@
}
}
+ private void setMaxApiReferenceLevelForGuard(DexType type) {
+ if (isEnabled) {
+ ComputedApiLevel computedApiLevel = apiLevelCompute.computeApiLevelForLibraryReference(type);
+ if (ApiReferenceStubber.isNeverStubbedType(type, appInfoWithClassHierarchy.dexItemFactory())
+ || appInfoWithClassHierarchy.options().canHaveDalvikCatchHandlerVerificationBug()
+ || computedApiLevel.isUnknownApiLevel()) {
+ maxApiReferenceLevel = maxApiReferenceLevel.max(computedApiLevel);
+ }
+ }
+ }
+
@SuppressWarnings("HidingField")
public ComputedApiLevel getMaxApiReferenceLevel() {
return maxApiReferenceLevel;
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracing.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracing.java
index c48ceb0..d7369fbd 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracing.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracing.java
@@ -22,7 +22,7 @@
public static EnqueuerDeferredTracing create(
AppView<? extends AppInfoWithClassHierarchy> appView, Enqueuer enqueuer, Mode mode) {
InternalOptions options = appView.options();
- if (!options.isShrinking() || !options.enableEnqueuerDeferredTracing) {
+ if (!options.isShrinking()) {
return empty();
}
if (!options.isOptimizedResourceShrinking()) {
diff --git a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
index 8eac490..1c3889d 100644
--- a/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
+++ b/src/main/java/com/android/tools/r8/shaking/EnqueuerDeferredTracingImpl.java
@@ -82,6 +82,11 @@
ProgramMethod context,
FieldAccessKind accessKind,
FieldAccessMetadata metadata) {
+ if (!fieldReference.getType().isPrimitiveType()
+ && !options.getTestingOptions().enableEnqueuerDeferredTracingForReferenceFields) {
+ return false;
+ }
+
ProgramField field = resolutionResult.getSingleProgramField();
if (field == null) {
return false;
@@ -97,7 +102,7 @@
}
// If the access is from a reachability sensitive method, then bail out.
- if (context.getHolder().getOrComputeReachabilitySensitive(appView)) {
+ if (context.getHolder().isReachabilitySensitive()) {
return enqueueDeferredEnqueuerActions(field);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ObjectAllocationInfoCollectionUtils.java b/src/main/java/com/android/tools/r8/shaking/ObjectAllocationInfoCollectionUtils.java
index 7eb81ce..30e9735 100644
--- a/src/main/java/com/android/tools/r8/shaking/ObjectAllocationInfoCollectionUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ObjectAllocationInfoCollectionUtils.java
@@ -67,8 +67,12 @@
.resolveMethodOnLegacy(
clazz, appView.dexItemFactory().objectMembers.finalize)
.asSingleResolution();
- if (resolution != null && resolution.getResolvedHolder().isProgramClass()) {
- return TraversalContinuation.doBreak();
+ if (resolution != null) {
+ DexType resolvedType = resolution.getResolvedHolder().getType();
+ if (resolvedType.isNotIdenticalTo(appView.dexItemFactory().objectType)
+ && resolvedType.isNotIdenticalTo(appView.dexItemFactory().enumType)) {
+ return TraversalContinuation.doBreak();
+ }
}
}
return TraversalContinuation.doContinue();
diff --git a/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerFactory.java b/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerFactory.java
index e33a5fa..3354e65 100644
--- a/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerFactory.java
+++ b/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerFactory.java
@@ -32,6 +32,7 @@
import com.android.tools.r8.graph.NestHostClassAttribute;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.ReachabilitySensitiveValue;
import com.google.common.collect.ImmutableList;
import java.util.Collections;
@@ -58,7 +59,8 @@
MethodCollectionFactory.fromMethods(
createDirectMethods(dexItemFactory), createVirtualMethods(dexItemFactory)),
dexItemFactory.getSkipNameValidationForTesting(),
- DexProgramClass::invalidChecksumRequest);
+ DexProgramClass::invalidChecksumRequest,
+ ReachabilitySensitiveValue.DISABLED);
}
private static DexEncodedField[] createInstanceFields() {
diff --git a/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerImplFactory.java b/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerImplFactory.java
index f6d1bf4..a70f457 100644
--- a/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerImplFactory.java
+++ b/src/main/java/com/android/tools/r8/startup/generated/InstrumentationServerImplFactory.java
@@ -51,6 +51,7 @@
import com.android.tools.r8.ir.code.MonitorType;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.ReachabilitySensitiveValue;
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap;
import java.util.ArrayDeque;
@@ -80,7 +81,8 @@
MethodCollectionFactory.fromMethods(
createDirectMethods(dexItemFactory), createVirtualMethods(dexItemFactory)),
dexItemFactory.getSkipNameValidationForTesting(),
- DexProgramClass::invalidChecksumRequest);
+ DexProgramClass::invalidChecksumRequest,
+ ReachabilitySensitiveValue.DISABLED);
}
private static DexEncodedField[] createInstanceFields(DexItemFactory dexItemFactory) {
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
index 068a1a7..3340e46 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticClassBuilder.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.graph.RecordComponentInfo;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.ReachabilitySensitiveValue;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -214,7 +215,8 @@
DexEncodedMethod.EMPTY_ARRAY,
factory.getSkipNameValidationForTesting(),
c -> checksum,
- null);
+ null,
+ ReachabilitySensitiveValue.DISABLED);
if (useSortedMethodBacking) {
clazz.getMethodCollection().useSortedBacking();
}
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpBase.java b/src/main/java/com/android/tools/r8/utils/CompileDumpBase.java
index c5cb640..f6c7269 100644
--- a/src/main/java/com/android/tools/r8/utils/CompileDumpBase.java
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpBase.java
@@ -29,6 +29,11 @@
.accept(new Object[] {androidPlatformBuild});
}
+ static void setIsolatedSplits(Object builder, boolean isolatedSplits) {
+ getReflectiveBuilderMethod(builder, "setEnableIsolatedSplits", boolean.class)
+ .accept(new Object[] {isolatedSplits});
+ }
+
static void addArtProfilesForRewriting(Object builder, Map<Path, Path> artProfileFiles) {
try {
Class<?> artProfileProviderClass =
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
index 6760583..cd01544 100644
--- a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
@@ -35,6 +35,8 @@
*/
public class CompileDumpCompatR8 extends CompileDumpBase {
+ private static final String ISOLATED_SPLITS_FLAG = "--isolated-splits";
+
private static final List<String> VALID_OPTIONS =
Arrays.asList(
"--classfile",
@@ -42,7 +44,8 @@
"--debug",
"--release",
"--enable-missing-library-api-modeling",
- "--android-platform-build");
+ "--android-platform-build",
+ ISOLATED_SPLITS_FLAG);
private static final List<String> VALID_OPTIONS_WITH_SINGLE_OPERAND =
Arrays.asList(
@@ -92,6 +95,7 @@
int threads = -1;
boolean enableMissingLibraryApiModeling = false;
boolean androidPlatformBuild = false;
+ boolean isolatedSplits = false;
for (int i = 0; i < args.length; i++) {
String option = args[i];
if (VALID_OPTIONS.contains(option)) {
@@ -122,6 +126,9 @@
case "--android-platform-build":
androidPlatformBuild = true;
break;
+ case ISOLATED_SPLITS_FLAG:
+ isolatedSplits = true;
+ break;
default:
throw new IllegalArgumentException("Unimplemented option: " + option);
}
@@ -225,6 +232,7 @@
addArtProfilesForRewriting(commandBuilder, artProfileFiles);
addStartupProfileProviders(commandBuilder, startupProfileFiles);
setAndroidPlatformBuild(commandBuilder, androidPlatformBuild);
+ setIsolatedSplits(commandBuilder, isolatedSplits);
setEnableExperimentalMissingLibraryApiModeling(commandBuilder, enableMissingLibraryApiModeling);
if (desugaredLibJson != null) {
commandBuilder.addDesugaredLibraryConfiguration(readAllBytesJava7(desugaredLibJson));
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 bbfe77c..d68f123 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -737,9 +737,6 @@
// @dalvik.annotation.codegen.CovariantReturnType$CovariantReturnTypes.
public boolean processCovariantReturnTypeAnnotations = true;
- public boolean enableEnqueuerDeferredTracing =
- System.getProperty("com.android.tools.r8.disableEnqueuerDeferredTracing") == null;
-
public boolean loadAllClassDefinitions = false;
// Whether or not to check for valid multi-dex builds.
@@ -924,12 +921,12 @@
public boolean debug = false;
- public boolean shouldCompileMethodInDebugMode(AppView<?> appView, ProgramMethod method) {
- return debug || method.getOrComputeReachabilitySensitive(appView);
+ public boolean shouldCompileMethodInDebugMode(ProgramMethod method) {
+ return debug || method.isReachabilitySensitive();
}
public boolean shouldCompileMethodInReleaseMode(AppView<?> appView, ProgramMethod method) {
- return !shouldCompileMethodInDebugMode(appView, method);
+ return !shouldCompileMethodInDebugMode(method);
}
private final AccessModifierOptions accessModifierOptions = new AccessModifierOptions(this);
@@ -2367,6 +2364,8 @@
public boolean enableBridgeHoistingToSharedSyntheticSuperclass = false;
public boolean enableCheckCastAndInstanceOfRemoval = true;
public boolean enableDeadSwitchCaseElimination = true;
+ public boolean enableEnqueuerDeferredTracingForReferenceFields =
+ System.getProperty("com.android.tools.r8.disableEnqueuerDeferredTracing") == null;
public boolean enableInvokeSuperToInvokeVirtualRewriting = true;
public boolean enableLegacyClassDefOrdering =
System.getProperty("com.android.tools.r8.enableLegacyClassDefOrdering") != null;
diff --git a/src/main/java/com/android/tools/r8/utils/ReachabilitySensitiveValue.java b/src/main/java/com/android/tools/r8/utils/ReachabilitySensitiveValue.java
new file mode 100644
index 0000000..efdddc8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/ReachabilitySensitiveValue.java
@@ -0,0 +1,18 @@
+// Copyright (c) 2024, 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 enum ReachabilitySensitiveValue {
+ DISABLED,
+ ENABLED;
+
+ public static ReachabilitySensitiveValue fromBoolean(boolean enabled) {
+ return enabled ? ENABLED : DISABLED;
+ }
+
+ public boolean isEnabled() {
+ return this == ENABLED;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java
index 891593a..c778df3 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldSet.java
@@ -8,15 +8,14 @@
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramField;
-import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Predicate;
import java.util.stream.Stream;
public class ProgramFieldSet implements Iterable<ProgramField> {
@@ -55,10 +54,6 @@
backing.putAll(fields.backing);
}
- public boolean allMatch(Predicate<? super ProgramField> predicate) {
- return Iterables.all(this, predicate);
- }
-
public boolean createAndAdd(DexProgramClass clazz, DexEncodedField definition) {
return add(new ProgramField(clazz, definition));
}
diff --git a/src/test/examplesJava11/com/android/tools/r8/AlwaysInline.java b/src/test/examplesJava11/com/android/tools/r8/AlwaysInline.java
new file mode 100644
index 0000000..f76ecf0
--- /dev/null
+++ b/src/test/examplesJava11/com/android/tools/r8/AlwaysInline.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD})
+public @interface AlwaysInline {}
diff --git a/src/test/examplesJava11/com/android/tools/r8/NeverInline.java b/src/test/examplesJava11/com/android/tools/r8/NeverInline.java
index 3c69ab7..f41f718 100644
--- a/src/test/examplesJava11/com/android/tools/r8/NeverInline.java
+++ b/src/test/examplesJava11/com/android/tools/r8/NeverInline.java
@@ -1,11 +1,14 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+@Retention(RetentionPolicy.CLASS)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public @interface NeverInline {}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java b/src/test/examplesJava11/nesthostexample/NestAttributesUpdateTest.java
similarity index 77%
rename from src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
rename to src/test/examplesJava11/nesthostexample/NestAttributesUpdateTest.java
index e4847bd..52960eb 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAttributesUpdateTest.java
+++ b/src/test/examplesJava11/nesthostexample/NestAttributesUpdateTest.java
@@ -2,10 +2,8 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.desugar.nestaccesscontrol;
+package nesthostexample;
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.PACKAGE_NAME;
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.classesMatching;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNotNull;
@@ -33,20 +31,19 @@
@RunWith(Parameterized.class)
public class NestAttributesUpdateTest extends TestBase {
+ private static final String PACKAGE_NAME = "nesthostexample.";
+
+ private static final Class<?> MERGING_OUTER_CLASS = BasicNestHostClassMerging.class;
+ private static final Class<?> PRUNING_OUTER_CLASS = BasicNestHostTreePruning.class;
+ private static final String MERGING_EXPECTED_RESULT = StringUtils.lines("OuterMiddleInner");
+ private static final String PRUNING_EXPECTED_RESULT = StringUtils.lines("NotPruned");
@Parameter(0)
public TestParameters parameters;
- private final String MERGING_OUTER_CLASS = "BasicNestHostClassMerging";
- private final String PRUNING_OUTER_CLASS = "BasicNestHostTreePruning";
- private final String MERGING_EXPECTED_RESULT = StringUtils.lines("OuterMiddleInner");
- private final String PRUNING_EXPECTED_RESULT = StringUtils.lines("NotPruned");
-
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters()
- .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
- .build();
+ return getTestParameters().withCfRuntimesStartingFromIncluding(CfVm.JDK11).build();
}
@Test
@@ -62,18 +59,17 @@
@Test
public void testClassMergingNestHostRemoval() throws Exception {
testNestAttributesCorrect(
- MERGING_OUTER_CLASS + "$MiddleOuter",
+ BasicNestHostClassMerging.MiddleOuter.class,
MERGING_OUTER_CLASS,
MERGING_EXPECTED_RESULT,
2,
- builder -> {
- builder.addOptionsModification(
- internalOptions -> {
- // The test makes an invoke to StringConcatFactory which is not known to DEX and
- // we therefore fail to merge the classes.
- internalOptions.apiModelingOptions().enableApiCallerIdentification = false;
- });
- });
+ builder ->
+ builder.addOptionsModification(
+ internalOptions -> {
+ // The test makes an invoke to StringConcatFactory which is not known to DEX and
+ // we therefore fail to merge the classes.
+ internalOptions.apiModelingOptions().enableApiCallerIdentification = false;
+ }));
}
@Test
@@ -89,7 +85,7 @@
@Test
public void testTreePruningNestHostRemoval() throws Exception {
testNestAttributesCorrect(
- PRUNING_OUTER_CLASS + "$Pruned",
+ BasicNestHostTreePruning.Pruned.class,
PRUNING_OUTER_CLASS,
PRUNING_EXPECTED_RESULT,
1,
@@ -97,22 +93,22 @@
}
public void testNestAttributesCorrect(
- String mainClassName,
- String outerNestName,
+ Class<?> mainClass,
+ Class<?> outerNestClass,
String expectedResult,
int expectedNumClassesLeft,
ThrowableConsumer<R8FullTestBuilder> testBuilderConsumer)
throws Exception {
testNestAttributesCorrect(
- mainClassName,
- outerNestName,
+ mainClass,
+ outerNestClass,
expectedResult,
true,
expectedNumClassesLeft,
testBuilderConsumer);
testNestAttributesCorrect(
- mainClassName,
- outerNestName,
+ mainClass,
+ outerNestClass,
expectedResult,
false,
expectedNumClassesLeft,
@@ -120,23 +116,22 @@
}
public void testNestAttributesCorrect(
- String mainClassName,
- String outerNestName,
+ Class<?> mainClass,
+ Class<?> outerNestClass,
String expectedResult,
boolean minification,
int expectedNumClassesLeft,
ThrowableConsumer<R8FullTestBuilder> testBuilderConsumer)
throws Exception {
- String actualMainClassName = PACKAGE_NAME + mainClassName;
testForR8(parameters.getBackend())
- .addKeepMainRule(actualMainClassName)
+ .addKeepMainRule(mainClass)
.minification(minification)
.addOptionsModification(
options -> {
// Disable optimizations else additional classes are removed since they become unused.
options.enableClassInlining = false;
})
- .addProgramFiles(classesMatching(outerNestName))
+ .addProgramClassesAndInnerClasses(outerNestClass)
.applyIf(parameters.isCfRuntime(), Jdk9TestUtils.addJdk9LibraryFiles(temp))
.addKeepPackageNamesRule("nesthostexample")
.addInliningAnnotations()
@@ -147,7 +142,7 @@
assertEquals(expectedNumClassesLeft, inspector.allClasses().size());
assertNestAttributesCorrect(inspector);
})
- .run(parameters.getRuntime(), actualMainClassName)
+ .run(parameters.getRuntime(), mainClass)
.assertSuccessWithOutput(expectedResult);
}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java b/src/test/examplesJava11/nesthostexample/NestClassMergingTest.java
similarity index 65%
rename from src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java
rename to src/test/examplesJava11/nesthostexample/NestClassMergingTest.java
index 322fb9b..273b7ec 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestClassMergingTest.java
+++ b/src/test/examplesJava11/nesthostexample/NestClassMergingTest.java
@@ -1,17 +1,16 @@
-package com.android.tools.r8.desugar.nestaccesscontrol;
+// 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.
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.PACKAGE_NAME;
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.classesMatching;
+package nesthostexample;
import com.android.tools.r8.Jdk9TestUtils;
-import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.utils.StringUtils;
-import java.nio.file.Path;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -25,10 +24,10 @@
@Parameter(0)
public TestParameters parameters;
- private final String NEST_MAIN_CLASS = PACKAGE_NAME + "NestHostInlining";
- private final String NEST_SUBCLASS_MAIN_CLASS = PACKAGE_NAME + "NestHostInliningSubclasses";
- private final String OUTSIDE_WITH_ACCESS_MAIN_CLASS = PACKAGE_NAME + "OutsideInliningWithAccess";
- private final String OUTSIDE_NO_ACCESS_MAIN_CLASS = PACKAGE_NAME + "OutsideInliningNoAccess";
+ private final Class<?> NEST_MAIN_CLASS = NestHostInlining.class;
+ private final Class<?> NEST_SUBCLASS_MAIN_CLASS = NestHostInliningSubclasses.class;
+ private final Class<?> OUTSIDE_WITH_ACCESS_MAIN_CLASS = OutsideInliningWithAccess.class;
+ private final Class<?> OUTSIDE_NO_ACCESS_MAIN_CLASS = OutsideInliningNoAccess.class;
private final String NEST_MAIN_EXPECTED_RESULT =
StringUtils.lines("inlining", "InnerNoPrivAccess");
private final String NEST_SUBCLASS_MAIN_EXPECTED_RESULT =
@@ -40,20 +39,19 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters()
- .withCfRuntimesStartingFromIncluding(CfVm.JDK11)
- .build();
+ return getTestParameters().withCfRuntimesStartingFromIncluding(CfVm.JDK11).build();
}
@Test
public void testClassMergeAcrossTwoNests() throws Exception {
// Potentially merge classes from one nest with classes from another nest.
testClassMergeAcrossNest(
- new String[] {NEST_MAIN_CLASS}, new String[] {NEST_MAIN_EXPECTED_RESULT});
+ new Class<?>[] {NEST_MAIN_CLASS}, new String[] {NEST_MAIN_EXPECTED_RESULT});
testClassMergeAcrossNest(
- new String[] {NEST_SUBCLASS_MAIN_CLASS}, new String[] {NEST_SUBCLASS_MAIN_EXPECTED_RESULT});
+ new Class<?>[] {NEST_SUBCLASS_MAIN_CLASS},
+ new String[] {NEST_SUBCLASS_MAIN_EXPECTED_RESULT});
testClassMergeAcrossNest(
- new String[] {NEST_MAIN_CLASS, NEST_SUBCLASS_MAIN_CLASS},
+ new Class<?>[] {NEST_MAIN_CLASS, NEST_SUBCLASS_MAIN_CLASS},
new String[] {NEST_MAIN_EXPECTED_RESULT, NEST_SUBCLASS_MAIN_EXPECTED_RESULT});
}
@@ -61,7 +59,7 @@
public void testClassMergeAcrossNestAndNonNest() throws Exception {
// Potentially merge classes from a nest with non nest classes.
testClassMergeAcrossNest(
- new String[] {
+ new Class<?>[] {
NEST_MAIN_CLASS, OUTSIDE_NO_ACCESS_MAIN_CLASS, OUTSIDE_WITH_ACCESS_MAIN_CLASS
},
new String[] {
@@ -70,22 +68,23 @@
OUTSIDE_WITH_ACCESS_MAIN_EXPECTED_RESULT
});
testClassMergeAcrossNest(
- new String[] {OUTSIDE_NO_ACCESS_MAIN_CLASS},
+ new Class<?>[] {OUTSIDE_NO_ACCESS_MAIN_CLASS},
new String[] {OUTSIDE_NO_ACCESS_MAIN_EXPECTED_RESULT});
testClassMergeAcrossNest(
- new String[] {OUTSIDE_WITH_ACCESS_MAIN_CLASS},
+ new Class<?>[] {OUTSIDE_WITH_ACCESS_MAIN_CLASS},
new String[] {OUTSIDE_WITH_ACCESS_MAIN_EXPECTED_RESULT});
}
- public void testClassMergeAcrossNest(String[] mainClasses, String[] expectedResults)
+ public void testClassMergeAcrossNest(Class<?>[] mainClasses, String[] expectedResults)
throws Exception {
- List<Path> bothNestsAndOutsideClassToCompile = classesMatching("Inlining");
- R8FullTestBuilder r8FullTestBuilder = testForR8(parameters.getBackend());
- for (String clazz : mainClasses) {
- r8FullTestBuilder.addKeepMainRule(clazz);
- }
R8TestCompileResult compileResult =
- r8FullTestBuilder
+ testForR8(parameters.getBackend())
+ .apply(
+ b -> {
+ for (Class<?> clazz : mainClasses) {
+ b.addKeepMainRule(clazz);
+ }
+ })
.addOptionsModification(
options -> {
// Disable optimizations else additional classes are removed since they become
@@ -94,7 +93,12 @@
options.enableNestReduction = false;
})
.enableInliningAnnotations()
- .addProgramFiles(bothNestsAndOutsideClassToCompile)
+ .addProgramClassesAndInnerClasses(
+ List.of(
+ NEST_MAIN_CLASS,
+ NEST_SUBCLASS_MAIN_CLASS,
+ OUTSIDE_WITH_ACCESS_MAIN_CLASS,
+ OUTSIDE_NO_ACCESS_MAIN_CLASS))
.applyIf(parameters.isCfRuntime(), Jdk9TestUtils.addJdk9LibraryFiles(temp))
.addKeepPackageNamesRule("nesthostexample")
.compile()
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestConstructorRemovedArgTest.java b/src/test/examplesJava11/nesthostexample/NestConstructorRemovedArgTest.java
similarity index 65%
rename from src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestConstructorRemovedArgTest.java
rename to src/test/examplesJava11/nesthostexample/NestConstructorRemovedArgTest.java
index 5b59d1d..70c0120 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestConstructorRemovedArgTest.java
+++ b/src/test/examplesJava11/nesthostexample/NestConstructorRemovedArgTest.java
@@ -2,18 +2,15 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.desugar.nestaccesscontrol;
+package nesthostexample;
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.classesOfNest;
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.getExpectedResult;
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.getMainClass;
+import static nesthostexample.BasicNestHostWithInnerClassConstructors.getExpectedResult;
import com.android.tools.r8.Jdk9TestUtils;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
-import com.android.tools.r8.ToolHelper.DexVm;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -22,6 +19,8 @@
@RunWith(Parameterized.class)
public class NestConstructorRemovedArgTest extends TestBase {
+ private static final Class<?> MAIN_CLASS = BasicNestHostWithInnerClassConstructors.class;
+
public NestConstructorRemovedArgTest(TestParameters parameters) {
this.parameters = parameters;
}
@@ -32,8 +31,7 @@
public static TestParametersCollection data() {
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK11)
- .withDexRuntime(DexVm.Version.first())
- .withDexRuntime(DexVm.Version.last())
+ .withDexRuntimes()
.withApiLevelsStartingAtIncluding(apiLevelWithInvokeCustomSupport())
.enableApiLevelsForCf()
.build();
@@ -42,33 +40,31 @@
@Test
public void testRemoveArgConstructorNestsR8() throws Exception {
parameters.assumeR8TestParameters();
- String nestID = "constructors";
testForR8(parameters.getBackend())
- .addKeepMainRule(getMainClass(nestID))
+ .addKeepMainRule(MAIN_CLASS)
.addDontObfuscate()
.setMinApi(parameters)
.addOptionsModification(options -> options.enableClassInlining = false)
- .addProgramFiles(classesOfNest(nestID))
+ .addProgramClassesAndInnerClasses(MAIN_CLASS)
.applyIf(parameters.isCfRuntime(), Jdk9TestUtils.addJdk9LibraryFiles(temp))
.compile()
- .run(parameters.getRuntime(), getMainClass(nestID))
- .assertSuccessWithOutput(getExpectedResult(nestID));
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutputLines(getExpectedResult());
}
@Test
public void testRemoveArgConstructorNestsR8NoTreeShaking() throws Exception {
parameters.assumeR8TestParameters();
- String nestID = "constructors";
testForR8(parameters.getBackend())
.noTreeShaking()
- .addKeepMainRule(getMainClass(nestID))
+ .addKeepMainRule(MAIN_CLASS)
.addDontObfuscate()
.setMinApi(parameters)
.addOptionsModification(options -> options.enableClassInlining = false)
- .addProgramFiles(classesOfNest(nestID))
+ .addProgramClassesAndInnerClasses(MAIN_CLASS)
.applyIf(parameters.isCfRuntime(), Jdk9TestUtils.addJdk9LibraryFiles(temp))
.compile()
- .run(parameters.getRuntime(), getMainClass(nestID))
- .assertSuccessWithOutput(getExpectedResult(nestID));
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutputLines(getExpectedResult());
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMemberPropagatedTest.java b/src/test/examplesJava11/nesthostexample/NestMemberPropagatedTest.java
similarity index 63%
rename from src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMemberPropagatedTest.java
rename to src/test/examplesJava11/nesthostexample/NestMemberPropagatedTest.java
index 3e86e4f..dc1a1d3 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMemberPropagatedTest.java
+++ b/src/test/examplesJava11/nesthostexample/NestMemberPropagatedTest.java
@@ -2,11 +2,8 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.desugar.nestaccesscontrol;
+package nesthostexample;
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.classesMatching;
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.getExpectedResult;
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.getMainClass;
import static junit.framework.TestCase.assertEquals;
import com.android.tools.r8.TestBase;
@@ -15,8 +12,6 @@
import com.android.tools.r8.TestRuntime.CfVm;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
-import java.nio.file.Path;
-import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -25,6 +20,8 @@
@RunWith(Parameterized.class)
public class NestMemberPropagatedTest extends TestBase {
+ private static final Class<?> MAIN_CLASS = NestPvtFieldPropagated.class;
+
public NestMemberPropagatedTest(TestParameters parameters) {
this.parameters = parameters;
}
@@ -38,19 +35,15 @@
@Test
public void testPvtMemberPropagated() throws Exception {
- List<Path> toCompile = classesMatching("NestPvtFieldPropagated");
testForR8(parameters.getBackend())
- .addKeepMainRule(getMainClass("memberPropagated"))
+ .addKeepMainRule(MAIN_CLASS)
.addDontObfuscate()
- .addOptionsModification(
- options -> {
- options.enableClassInlining = false;
- })
- .addProgramFiles(toCompile)
+ .addOptionsModification(options -> options.enableClassInlining = false)
+ .addProgramClassesAndInnerClasses(MAIN_CLASS)
.compile()
.inspect(this::assertMemberPropagated)
- .run(parameters.getRuntime(), getMainClass("memberPropagated"))
- .assertSuccessWithOutput(getExpectedResult("memberPropagated"));
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutputLines("toPropagateStatic");
}
private void assertMemberPropagated(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java b/src/test/examplesJava11/nesthostexample/NestMethodInlinedTest.java
similarity index 77%
rename from src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java
rename to src/test/examplesJava11/nesthostexample/NestMethodInlinedTest.java
index 48d4ca1..ef93446 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestMethodInlinedTest.java
+++ b/src/test/examplesJava11/nesthostexample/NestMethodInlinedTest.java
@@ -2,11 +2,8 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.desugar.nestaccesscontrol;
+package nesthostexample;
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.classesMatching;
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.getExpectedResult;
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.getMainClass;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue;
@@ -14,12 +11,11 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
-import java.nio.file.Path;
-import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -28,6 +24,19 @@
@RunWith(Parameterized.class)
public class NestMethodInlinedTest extends TestBase {
+ private static final Class<?> MAIN_CLASS = NestPvtMethodCallInlined.class;
+
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines(
+ "nestPvtCallToInlineInner",
+ "nestPvtCallToInlineInnerInterface",
+ "notInlinedPvtCallInner",
+ "notInlinedPvtCallInnerInterface",
+ "notInlinedPvtCallInnerSub",
+ "notInlinedPvtCallInnerInterface",
+ "nestPvtCallToInlineInnerSub",
+ "nestPvtCallToInlineInner");
+
public NestMethodInlinedTest(TestParameters parameters) {
this.parameters = parameters;
}
@@ -45,17 +54,16 @@
@Test
public void testReference() throws Exception {
testForRuntime(parameters)
- .addProgramFiles(classesMatching("NestPvtMethodCallInlined"))
- .run(parameters.getRuntime(), getMainClass("pvtCallInlined"))
- .assertSuccessWithOutput(getExpectedResult("pvtCallInlined"));
+ .addProgramClassesAndInnerClasses(MAIN_CLASS)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
}
@Test
public void testPvtMethodCallInlined() throws Exception {
parameters.assumeR8TestParameters();
- List<Path> toCompile = classesMatching("NestPvtMethodCallInlined");
testForR8(parameters.getBackend())
- .addKeepMainRule(getMainClass("pvtCallInlined"))
+ .addKeepMainRule(MAIN_CLASS)
.addDontObfuscate()
.addOptionsModification(
options -> {
@@ -63,13 +71,14 @@
options.getVerticalClassMergerOptions().disable();
})
.enableInliningAnnotations()
+ .enableAlwaysInliningAnnotations()
.enableMemberValuePropagationAnnotations()
- .addProgramFiles(toCompile)
+ .addProgramClassesAndInnerClasses(MAIN_CLASS)
.compile()
.inspect(this::assertMethodsInlined)
.inspect(NestAttributesUpdateTest::assertNestAttributesCorrect)
- .run(parameters.getRuntime(), getMainClass("pvtCallInlined"))
- .assertSuccessWithOutput(getExpectedResult("pvtCallInlined"));
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertSuccessWithOutput(EXPECTED_RESULT);
}
private void assertMethodsInlined(CodeInspector inspector) {
diff --git a/src/test/examplesJava11/nesthostexample/NestOnProgramOnClassPathTest.java b/src/test/examplesJava11/nesthostexample/NestOnProgramOnClassPathTest.java
new file mode 100644
index 0000000..d2e3d0c
--- /dev/null
+++ b/src/test/examplesJava11/nesthostexample/NestOnProgramOnClassPathTest.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 nesthostexample;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+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 NestOnProgramOnClassPathTest extends TestBase {
+
+ public NestOnProgramOnClassPathTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private final TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ @Test
+ public void testD8MethodBridgesPresent() throws Exception {
+ parameters.assumeDexRuntime();
+ Class<?> nestHost = BasicNestHostWithInnerClassMethods.class;
+ // 1 inner class.
+ D8TestCompileResult singleInner =
+ compileClassesWithD8ProgramClasses(
+ nestHost, BasicNestHostWithInnerClassMethods.BasicNestedClass.class);
+ singleInner.inspect(inspector -> assertThisNumberOfBridges(inspector, 2));
+ // Outer class.
+ D8TestCompileResult host = compileClassesWithD8ProgramClasses(nestHost, nestHost);
+ host.inspect(inspector -> assertThisNumberOfBridges(inspector, 2));
+ // 2 inner classes.
+ D8TestCompileResult multipleInner =
+ compileClassesWithD8ProgramClasses(
+ NestHostExample.class,
+ NestHostExample.StaticNestMemberInner.class,
+ NestHostExample.StaticNestMemberInner.StaticNestMemberInnerInner.class);
+ multipleInner.inspect(inspector -> assertThisNumberOfBridges(inspector, 5));
+ }
+
+ @Test
+ public void testD8ConstructorBridgesPresent() throws Exception {
+ parameters.assumeDexRuntime();
+ Class<?> nestHost = BasicNestHostWithInnerClassConstructors.class;
+ D8TestCompileResult inner =
+ compileClassesWithD8ProgramClasses(
+ nestHost, BasicNestHostWithInnerClassConstructors.BasicNestedClass.class);
+ inner.inspect(
+ inspector -> {
+ assertThisNumberOfBridges(inspector, 3);
+ assertNestConstructor(inspector);
+ });
+ D8TestCompileResult host = compileClassesWithD8ProgramClasses(nestHost, nestHost);
+ host.inspect(
+ inspector -> {
+ assertThisNumberOfBridges(inspector, 1);
+ assertNestConstructor(inspector);
+ });
+ }
+
+ @Test
+ public void testD8ConstructorNestMergeCorrect() throws Exception {
+ // Multiple Nest Constructor classes have to be merged here.
+ parameters.assumeDexRuntime();
+ Class<?> nestHost = BasicNestHostWithInnerClassConstructors.class;
+ D8TestCompileResult inner =
+ compileClassesWithD8ProgramClasses(
+ nestHost, BasicNestHostWithInnerClassConstructors.BasicNestedClass.class);
+ D8TestCompileResult host = compileClassesWithD8ProgramClasses(nestHost, nestHost);
+ testForD8()
+ .addProgramFiles(inner.writeToZip(), host.writeToZip())
+ .setMinApi(parameters)
+ .compile()
+ .run(parameters.getRuntime(), nestHost)
+ .assertSuccessWithOutputLines(BasicNestHostWithInnerClassConstructors.getExpectedResult());
+ }
+
+ private D8TestCompileResult compileClassesWithD8ProgramClasses(
+ Class<?> nestHost, Class<?>... classes) throws Exception {
+ return testForD8()
+ .setMinApi(parameters)
+ .addProgramClasses(classes)
+ .addClasspathClasses(nestHost.getNestMembers())
+ .compile();
+ }
+
+ private static void assertNestConstructor(CodeInspector inspector) {
+ assertTrue(inspector.allClasses().stream().anyMatch(FoundClassSubject::isSynthetic));
+ }
+
+ private static void assertThisNumberOfBridges(CodeInspector inspector, int numBridges) {
+ for (FoundClassSubject clazz : inspector.allClasses()) {
+ if (!clazz.isSynthetic()) {
+ assertEquals(numBridges, clazz.allMethods(FoundMethodSubject::isSynthetic).size());
+ }
+ }
+ }
+}
diff --git a/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java b/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java
index 1e36fe1..9670e69 100644
--- a/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java
+++ b/src/test/examplesJava11/nesthostexample/NestPvtMethodCallInlined.java
@@ -4,6 +4,7 @@
package nesthostexample;
+import com.android.tools.r8.AlwaysInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverPropagateValue;
@@ -11,6 +12,7 @@
public static class Inner {
+ @AlwaysInline
public String methodWithPvtCallToInline() {
return notInlinedPvtCall();
}
@@ -21,6 +23,7 @@
return "notInlinedPvtCallInner";
}
+ @AlwaysInline
private String nestPvtCallToInline() {
return "nestPvtCallToInlineInner";
}
@@ -28,6 +31,7 @@
public interface InnerInterface {
+ @AlwaysInline
default String methodWithPvtCallToInline() {
return notInlinedPvtCall();
}
@@ -38,6 +42,7 @@
return "notInlinedPvtCallInnerInterface";
}
+ @AlwaysInline
private String nestPvtCallToInline() {
return "nestPvtCallToInlineInnerInterface";
}
@@ -84,22 +89,22 @@
InnerInterface impl = new InnerInterfaceImpl();
// Inlining through nest access (invoke virtual/interface).
- System.out.println(i.nestPvtCallToInline());
- System.out.println(impl.nestPvtCallToInline());
+ System.out.println((Object) i.nestPvtCallToInline());
+ System.out.println((Object) impl.nestPvtCallToInline());
// Inlining transformations.
// Invoke direct -> invoke virtual.
- System.out.println(i.methodWithPvtCallToInline());
+ System.out.println((Object) i.methodWithPvtCallToInline());
// Invoke interface -> invoke virtual.
- System.out.println(impl.methodWithPvtCallToInline());
+ System.out.println((Object) impl.methodWithPvtCallToInline());
// Invoke virtual -> invoke direct.
- System.out.println(iSub.dispatchInlining(impl));
+ System.out.println((Object) iSub.dispatchInlining(impl));
// Invoke interface -> invoke direct.
- System.out.println(impl.dispatchInlining(iSub));
+ System.out.println((Object) impl.dispatchInlining(iSub));
// Inheritance + invoke virtual and nest access.
// This may mess up lookup logic.
- System.out.println(iSub.nestPvtCallToInline());
- System.out.println(((Inner) iSub).nestPvtCallToInline());
+ System.out.println((Object) iSub.nestPvtCallToInline());
+ System.out.println((Object) ((Inner) iSub).nestPvtCallToInline());
}
}
diff --git a/src/test/java/com/android/tools/r8/B344363462Test.java b/src/test/java/com/android/tools/r8/B344363462Test.java
deleted file mode 100644
index bba41ed..0000000
--- a/src/test/java/com/android/tools/r8/B344363462Test.java
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8;
-
-import com.android.tools.r8.utils.StringUtils;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
-
-@RunWith(Parameterized.class)
-public class B344363462Test extends TestBase {
-
- @Parameter() public TestParameters parameters;
-
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
- }
-
- private static final String EXPECTED_OUTPUT = StringUtils.lines("-225");
- private static final String UNEXPECTED_OUTPUT = StringUtils.lines("0");
-
- @Test
- public void testD8() throws Exception {
- parameters.assumeDexRuntime();
- testForD8(parameters.getBackend())
- .addInnerClasses(getClass())
- .setMinApi(parameters)
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(EXPECTED_OUTPUT);
- }
-
- @Test
- public void testD8Release() throws Exception {
- parameters.assumeDexRuntime();
- testForD8(parameters.getBackend())
- .addInnerClasses(getClass())
- .setMinApi(parameters)
- .release()
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(UNEXPECTED_OUTPUT);
- }
-
- @Test
- public void testR8() throws Exception {
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
- .addKeepMainRule(TestClass.class)
- .setMinApi(parameters)
- .run(parameters.getRuntime(), TestClass.class)
- .assertSuccessWithOutput(UNEXPECTED_OUTPUT);
- }
-
- static class TestClass {
-
- int iFld1;
-
- void t() {
- int i5 = 61127, i7 = 42011;
- long[] lArr = new long[10];
- try {
- iFld1 -= 225L;
- lArr[i5] = i7;
- iFld1 = i7;
- } catch (ArrayIndexOutOfBoundsException err) {
- }
- System.out.println(iFld1);
- }
-
- public static void main(String[] strArr) {
- TestClass test = new TestClass();
- test.t();
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionInstantiateAndCatchTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionInstantiateAndCatchTest.java
index a45b22e..e212a7a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionInstantiateAndCatchTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionInstantiateAndCatchTest.java
@@ -160,12 +160,11 @@
testForR8(parameters.getBackend())
.apply(this::setupTestBuilder)
.addKeepMainRule(Main.class)
- .addDontObfuscate()
.compile()
.applyIf(
exceptionPresentAtRuntime(), b -> b.addBootClasspathClasses(LibraryException.class))
.run(parameters.getRuntime(), Main.class)
- .apply(this::checkUnexpectedOutput)
+ .apply(this::checkOutput)
.inspect(this::inspect);
}
@@ -181,21 +180,6 @@
}
}
- private void checkUnexpectedOutput(SingleTestRunResult<?> runResult) {
- if (exceptionPresentAtRuntime()) {
- if (parameters.isCfRuntime()
- || parameters.getApiLevel().isGreaterThanOrEqualTo(mockExceptionLevel)) {
- runResult.assertSuccessWithOutputLines("Valid behaviour");
- } else {
- // TODO(b/342961827): This should not happen.
- runResult.assertSuccessWithOutputLines(
- LibraryException.class.getTypeName() + ": Failed, true");
- }
- } else {
- runResult.assertSuccessWithOutputLines("java.lang.NoClassDefFoundError, false");
- }
- }
-
// Only present from api level P.
public static class LibraryException extends Exception {
public LibraryException(String message) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfTryCatchReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfTryCatchReferenceTest.java
index a9ca217..6074c6e 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfTryCatchReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoInliningOfTryCatchReferenceTest.java
@@ -24,7 +24,7 @@
@RunWith(Parameterized.class)
public class ApiModelNoInliningOfTryCatchReferenceTest extends TestBase {
- private final AndroidApiLevel exceptionApiLevel = AndroidApiLevel.L_MR1;
+ private final AndroidApiLevel exceptionApiLevel = AndroidApiLevel.M;
@Parameter() public TestParameters parameters;
@@ -51,8 +51,9 @@
.enableInliningAnnotations()
.addHorizontallyMergedClassesInspector(
horizontallyMergedClassesInspector -> {
+ // Dalvik verifier error present up to and not including L.
if (parameters.isDexRuntime()
- && parameters.getApiLevel().isGreaterThanOrEqualTo(exceptionApiLevel)) {
+ && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L)) {
horizontallyMergedClassesInspector.assertIsCompleteMergeGroup(
TestClass.class, Caller.class);
} else {
@@ -63,11 +64,14 @@
ApiModelingTestHelper.addTracedApiReferenceLevelCallBack(
(reference, apiLevel) -> {
if (reference.equals(Reference.methodFromMethod(tryCatch))) {
+ // Dalvik verifier error present up to and not including L.
assertEquals(
- exceptionApiLevel.max(
- parameters.isCfRuntime()
- ? AndroidApiLevel.B
- : parameters.getApiLevel()),
+ parameters.isDexRuntime()
+ && parameters
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(AndroidApiLevel.L)
+ ? parameters.getApiLevel()
+ : exceptionApiLevel,
apiLevel);
}
}))
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkBase.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkBase.java
index 1cb50ba..917c268 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkBase.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkBase.java
@@ -40,7 +40,7 @@
config.run(new BenchmarkEnvironment(config, temp, false));
}
- public static BenchmarkRunner runner(BenchmarkConfig config) {
- return BenchmarkRunner.runner(config);
+ public static BenchmarkRunner runner(BenchmarkEnvironment environment) {
+ return BenchmarkRunner.runner(environment);
}
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkEnvironment.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkEnvironment.java
index fe36c52..e8e5641 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkEnvironment.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkEnvironment.java
@@ -19,6 +19,10 @@
this.isGolem = isGolem;
}
+ public boolean failOnCodeSizeDifferences() {
+ return System.getProperty("BENCHMARK_IGNORE_CODE_SIZE_DIFFERENCES") == null;
+ }
+
public BenchmarkConfig getConfig() {
return config;
}
@@ -36,4 +40,20 @@
public Path getGolemDependencyRoot() {
return Paths.get("benchmarks", config.getDependencyDirectoryName());
}
+
+ public boolean hasBenchmarkIterationsOverride() {
+ return System.getProperty("BENCHMARK_ITERATIONS") != null;
+ }
+
+ public int getBenchmarkIterationsOverride() {
+ return Integer.parseInt(System.getProperty("BENCHMARK_ITERATIONS"));
+ }
+
+ public boolean hasOutputPath() {
+ return System.getProperty("BENCHMARK_OUTPUT") != null;
+ }
+
+ public Path getOutputPath() {
+ return Paths.get(System.getProperty("BENCHMARK_OUTPUT"));
+ }
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsCollection.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsCollection.java
index 14b10f4..da3f3d2 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsCollection.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsCollection.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.benchmarks;
+import com.android.tools.r8.errors.Unimplemented;
+import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -46,12 +48,17 @@
}
@Override
- public void printResults(ResultMode mode) {
+ public void printResults(ResultMode mode, boolean failOnCodeSizeDifferences) {
List<String> sorted = new ArrayList<>(results.keySet());
sorted.sort(String::compareTo);
for (String name : sorted) {
BenchmarkResultsSingle singleResults = results.get(name);
- singleResults.printResults(mode);
+ singleResults.printResults(mode, failOnCodeSizeDifferences);
}
}
+
+ @Override
+ public void writeResults(PrintStream out) {
+ throw new Unimplemented();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingle.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingle.java
index e0bb9f193..e15dffb 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingle.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingle.java
@@ -3,8 +3,11 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.benchmarks;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongList;
+import java.io.PrintStream;
import java.util.Set;
public class BenchmarkResultsSingle implements BenchmarkResults {
@@ -19,6 +22,18 @@
this.metrics = metrics;
}
+ public String getName() {
+ return name;
+ }
+
+ public LongList getCodeSizeResults() {
+ return codeSizeResults;
+ }
+
+ public LongList getRuntimeResults() {
+ return runtimeResults;
+ }
+
@Override
public void addRuntimeResult(long result) {
verifyMetric(BenchmarkMetric.RunTimeRaw, metrics.contains(BenchmarkMetric.RunTimeRaw), true);
@@ -75,7 +90,7 @@
}
@Override
- public void printResults(ResultMode mode) {
+ public void printResults(ResultMode mode, boolean failOnCodeSizeDifferences) {
verifyConfigAndResults();
if (!runtimeResults.isEmpty()) {
long sum = runtimeResults.stream().mapToLong(l -> l).sum();
@@ -84,13 +99,24 @@
}
if (!codeSizeResults.isEmpty()) {
long size = codeSizeResults.getLong(0);
- for (int i = 1; i < codeSizeResults.size(); i++) {
- if (size != codeSizeResults.getLong(i)) {
- throw new RuntimeException(
- "Unexpected code size difference: " + size + " and " + codeSizeResults.getLong(i));
+ if (failOnCodeSizeDifferences) {
+ for (int i = 1; i < codeSizeResults.size(); i++) {
+ if (size != codeSizeResults.getLong(i)) {
+ throw new RuntimeException(
+ "Unexpected code size difference: " + size + " and " + codeSizeResults.getLong(i));
+ }
}
}
printCodeSize(size);
}
}
+
+ @Override
+ public void writeResults(PrintStream out) {
+ Gson gson =
+ new GsonBuilder()
+ .registerTypeAdapter(BenchmarkResultsSingle.class, new BenchmarkResultsSingleAdapter())
+ .create();
+ out.print(gson.toJson(this));
+ }
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingleAdapter.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingleAdapter.java
new file mode 100644
index 0000000..55936d7
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsSingleAdapter.java
@@ -0,0 +1,34 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.benchmarks;
+
+import com.android.tools.r8.utils.ListUtils;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import java.lang.reflect.Type;
+
+public class BenchmarkResultsSingleAdapter implements JsonSerializer<BenchmarkResultsSingle> {
+
+ @Override
+ public JsonElement serialize(
+ BenchmarkResultsSingle result, Type type, JsonSerializationContext jsonSerializationContext) {
+ JsonArray resultsArray = new JsonArray();
+ ListUtils.forEachWithIndex(
+ result.getCodeSizeResults(),
+ (codeSizeResult, iteration) -> {
+ JsonObject resultObject = new JsonObject();
+ resultObject.addProperty("code_size", codeSizeResult);
+ resultObject.addProperty("runtime", result.getRuntimeResults().getLong(iteration));
+ resultsArray.add(resultObject);
+ });
+
+ JsonObject benchmarkObject = new JsonObject();
+ benchmarkObject.addProperty("benchmark_name", result.getName());
+ benchmarkObject.add("results", resultsArray);
+ return benchmarkObject;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsWarmup.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsWarmup.java
index 2ac97a6..1a4e33c 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsWarmup.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkResultsWarmup.java
@@ -3,8 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.benchmarks;
+import com.android.tools.r8.errors.Unimplemented;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongList;
+import java.io.PrintStream;
public class BenchmarkResultsWarmup implements BenchmarkResults {
@@ -51,7 +53,7 @@
}
@Override
- public void printResults(ResultMode mode) {
+ public void printResults(ResultMode mode, boolean failOnCodeSizeDifferences) {
if (runtimeResults.isEmpty()) {
throw new BenchmarkConfigError("Expected runtime results for warmup run");
}
@@ -61,4 +63,9 @@
BenchmarkResults.prettyMetric(
name, BenchmarkMetric.StartupTime, BenchmarkResults.prettyTime(result)));
}
+
+ @Override
+ public void writeResults(PrintStream out) {
+ throw new Unimplemented();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkRunner.java b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkRunner.java
index 2fed6dc..78441e3 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/BenchmarkRunner.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/BenchmarkRunner.java
@@ -4,6 +4,9 @@
package com.android.tools.r8.benchmarks;
import com.android.tools.r8.benchmarks.BenchmarkResults.ResultMode;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.file.Files;
public class BenchmarkRunner {
@@ -11,17 +14,17 @@
void run(BenchmarkResults results) throws Exception;
}
- private final BenchmarkConfig config;
+ private final BenchmarkEnvironment environment;
private int warmups = 0;
private int iterations = 1;
private ResultMode resultMode = BenchmarkResults.ResultMode.AVERAGE;
- private BenchmarkRunner(BenchmarkConfig config) {
- this.config = config;
+ private BenchmarkRunner(BenchmarkEnvironment environment) {
+ this.environment = environment;
}
- public static BenchmarkRunner runner(BenchmarkConfig config) {
- return new BenchmarkRunner(config);
+ public static BenchmarkRunner runner(BenchmarkEnvironment environment) {
+ return new BenchmarkRunner(environment);
}
public BenchmarkRunner setWarmupIterations(int iterations) {
@@ -29,6 +32,12 @@
return this;
}
+ public int getBenchmarkIterations() {
+ return environment.hasBenchmarkIterationsOverride()
+ ? environment.getBenchmarkIterationsOverride()
+ : iterations;
+ }
+
public BenchmarkRunner setBenchmarkIterations(int iterations) {
this.iterations = iterations;
return this;
@@ -46,6 +55,7 @@
public void run(BenchmarkRunnerFunction fn) throws Exception {
long warmupTotalTime = 0;
+ BenchmarkConfig config = environment.getConfig();
BenchmarkResults warmupResults = new BenchmarkResultsWarmup(config.getName());
if (warmups > 0) {
long start = System.nanoTime();
@@ -59,7 +69,7 @@
? new BenchmarkResultsSingle(config.getName(), config.getMetrics())
: new BenchmarkResultsCollection(config.getSubBenchmarks());
long start = System.nanoTime();
- for (int i = 0; i < iterations; i++) {
+ for (int i = 0; i < getBenchmarkIterations(); i++) {
fn.run(results);
}
long benchmarkTotalTime = System.nanoTime() - start;
@@ -71,11 +81,14 @@
if (warmups > 0) {
printMetaInfo("warmup", warmups, warmupTotalTime);
if (config.hasTimeWarmupRuns()) {
- warmupResults.printResults(resultMode);
+ warmupResults.printResults(resultMode, environment.failOnCodeSizeDifferences());
}
}
- printMetaInfo("benchmark", iterations, benchmarkTotalTime);
- results.printResults(resultMode);
+ printMetaInfo("benchmark", getBenchmarkIterations(), benchmarkTotalTime);
+ results.printResults(resultMode, environment.failOnCodeSizeDifferences());
+ if (environment.hasOutputPath()) {
+ writeResults(results);
+ }
System.out.println();
}
@@ -84,4 +97,11 @@
System.out.println(" " + kind + " iterations: " + iterations);
System.out.println(" " + kind + " total time: " + BenchmarkResults.prettyTime(totalTime));
}
+
+ private void writeResults(BenchmarkResults results) throws IOException {
+ try (PrintStream printStream =
+ new PrintStream(Files.newOutputStream(environment.getOutputPath()))) {
+ results.writeResults(printStream);
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java b/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
index 7e2a677..ac4f229 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/appdumps/AppDumpBenchmarkBuilder.java
@@ -203,7 +203,7 @@
private static BenchmarkMethod internalRunR8(
AppDumpBenchmarkBuilder builder, boolean enableResourceShrinking) {
return environment ->
- BenchmarkBase.runner(environment.getConfig())
+ BenchmarkBase.runner(environment)
.setWarmupIterations(1)
.run(
results -> {
@@ -244,7 +244,7 @@
private static BenchmarkMethod runBatchD8(AppDumpBenchmarkBuilder builder) {
return environment ->
- BenchmarkBase.runner(environment.getConfig())
+ BenchmarkBase.runner(environment)
.setWarmupIterations(1)
.run(
results -> {
@@ -262,7 +262,7 @@
private static BenchmarkMethod runIncrementalD8(AppDumpBenchmarkBuilder builder) {
return environment ->
- BenchmarkBase.runner(environment.getConfig())
+ BenchmarkBase.runner(environment)
.setWarmupIterations(1)
.reportResultSum()
.run(
diff --git a/src/test/java/com/android/tools/r8/benchmarks/desugaredlib/L8Benchmark.java b/src/test/java/com/android/tools/r8/benchmarks/desugaredlib/L8Benchmark.java
index 3f12fc8..adfa5f8 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/desugaredlib/L8Benchmark.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/desugaredlib/L8Benchmark.java
@@ -65,7 +65,7 @@
ImmutableSet.of(ANDROID_JAR.getRoot(environment).resolve("android.jar")),
LibraryDesugaringSpecification.JDK11_DESCRIPTOR,
"");
- runner(environment.getConfig())
+ runner(environment)
.setWarmupIterations(1)
.setBenchmarkIterations(10)
.reportResultSum()
diff --git a/src/test/java/com/android/tools/r8/benchmarks/desugaredlib/LegacyDesugaredLibraryBenchmark.java b/src/test/java/com/android/tools/r8/benchmarks/desugaredlib/LegacyDesugaredLibraryBenchmark.java
index 6b5e4f9..2d50a3d 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/desugaredlib/LegacyDesugaredLibraryBenchmark.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/desugaredlib/LegacyDesugaredLibraryBenchmark.java
@@ -54,7 +54,7 @@
}
public static void run(BenchmarkEnvironment environment) throws Exception {
- runner(environment.getConfig())
+ runner(environment)
.setWarmupIterations(1)
.setBenchmarkIterations(10)
.reportResultSum()
diff --git a/src/test/java/com/android/tools/r8/benchmarks/helloworld/HelloWorldBenchmark.java b/src/test/java/com/android/tools/r8/benchmarks/helloworld/HelloWorldBenchmark.java
index cb43a1d..4c8c31c 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/helloworld/HelloWorldBenchmark.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/helloworld/HelloWorldBenchmark.java
@@ -101,7 +101,7 @@
public static BenchmarkMethod benchmarkD8(Options options) {
return environment ->
- runner(environment.getConfig())
+ runner(environment)
.setWarmupIterations(1)
.setBenchmarkIterations(100)
.reportResultSum()
@@ -119,7 +119,7 @@
public static BenchmarkMethod benchmarkR8(Options options) {
return environment ->
- runner(environment.getConfig())
+ runner(environment)
.setWarmupIterations(1)
.setBenchmarkIterations(4)
.reportResultSum()
diff --git a/src/test/java/com/android/tools/r8/benchmarks/retrace/RetraceStackTraceBenchmark.java b/src/test/java/com/android/tools/r8/benchmarks/retrace/RetraceStackTraceBenchmark.java
index 28e1e83..c3cd9d0 100644
--- a/src/test/java/com/android/tools/r8/benchmarks/retrace/RetraceStackTraceBenchmark.java
+++ b/src/test/java/com/android/tools/r8/benchmarks/retrace/RetraceStackTraceBenchmark.java
@@ -59,7 +59,7 @@
public static BenchmarkMethod benchmarkRetrace() {
return environment ->
- runner(environment.getConfig())
+ runner(environment)
.setWarmupIterations(1)
.setBenchmarkIterations(4)
.reportResultSum()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java
index fa46f4d..0d912b3 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMembersAccessedTest.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.classmerging.horizontal;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -36,9 +35,7 @@
.assertSuccessWithOutputLines("foo", "hello", "5", "foobar")
.inspect(
codeInspector -> {
- assertThat(
- codeInspector.clazz(C.class),
- isAbsentIf(parameters.canInitNewInstanceUsingSuperclassConstructor()));
+ assertThat(codeInspector.clazz(C.class), isPresent());
assertThat(codeInspector.clazz(D.class), isPresent());
assertThat(codeInspector.clazz(E.class), isPresent());
});
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAccessControlTestUtils.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAccessControlTestUtils.java
index 8f73003..cba6309 100644
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAccessControlTestUtils.java
+++ b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestAccessControlTestUtils.java
@@ -4,13 +4,9 @@
package com.android.tools.r8.desugar.nestaccesscontrol;
-import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
import static com.android.tools.r8.utils.FileUtils.JAR_EXTENSION;
-import static java.util.stream.Collectors.toList;
-import static org.hamcrest.core.StringContains.containsString;
import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.nio.file.Path;
@@ -23,7 +19,6 @@
Paths.get(ToolHelper.EXAMPLES_JAVA11_JAR_DIR).resolve("nesthostexample" + JAR_EXTENSION);
public static final Path CLASSES_PATH =
Paths.get(ToolHelper.getExamplesJava11BuildDir()).resolve("nesthostexample/");
- public static final String PACKAGE_NAME = "nesthostexample.";
public static final List<String> CLASS_NAMES =
ImmutableList.of(
@@ -68,13 +63,6 @@
"NestHostExample$StaticNestMemberInner$StaticNestMemberInnerInner",
"NestHostExample$StaticNestInterfaceInner",
"NestHostExample$ExampleEnumCompilation");
- public static final int NUMBER_OF_TEST_CLASSES = CLASS_NAMES.size();
-
- // The following map use ids, i.e., strings which represents respectively
- // a nest with only field, method, constructor, anonymous class and
- // all at once nest based private accesses.
- public static final ImmutableList<String> NEST_IDS =
- ImmutableList.of("fields", "methods", "constructors", "anonymous", "all");
public static final ImmutableMap<String, String> MAIN_CLASSES =
ImmutableMap.<String, String>builder()
.put("fields", "BasicNestHostWithInnerClassFields")
@@ -89,94 +77,10 @@
.put("pvtCallInlined", "NestPvtMethodCallInlined")
.put("memberPropagated", "NestPvtFieldPropagated")
.build();
- public static final String ALL_RESULT_LINE =
- String.join(
- ", ",
- new String[] {
- "field",
- "staticField",
- "staticField",
- "hostMethod",
- "staticHostMethod",
- "staticHostMethod",
- "nest1SField",
- "staticNest1SField",
- "staticNest1SField",
- "nest1SMethod",
- "staticNest1SMethod",
- "staticNest1SMethod",
- "nest2SField",
- "staticNest2SField",
- "staticNest2SField",
- "nest2SMethod",
- "staticNest2SMethod",
- "staticNest2SMethod",
- "nest1Field",
- "nest1Method",
- "nest2Field",
- "nest2Method"
- });
- public static final ImmutableMap<String, String> EXPECTED_RESULTS =
- ImmutableMap.<String, String>builder()
- .put(
- "fields",
- StringUtils.lines(
- "RWnestFieldRWRWnestFieldRWRWnestFieldnoBridge", "RWfieldRWRWfieldRWRWnestField"))
- .put(
- "methods",
- StringUtils.lines(
- "nestMethodstaticNestMethodstaticNestMethodnoBridge",
- "hostMethodstaticHostMethodstaticNestMethod"))
- .put(
- "constructors",
- StringUtils.lines(
- "field", "nest1SField", "1", "innerFieldUnusedConstructor", "nothing"))
- .put(
- "anonymous",
- StringUtils.lines(
- "fieldstaticFieldstaticFieldhostMethodstaticHostMethodstaticHostMethod"))
- .put(
- "all",
- StringUtils.lines(
- ALL_RESULT_LINE,
- ALL_RESULT_LINE,
- ALL_RESULT_LINE,
- ALL_RESULT_LINE,
- "staticInterfaceMethodstaticStaticInterfaceMethod",
- "staticInterfaceMethodstaticStaticInterfaceMethod",
- "staticInterfaceMethodstaticStaticInterfaceMethod",
- "staticInterfaceMethodstaticStaticInterfaceMethod",
- "3"))
- .put(
- "pvtCallInlined",
- StringUtils.lines(
- "nestPvtCallToInlineInner",
- "nestPvtCallToInlineInnerInterface",
- "notInlinedPvtCallInner",
- "notInlinedPvtCallInnerInterface",
- "notInlinedPvtCallInnerSub",
- "notInlinedPvtCallInnerInterface",
- "nestPvtCallToInlineInnerSub",
- "nestPvtCallToInlineInner"))
- .put("memberPropagated", StringUtils.lines("toPropagateStatic"))
- .build();
public static String getMainClass(String id) {
- return PACKAGE_NAME + MAIN_CLASSES.get(id);
+ return "nesthostexample." + MAIN_CLASSES.get(id);
}
- public static String getExpectedResult(String id) {
- return EXPECTED_RESULTS.get(id);
- }
- public static List<Path> classesOfNest(String nestID) {
- return classesMatching(MAIN_CLASSES.get(nestID));
- }
-
- public static List<Path> classesMatching(String matcher) {
- return CLASS_NAMES.stream()
- .filter(name -> containsString(matcher).matches(name))
- .map(name -> CLASSES_PATH.resolve(name + CLASS_EXTENSION))
- .collect(toList());
- }
}
diff --git a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestOnProgramAndClassPathTest.java b/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestOnProgramAndClassPathTest.java
deleted file mode 100644
index 464e2c1..0000000
--- a/src/test/java/com/android/tools/r8/desugar/nestaccesscontrol/NestOnProgramAndClassPathTest.java
+++ /dev/null
@@ -1,131 +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.desugar.nestaccesscontrol;
-
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.CLASSES_PATH;
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.CLASS_NAMES;
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.JAR;
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.getExpectedResult;
-import static com.android.tools.r8.desugar.nestaccesscontrol.NestAccessControlTestUtils.getMainClass;
-import static com.android.tools.r8.utils.FileUtils.CLASS_EXTENSION;
-import static java.util.stream.Collectors.toList;
-import static org.hamcrest.core.StringContains.containsString;
-import static org.hamcrest.core.StringEndsWith.endsWith;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import com.android.tools.r8.D8TestCompileResult;
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
-import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
-import java.nio.file.Path;
-import java.util.List;
-import org.hamcrest.Matcher;
-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 NestOnProgramAndClassPathTest extends TestBase {
-
- public NestOnProgramAndClassPathTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- private final TestParameters parameters;
-
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withDexRuntimes().withAllApiLevels().build();
- }
-
- @Test
- public void testD8MethodBridgesPresent() throws Exception {
- parameters.assumeDexRuntime();
- // 1 inner class.
- D8TestCompileResult singleInner =
- compileClassesWithD8ProgramClassesMatching(
- containsString("BasicNestHostWithInnerClassMethods$BasicNestedClass"));
- singleInner.inspect(inspector -> assertThisNumberOfBridges(inspector, 2));
- // Outer class.
- D8TestCompileResult host =
- compileClassesWithD8ProgramClassesMatching(endsWith("BasicNestHostWithInnerClassMethods"));
- host.inspect(inspector -> assertThisNumberOfBridges(inspector, 2));
- // 2 inner classes.
- D8TestCompileResult multipleInner =
- compileClassesWithD8ProgramClassesMatching(
- containsString("NestHostExample$StaticNestMemberInner"));
- multipleInner.inspect(inspector -> assertThisNumberOfBridges(inspector, 5));
- }
-
- @Test
- public void testD8ConstructorBridgesPresent() throws Exception {
- parameters.assumeDexRuntime();
- D8TestCompileResult inner =
- compileClassesWithD8ProgramClassesMatching(
- containsString("BasicNestHostWithInnerClassConstructors$BasicNestedClass"));
- inner.inspect(
- inspector -> {
- assertThisNumberOfBridges(inspector, 3);
- assertNestConstructor(inspector);
- });
- D8TestCompileResult host =
- compileClassesWithD8ProgramClassesMatching(
- endsWith("BasicNestHostWithInnerClassConstructors"));
- host.inspect(
- inspector -> {
- assertThisNumberOfBridges(inspector, 1);
- assertNestConstructor(inspector);
- });
- }
-
- @Test
- public void testD8ConstructorNestMergeCorrect() throws Exception {
- // Multiple Nest Constructor classes have to be merged here.
- parameters.assumeDexRuntime();
- D8TestCompileResult inner =
- compileClassesWithD8ProgramClassesMatching(
- containsString("BasicNestHostWithInnerClassConstructors$BasicNestedClass"));
- D8TestCompileResult host =
- compileClassesWithD8ProgramClassesMatching(
- endsWith("BasicNestHostWithInnerClassConstructors"));
- testForD8()
- .addProgramFiles(inner.writeToZip(), host.writeToZip())
- .setMinApi(parameters)
- .compile()
- .run(parameters.getRuntime(), getMainClass("constructors"))
- .assertSuccessWithOutput(getExpectedResult("constructors"));
- }
-
- private D8TestCompileResult compileClassesWithD8ProgramClassesMatching(Matcher<String> matcher)
- throws Exception {
- List<Path> matchingClasses =
- CLASS_NAMES.stream()
- .filter(matcher::matches)
- .map(name -> CLASSES_PATH.resolve(name + CLASS_EXTENSION))
- .collect(toList());
- return testForD8()
- .setMinApi(parameters)
- .addProgramFiles(matchingClasses)
- .addClasspathFiles(JAR)
- .compile();
- }
-
- private static void assertNestConstructor(CodeInspector inspector) {
- assertTrue(inspector.allClasses().stream().anyMatch(FoundClassSubject::isSynthetic));
- }
-
- private static void assertThisNumberOfBridges(CodeInspector inspector, int numBridges) {
- for (FoundClassSubject clazz : inspector.allClasses()) {
- if (!clazz.isSynthetic()) {
- assertEquals(numBridges, clazz.allMethods(FoundMethodSubject::isSynthetic).size());
- }
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java b/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
index c7c00b8..6499f3d 100644
--- a/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
+++ b/src/test/java/com/android/tools/r8/ir/conversion/CallGraphTestBase.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.callgraph.Node;
import com.android.tools.r8.origin.SynthesizedOrigin;
+import com.android.tools.r8.utils.ReachabilitySensitiveValue;
import java.util.Collections;
class CallGraphTestBase extends TestBase {
@@ -44,7 +45,8 @@
DexEncodedField.EMPTY_ARRAY,
MethodCollectionFactory.empty(),
false,
- DexProgramClass::invalidChecksumRequest);
+ DexProgramClass::invalidChecksumRequest,
+ ReachabilitySensitiveValue.DISABLED);
Node createNode(String methodName) {
DexMethod signature =
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/Regress345248270ConstClassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/Regress345248270ConstClassTest.java
new file mode 100644
index 0000000..684f3a4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/Regress345248270ConstClassTest.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2024, 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.effectivelytrivialphioptimization;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ir.optimize.effectivelytrivialphioptimization.b345248270.I;
+import com.android.tools.r8.ir.optimize.effectivelytrivialphioptimization.b345248270.PublicAccessor;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class Regress345248270ConstClassTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("true");
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addProgramClasses(
+ I.class, PublicAccessor.class, PublicAccessor.getPackagePrivateImplementationClass())
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addProgramClasses(
+ I.class, PublicAccessor.class, PublicAccessor.getPackagePrivateImplementationClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableNeverClassInliningAnnotations()
+ .enableNoAccessModificationAnnotationsForMembers()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ static class TestClass {
+ public static Class<?> test() {
+ Class<?> r = null;
+ if (System.currentTimeMillis() > 0) {
+ r = PublicAccessor.getPackagePrivateImplementationClass();
+ } else {
+ System.out.println("Do something");
+ r = PublicAccessor.getPackagePrivateImplementationClass();
+ }
+ return r;
+ }
+
+ public static Class<?> test2() {
+ return System.currentTimeMillis() > 0
+ ? PublicAccessor.getPackagePrivateImplementationClass()
+ : null;
+ }
+
+ public static void main(String[] args) {
+ Class<?> c = test();
+ System.out.println(c == test2());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/Regress345248270Test.java b/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/Regress345248270Test.java
new file mode 100644
index 0000000..0b0fcb3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/Regress345248270Test.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2024, 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.effectivelytrivialphioptimization;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+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.ir.optimize.effectivelytrivialphioptimization.b345248270.I;
+import com.android.tools.r8.ir.optimize.effectivelytrivialphioptimization.b345248270.PublicAccessor;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.Matchers;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class Regress345248270Test extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("true");
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addProgramClasses(
+ I.class, PublicAccessor.class, PublicAccessor.getPackagePrivateImplementationClass())
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addProgramClasses(
+ I.class, PublicAccessor.class, PublicAccessor.getPackagePrivateImplementationClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getApiLevel())
+ .enableNeverClassInliningAnnotations()
+ .enableNoAccessModificationAnnotationsForMembers()
+ .run(parameters.getRuntime(), TestClass.class)
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz(TestClass.class), isPresent());
+ // test is inlined into main.
+ assertThat(
+ inspector.clazz(TestClass.class).uniqueMethodWithOriginalName("test"),
+ Matchers.isAbsent());
+ ClassSubject packagePrivateImplementation =
+ inspector.clazz(PublicAccessor.getPackagePrivateImplementationClass());
+ assertThat(packagePrivateImplementation, isPresent());
+ // No direct field access of the package private field.
+ assertTrue(
+ inspector
+ .clazz(TestClass.class)
+ .mainMethod()
+ .streamInstructions()
+ .filter(InstructionSubject::isFieldAccess)
+ .map(InstructionSubject::getField)
+ .noneMatch(
+ f ->
+ f.getType()
+ .isIdenticalTo(
+ packagePrivateImplementation
+ .getDexProgramClass()
+ .getType())));
+ })
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ static class TestClass {
+ public static I test() {
+ I r = null;
+ if (System.currentTimeMillis() > 0) {
+ r = PublicAccessor.getPackagePrivateImplementation();
+ } else {
+ System.out.println("Do something");
+ r = PublicAccessor.getPackagePrivateImplementation();
+ }
+ return r;
+ }
+
+ public static I test2() {
+ return System.currentTimeMillis() > 0
+ ? PublicAccessor.getPackagePrivateImplementation()
+ : null;
+ }
+
+ public static void main(String[] args) {
+ I i = test();
+ System.out.println(i == test2());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/b345248270/I.java b/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/b345248270/I.java
new file mode 100644
index 0000000..ea07f32
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/b345248270/I.java
@@ -0,0 +1,6 @@
+// Copyright (c) 2024, 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.effectivelytrivialphioptimization.b345248270;
+
+public interface I {}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/b345248270/PackagePrivateImplementation.java b/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/b345248270/PackagePrivateImplementation.java
new file mode 100644
index 0000000..7d615db
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/b345248270/PackagePrivateImplementation.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2024, 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.effectivelytrivialphioptimization.b345248270;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoAccessModification;
+
+@NeverClassInline
+class PackagePrivateImplementation implements I {
+ @NoAccessModification static final I NULL = new PackagePrivateImplementation();
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/b345248270/PublicAccessor.java b/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/b345248270/PublicAccessor.java
new file mode 100644
index 0000000..52ab243
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/effectivelytrivialphioptimization/b345248270/PublicAccessor.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2024, 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.effectivelytrivialphioptimization.b345248270;
+
+public class PublicAccessor {
+ public static I getPackagePrivateImplementation() {
+ return PackagePrivateImplementation.NULL;
+ }
+
+ public static Class<?> getPackagePrivateImplementationClass() {
+ return PackagePrivateImplementation.class;
+ }
+}
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 ea19697..4ff3f4f 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
@@ -4,8 +4,8 @@
package com.android.tools.r8.ir.optimize.inliner;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestRunResult;
@@ -87,10 +87,16 @@
.transform();
}
- private boolean compilationTargetIsMissingExceptionType() {
+ private boolean isStubbed() {
+ // Note: this is a simplified version of ApiReferenceStubber.isAlwaysStubbedType only testing
+ // the types relevant for this test.
+ return !exception.startsWith("java.") && !exception.startsWith("javax.");
+ }
+
+ private boolean isPresentInRuntime() {
// A CF target could target any API in the end.
- return parameters.isCfRuntime()
- || parameters.getApiLevel().getLevel() < EXCEPTIONS.get(exception);
+ return parameters.isDexRuntime()
+ && parameters.getApiLevel().getLevel() >= EXCEPTIONS.get(exception);
}
@Test
@@ -101,7 +107,7 @@
.addProgramClassFileData(getClassWithCatchHandler())
.addKeepMainRule(TestClass.class)
.setMinApi(parameters)
- // Use the latest library so that all of the exceptions are defined.
+ // Use the latest library so that all the exceptions are defined.
.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
.compile()
.inspect(this::checkInlined)
@@ -111,7 +117,7 @@
private void checkResult(R8TestRunResult runResult) {
// The bootclasspath for our build of 4.4.4 does not contain various bits. Allow verify error.
- if (!compilationTargetIsMissingExceptionType()
+ if (isPresentInRuntime()
&& parameters.getRuntime().asDex().getVm().getVersion().equals(Version.V4_4_4)
&& (exception.startsWith("android.media") || exception.startsWith("android.view"))) {
runResult.assertFailureWithErrorThatThrows(VerifyError.class);
@@ -126,10 +132,15 @@
boolean mainHasInlinedCatchHandler =
Streams.stream(classSubject.mainMethod().iterateTryCatches())
.anyMatch(tryCatch -> tryCatch.isCatching(exception));
- if (compilationTargetIsMissingExceptionType()) {
+ if (parameters.isCfRuntime()) {
assertFalse(mainHasInlinedCatchHandler);
} else {
- assertTrue(mainHasInlinedCatchHandler);
+ assertEquals(
+ // Dalvik verifier error present up to and not including L.
+ parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.L)
+ ? parameters.getApiLevel().getLevel() >= EXCEPTIONS.get(exception)
+ : isPresentInRuntime() || isStubbed(),
+ mainHasInlinedCatchHandler);
}
}
@@ -137,7 +148,7 @@
public static void main(String[] args) {
if (args.length == 200) {
- // Never called
+ // Never called.
ClassWithCatchHandler.methodWithCatch();
}
System.out.println("Done...");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldWithConstructorInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldWithConstructorInliningTest.java
index 142d565..f952fa9 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldWithConstructorInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldWithConstructorInliningTest.java
@@ -3,10 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize.membervaluepropagation;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.TestBase;
@@ -42,11 +42,17 @@
.inspect(
inspector -> {
ClassSubject aClassSubject = inspector.clazz(A.class);
- assertThat(aClassSubject, isAbsent());
+ // TODO(b/339210038): Should always be absent.
+ assertThat(
+ aClassSubject,
+ isPresentIf(parameters.canInitNewInstanceUsingSuperclassConstructor()));
MethodSubject mainMethodSubject = inspector.clazz(Main.class).mainMethod();
assertThat(mainMethodSubject, isPresent());
- assertTrue(mainMethodSubject.streamInstructions().anyMatch(i -> i.isConstNumber(42)));
+ // TODO(b/339210038): Should always contain 42.
+ assertEquals(
+ parameters.canInitNewInstanceUsingSuperclassConstructor(),
+ mainMethodSubject.streamInstructions().noneMatch(i -> i.isConstNumber(42)));
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("42");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentMultipleConstructorsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentMultipleConstructorsTest.java
index 862fbc9..fca4473 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentMultipleConstructorsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByConstantArgumentMultipleConstructorsTest.java
@@ -4,8 +4,8 @@
package com.android.tools.r8.ir.optimize.membervaluepropagation.fields;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
@@ -18,20 +18,21 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameter;
-import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class FieldInitializedByConstantArgumentMultipleConstructorsTest extends TestBase {
- @Parameter(0)
- public TestParameters parameters;
+ private final TestParameters parameters;
- @Parameters(name = "{0}")
+ @Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
+ public FieldInitializedByConstantArgumentMultipleConstructorsTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
@Test
public void test() throws Exception {
testForR8(parameters.getBackend())
@@ -50,7 +51,10 @@
ClassSubject testClassSubject = inspector.clazz(TestClass.class);
assertThat(testClassSubject, isPresent());
assertThat(testClassSubject.uniqueMethodWithOriginalName("live"), isPresent());
- assertThat(testClassSubject.uniqueMethodWithOriginalName("dead"), isAbsent());
+ // TODO(b/280275115): Constructor inlining regresses instance field value analysis.
+ assertThat(
+ testClassSubject.uniqueMethodWithOriginalName("dead"),
+ isPresentIf(parameters.canInitNewInstanceUsingSuperclassConstructor()));
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantarraygetelimination/RedundantArrayGetEliminationWithThrowingArrayAccessTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantarraygetelimination/RedundantArrayGetEliminationWithThrowingArrayAccessTest.java
new file mode 100644
index 0000000..de5ba07
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantarraygetelimination/RedundantArrayGetEliminationWithThrowingArrayAccessTest.java
@@ -0,0 +1,278 @@
+// Copyright (c) 2024, 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.redundantarraygetelimination;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FieldSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class RedundantArrayGetEliminationWithThrowingArrayAccessTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private static final String EXPECTED_OUTPUT =
+ StringUtils.lines("-1", "-3", "-6", "-10", "42011", "9", "42010", "8", "13");
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ private void countInstanceGetOfField(int count, MethodSubject method, FieldSubject field) {
+ assertThat(method, isPresentAndRenamed());
+ assertThat(field, isPresentAndRenamed());
+ assertEquals(
+ count,
+ method
+ .streamInstructions()
+ .filter(InstructionSubject::isInstancePut)
+ .map(InstructionSubject::getField)
+ .filter(f -> f.isIdenticalTo(field.getDexField()))
+ .count());
+ }
+
+ private void zeroArrayGet(MethodSubject method) {
+ countArrayGet(0, method);
+ }
+
+ private void oneArrayGet(MethodSubject method) {
+ countArrayGet(1, method);
+ }
+
+ private void countArrayGet(int count, MethodSubject method) {
+ assertThat(method, isPresentAndRenamed());
+ assertEquals(count, method.streamInstructions().filter(InstructionSubject::isArrayGet).count());
+ }
+
+ private void oneInstanceGetOfField(MethodSubject method, FieldSubject field) {
+ countInstanceGetOfField(1, method, field);
+ }
+
+ private void twoInstanceGetOfField(MethodSubject method, FieldSubject field) {
+ countInstanceGetOfField(2, method, field);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ ClassSubject testClass = inspector.clazz(TestClass.class);
+ assertThat(testClass, isPresentAndNotRenamed());
+ FieldSubject iFld1 = testClass.uniqueFieldWithOriginalName("iFld1");
+ twoInstanceGetOfField(
+ testClass.uniqueMethodWithOriginalName("arrayOfKnownSizePutThrows"), iFld1);
+ zeroArrayGet(testClass.uniqueMethodWithOriginalName("arrayOfKnownSizePutThrows"));
+ twoInstanceGetOfField(
+ testClass.uniqueMethodWithOriginalName("arrayOfKnownSizeGetThrows"), iFld1);
+ oneArrayGet(testClass.uniqueMethodWithOriginalName("arrayOfKnownSizeGetThrows"));
+ twoInstanceGetOfField(
+ testClass.uniqueMethodWithOriginalName("arrayOfUnknownSizePutThrows"), iFld1);
+ zeroArrayGet(testClass.uniqueMethodWithOriginalName("arrayOfUnknownSizePutThrows"));
+ twoInstanceGetOfField(
+ testClass.uniqueMethodWithOriginalName("arrayOfUnknownSizeGetThrows"), iFld1);
+ oneArrayGet(testClass.uniqueMethodWithOriginalName("arrayOfUnknownSizeGetThrows"));
+ oneInstanceGetOfField(
+ testClass.uniqueMethodWithOriginalName("arrayOfKnownSizePutSucceeds"), iFld1);
+ zeroArrayGet(testClass.uniqueMethodWithOriginalName("arrayOfKnownSizePutSucceeds"));
+ oneInstanceGetOfField(
+ testClass.uniqueMethodWithOriginalName("arrayOfKnownSizeGetSucceeds"), iFld1);
+ zeroArrayGet(testClass.uniqueMethodWithOriginalName("arrayOfKnownSizeGetSucceeds"));
+ twoInstanceGetOfField(
+ testClass.uniqueMethodWithOriginalName("arrayOfUnknownSizePutSucceeds"), iFld1);
+ zeroArrayGet(testClass.uniqueMethodWithOriginalName("arrayOfUnknownSizePutSucceeds"));
+ twoInstanceGetOfField(
+ testClass.uniqueMethodWithOriginalName("arrayOfUnknownSizeGetSucceeds"), iFld1);
+ oneArrayGet(testClass.uniqueMethodWithOriginalName("arrayOfUnknownSizeGetSucceeds"));
+ twoInstanceGetOfField(
+ testClass.uniqueMethodWithOriginalName("arrayOfUnknownSizePutAndGetSucceeds"), iFld1);
+ zeroArrayGet(testClass.uniqueMethodWithOriginalName("arrayOfUnknownSizePutAndGetSucceeds"));
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters)
+ .release()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters)
+ .enableInliningAnnotations()
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT)
+ .inspect(this::inspect);
+ }
+
+ static class TestClass {
+
+ int iFld1;
+
+ long[] array() {
+ return new long[System.currentTimeMillis() > 0 ? 10 : 20];
+ }
+
+ @NeverInline
+ void arrayOfKnownSizePutThrows() {
+ int i5 = 61127, i7 = 42011;
+ long[] lArr = new long[10];
+ try {
+ iFld1 -= 1L;
+ lArr[i5] = i7;
+ iFld1 = i7;
+ } catch (ArrayIndexOutOfBoundsException err) {
+ }
+ System.out.println(iFld1);
+ }
+
+ @NeverInline
+ void arrayOfKnownSizeGetThrows() {
+ int i5 = 61127, i7 = 42011;
+ long[] lArr = new long[10];
+ try {
+ iFld1 -= 2L;
+ long x = lArr[i5];
+ iFld1 = i7;
+ } catch (ArrayIndexOutOfBoundsException err) {
+ }
+ System.out.println(iFld1);
+ }
+
+ @NeverInline
+ void arrayOfUnknownSizePutThrows() {
+ int i5 = 61127, i7 = 42011;
+ long[] lArr = array();
+ try {
+ iFld1 -= 3L;
+ lArr[i5] = i7;
+ iFld1 = i7;
+ } catch (ArrayIndexOutOfBoundsException err) {
+ }
+ System.out.println(iFld1);
+ }
+
+ @NeverInline
+ void arrayOfUnknownSizeGetThrows() {
+ int i5 = 61127, i7 = 42011;
+ long[] lArr = array();
+ try {
+ iFld1 -= 4L;
+ long x = lArr[i5];
+ iFld1 = i7;
+ } catch (ArrayIndexOutOfBoundsException err) {
+ }
+ System.out.println(iFld1);
+ }
+
+ @NeverInline
+ void arrayOfKnownSizePutSucceeds() {
+ int i5 = 9, i7 = 42011;
+ long[] lArr = new long[10];
+ try {
+ iFld1 -= 5L;
+ lArr[i5] = i7;
+ iFld1 = i7;
+ } catch (ArrayIndexOutOfBoundsException err) {
+ }
+ System.out.println(iFld1);
+ }
+
+ @NeverInline
+ void arrayOfKnownSizeGetSucceeds() {
+ int i5 = 9, i7 = 42011;
+ long[] lArr = new long[10];
+ try {
+ iFld1 -= 6L;
+ long x = lArr[i5];
+ iFld1 = i5;
+ } catch (ArrayIndexOutOfBoundsException err) {
+ }
+ System.out.println(iFld1);
+ }
+
+ @NeverInline
+ void arrayOfUnknownSizePutSucceeds() {
+ int i5 = 9, i7 = 42010;
+ long[] lArr = array();
+ try {
+ iFld1 -= 225L;
+ lArr[i5] = i7;
+ iFld1 = i7;
+ } catch (ArrayIndexOutOfBoundsException err) {
+ }
+ System.out.println(iFld1);
+ }
+
+ @NeverInline
+ void arrayOfUnknownSizeGetSucceeds() {
+ int i5 = 8, i7 = 42011;
+ long[] lArr = array();
+ try {
+ iFld1 -= 225L;
+ long x = lArr[i5];
+ iFld1 = i5;
+ } catch (ArrayIndexOutOfBoundsException err) {
+ }
+ System.out.println(iFld1);
+ }
+
+ @NeverInline
+ void arrayOfUnknownSizePutAndGetSucceeds() {
+ int i5 = 0, i7 = 13;
+ long[] lArr = array();
+ try {
+ iFld1 -= 5L;
+ lArr[i5] = i7;
+ iFld1 = (int) lArr[i5];
+ } catch (ArrayIndexOutOfBoundsException err) {
+ }
+ System.out.println(iFld1);
+ }
+
+ public static void main(String[] strArr) {
+ TestClass test = new TestClass();
+ test.arrayOfKnownSizePutThrows();
+ test.arrayOfKnownSizeGetThrows();
+ test.arrayOfUnknownSizePutThrows();
+ test.arrayOfUnknownSizeGetThrows();
+ test.arrayOfKnownSizePutSucceeds();
+ test.arrayOfKnownSizeGetSucceeds();
+ test.arrayOfUnknownSizePutSucceeds();
+ test.arrayOfUnknownSizeGetSucceeds();
+ test.arrayOfUnknownSizePutAndGetSucceeds();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/ClassNameStringPatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/ClassNameStringPatternsTest.java
index 02195c5..43cf4a7 100644
--- a/src/test/java/com/android/tools/r8/keepanno/ClassNameStringPatternsTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/ClassNameStringPatternsTest.java
@@ -80,7 +80,7 @@
@TypePattern(
classNamePattern =
@ClassNamePattern(
- simpleNamePattern =
+ unqualifiedNamePattern =
@StringPattern(startsWith = "ClassNameStringPatternsTest$Foo")))
}),
@KeepTarget(
@@ -89,7 +89,7 @@
methodParameterTypePatterns = {
@TypePattern(
classNamePattern =
- @ClassNamePattern(simpleNamePattern = @StringPattern(endsWith = "Bar")))
+ @ClassNamePattern(unqualifiedNamePattern = @StringPattern(endsWith = "Bar")))
}),
})
public void foo() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/keepanno/MembersAnnotatedByPatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/MembersAnnotatedByPatternsTest.java
index 52850da..a024909 100644
--- a/src/test/java/com/android/tools/r8/keepanno/MembersAnnotatedByPatternsTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/MembersAnnotatedByPatternsTest.java
@@ -121,10 +121,11 @@
classConstant = OnMethods.class,
kind = KeepItemKind.CLASS_AND_METHODS,
methodAnnotatedByClassNamePattern =
- @ClassNamePattern(simpleName = "MembersAnnotatedByPatternsTest$C"),
+ @ClassNamePattern(unqualifiedName = "MembersAnnotatedByPatternsTest$C"),
constrainAnnotations =
@AnnotationPattern(
- namePattern = @ClassNamePattern(simpleName = "MembersAnnotatedByPatternsTest$C")))
+ namePattern =
+ @ClassNamePattern(unqualifiedName = "MembersAnnotatedByPatternsTest$C")))
})
public void foo(Class<?> clazz) throws Exception {
for (Field field : clazz.getDeclaredFields()) {
diff --git a/src/test/java/com/android/tools/r8/keepanno/classpatterns/ClassNamePatternsTest.java b/src/test/java/com/android/tools/r8/keepanno/classpatterns/ClassNamePatternsTest.java
index 699ddd6..d6b97d1 100644
--- a/src/test/java/com/android/tools/r8/keepanno/classpatterns/ClassNamePatternsTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/classpatterns/ClassNamePatternsTest.java
@@ -195,7 +195,7 @@
@UsesReflection({
@KeepTarget(
kind = KeepItemKind.CLASS_AND_METHODS,
- classNamePattern = @ClassNamePattern(simpleName = "B"),
+ classNamePattern = @ClassNamePattern(unqualifiedName = "B"),
methodName = "foo",
methodReturnTypeConstant = String.class)
})
@@ -216,13 +216,13 @@
kind = KeepItemKind.CLASS_AND_METHODS,
classNamePattern =
@ClassNamePattern(
- simpleName = "A",
+ unqualifiedName = "A",
packageName = "com.android.tools.r8.keepanno.classpatterns.pkg2"),
methodName = "foo",
methodReturnTypePattern =
@TypePattern(
classNamePattern =
- @ClassNamePattern(packageName = "java.lang", simpleName = "String"))))
+ @ClassNamePattern(packageName = "java.lang", unqualifiedName = "String"))))
public void foo() throws Exception {
Util.lookupClassesAndInvokeMethods();
}
@@ -240,11 +240,11 @@
kind = KeepItemKind.CLASS_AND_METHODS,
classNamePattern =
@ClassNamePattern(
- simpleName = "A",
+ unqualifiedName = "A",
packageName = "com.android.tools.r8.keepanno.classpatterns.pkg2"),
methodName = "foo",
methodReturnTypePattern =
- @TypePattern(classNamePattern = @ClassNamePattern(simpleName = "String"))))
+ @TypePattern(classNamePattern = @ClassNamePattern(unqualifiedName = "String"))))
public void foo() throws Exception {
Util.lookupClassesAndInvokeMethods();
}
diff --git a/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java b/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java
index f79c36e..5a45f17 100644
--- a/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java
@@ -25,8 +25,8 @@
import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.USED_BY_NATIVE;
import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.USED_BY_REFLECTION;
import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.USES_REFLECTION;
+import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.getUnqualifiedName;
import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.quote;
-import static com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.simpleName;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.keepanno.doctests.ForApiDocumentationTest;
@@ -116,7 +116,7 @@
}
private static String getPrefix(ClassReference annoType) {
- return "`@" + simpleName(annoType);
+ return "`@" + getUnqualifiedName(annoType);
}
private static String getSuffix() {
@@ -196,24 +196,24 @@
}
private String getMdAnnotationLink(ClassReference clazz) {
- return "[@" + simpleName(clazz) + "](" + getClassJavaDocUrl(clazz) + ")";
+ return "[@" + getUnqualifiedName(clazz) + "](" + getClassJavaDocUrl(clazz) + ")";
}
private String getMdAnnotationPropertyLink(ClassReference clazz, GroupMember method) {
String methodName = method.name;
String url = getClassJavaDocUrl(clazz) + "#" + methodName + "()";
- return "[@" + simpleName(clazz) + "." + methodName + "](" + url + ")";
+ return "[@" + getUnqualifiedName(clazz) + "." + methodName + "](" + url + ")";
}
private String getMdEnumLink(ClassReference clazz) {
- return "[" + simpleName(clazz) + "](" + getClassJavaDocUrl(clazz) + ")";
+ return "[" + getUnqualifiedName(clazz) + "](" + getClassJavaDocUrl(clazz) + ")";
}
private String getMdEnumMemberLink(EnumReference enumMember) {
ClassReference clazz = enumMember.enumClass;
String enumName = enumMember.name();
String url = getClassJavaDocUrl(clazz) + "#" + enumName;
- return "[" + simpleName(clazz) + "." + enumName + "](" + url + ")";
+ return "[" + getUnqualifiedName(clazz) + "." + enumName + "](" + url + ")";
}
private void println() {
diff --git a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
index c2a658d..b6be4dc 100644
--- a/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/KeepItemAnnotationGenerator.java
@@ -199,20 +199,20 @@
ImmutableList.of(FIELD_ACCESS_VOLATILE, FIELD_ACCESS_TRANSIENT);
private static final String DEFAULT_INVALID_STRING_PATTERN =
- "@" + simpleName(STRING_PATTERN) + "(exact = \"\")";
+ "@" + getUnqualifiedName(STRING_PATTERN) + "(exact = \"\")";
private static final String DEFAULT_INVALID_TYPE_PATTERN =
- "@" + simpleName(TYPE_PATTERN) + "(name = \"\")";
+ "@" + getUnqualifiedName(TYPE_PATTERN) + "(name = \"\")";
private static final String DEFAULT_INVALID_CLASS_NAME_PATTERN =
- "@" + simpleName(CLASS_NAME_PATTERN) + "(simpleName = \"\")";
+ "@" + getUnqualifiedName(CLASS_NAME_PATTERN) + "(unqualifiedName = \"\")";
private static final String DEFAULT_ANY_INSTANCE_OF_PATTERN =
- "@" + simpleName(INSTANCE_OF_PATTERN) + "()";
+ "@" + getUnqualifiedName(INSTANCE_OF_PATTERN) + "()";
- private static ClassReference astClass(String simpleName) {
- return classFromTypeName(AST_PKG + simpleName);
+ private static ClassReference astClass(String unqualifiedName) {
+ return classFromTypeName(AST_PKG + unqualifiedName);
}
- private static ClassReference annoClass(String simpleName) {
- return classFromTypeName(ANNO_PKG + simpleName);
+ private static ClassReference annoClass(String unqualifiedName) {
+ return classFromTypeName(ANNO_PKG + unqualifiedName);
}
private static EnumReference enumRef(ClassReference enumClass, String valueName) {
@@ -223,7 +223,7 @@
return "\"" + str + "\"";
}
- public static String simpleName(ClassReference clazz) {
+ public static String getUnqualifiedName(ClassReference clazz) {
String binaryName = clazz.getBinaryName();
return binaryName.substring(binaryName.lastIndexOf('/') + 1);
}
@@ -271,12 +271,12 @@
public GroupMember requiredValue(ClassReference type) {
assert valueDefault == null;
- return setType(simpleName(type));
+ return setType(getUnqualifiedName(type));
}
public GroupMember requiredArrayValue(ClassReference type) {
assert valueDefault == null;
- return setType(simpleName(type) + "[]");
+ return setType(getUnqualifiedName(type) + "[]");
}
public GroupMember requiredStringValue() {
@@ -289,12 +289,12 @@
}
public GroupMember defaultValue(ClassReference type, String value) {
- setType(simpleName(type));
+ setType(getUnqualifiedName(type));
return setValue(value);
}
public GroupMember defaultArrayValue(ClassReference type, String value) {
- setType(simpleName(type) + "[]");
+ setType(getUnqualifiedName(type) + "[]");
return setValue("{" + value + "}");
}
@@ -640,21 +640,23 @@
.defaultObjectClass());
}
- private Group classNamePatternSimpleNameGroup() {
- return new Group("class-simple-name")
+ private Group classNamePatternUnqualifiedNameGroup() {
+ return new Group("class-unqualified-name")
.addMember(
- new GroupMember("simpleName")
- .setDocTitle("Exact simple name of the class or interface.")
+ new GroupMember("unqualifiedName")
+ .setDocTitle("Exact and unqualified name of the class or interface.")
.addParagraph(
- "For example, the simple name of {@code com.example.MyClass} is {@code"
- + " MyClass}.")
- .addParagraph("The default matches any simple name.")
+ "For example, the unqualified name of {@code com.example.MyClass} is {@code"
+ + " MyClass}.",
+ "Note that for inner classes a `$` will appear in the unqualified name,"
+ + "such as, {@code MyClass$MyInnerClass}.")
+ .addParagraph("The default matches any unqualified name.")
.defaultEmptyString())
.addMember(
- new GroupMember("simpleNamePattern")
- .setDocTitle("Define the simple-name pattern by a string pattern.")
- .addParagraph("The default matches any simple name.")
- .setDocReturn("The string pattern of the class simple name.")
+ new GroupMember("unqualifiedNamePattern")
+ .setDocTitle("Define the unqualified class-name pattern by a string pattern.")
+ .setDocReturn("The string pattern of the unqualified class name.")
+ .addParagraph("The default matches any unqualified name.")
.defaultValue(STRING_PATTERN, DEFAULT_INVALID_STRING_PATTERN));
}
@@ -1246,7 +1248,7 @@
.printDoc(this::println);
println("@Target(ElementType.ANNOTATION_TYPE)");
println("@Retention(RetentionPolicy.CLASS)");
- println("public @interface " + simpleName(STRING_PATTERN) + " {");
+ println("public @interface " + getUnqualifiedName(STRING_PATTERN) + " {");
println();
withIndent(
() -> {
@@ -1275,7 +1277,7 @@
.printDoc(this::println);
println("@Target(ElementType.ANNOTATION_TYPE)");
println("@Retention(RetentionPolicy.CLASS)");
- println("public @interface " + simpleName(TYPE_PATTERN) + " {");
+ println("public @interface " + getUnqualifiedName(TYPE_PATTERN) + " {");
println();
withIndent(() -> typePatternGroup().generate(this));
println();
@@ -1294,18 +1296,18 @@
.printDoc(this::println);
println("@Target(ElementType.ANNOTATION_TYPE)");
println("@Retention(RetentionPolicy.CLASS)");
- println("public @interface " + simpleName(CLASS_NAME_PATTERN) + " {");
+ println("public @interface " + getUnqualifiedName(CLASS_NAME_PATTERN) + " {");
println();
withIndent(
() -> {
Group exactNameGroup = classNamePatternFullNameGroup();
- Group simpleNameGroup = classNamePatternSimpleNameGroup();
+ Group unqualifiedNameGroup = classNamePatternUnqualifiedNameGroup();
Group packageGroup = classNamePatternPackageGroup();
- exactNameGroup.addMutuallyExclusiveGroups(simpleNameGroup, packageGroup);
+ exactNameGroup.addMutuallyExclusiveGroups(unqualifiedNameGroup, packageGroup);
exactNameGroup.generate(this);
println();
- simpleNameGroup.generate(this);
+ unqualifiedNameGroup.generate(this);
println();
packageGroup.generate(this);
});
@@ -1323,7 +1325,7 @@
.printDoc(this::println);
println("@Target(ElementType.ANNOTATION_TYPE)");
println("@Retention(RetentionPolicy.CLASS)");
- println("public @interface " + simpleName(INSTANCE_OF_PATTERN) + " {");
+ println("public @interface " + getUnqualifiedName(INSTANCE_OF_PATTERN) + " {");
println();
withIndent(
() -> {
@@ -1347,7 +1349,7 @@
.printDoc(this::println);
println("@Target(ElementType.ANNOTATION_TYPE)");
println("@Retention(RetentionPolicy.CLASS)");
- println("public @interface " + simpleName(ANNOTATION_PATTERN) + " {");
+ println("public @interface " + getUnqualifiedName(ANNOTATION_PATTERN) + " {");
println();
withIndent(
() -> {
@@ -1522,10 +1524,10 @@
"Assume the item of the annotation is denoted by 'CTX' and referred to as its"
+ " context.")
.addCodeBlock(
- annoSimpleName(USES_REFLECTION)
+ annoUnqualifiedName(USES_REFLECTION)
+ "(value = targets, [additionalPreconditions = preconditions])",
"==>",
- annoSimpleName(KEEP_EDGE) + "(",
+ annoUnqualifiedName(KEEP_EDGE) + "(",
" consequences = targets,",
" preconditions = {createConditionFromContext(CTX)} + preconditions",
")",
@@ -1555,7 +1557,7 @@
"@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD,"
+ " ElementType.CONSTRUCTOR})");
println("@Retention(RetentionPolicy.CLASS)");
- println("public @interface " + simpleName(USES_REFLECTION) + " {");
+ println("public @interface " + getUnqualifiedName(USES_REFLECTION) + " {");
println();
withIndent(
() -> {
@@ -1642,12 +1644,12 @@
println("}");
}
- private static String annoSimpleName(ClassReference clazz) {
- return "@" + simpleName(clazz);
+ private static String annoUnqualifiedName(ClassReference clazz) {
+ return "@" + getUnqualifiedName(clazz);
}
private static String docLink(ClassReference clazz) {
- return "{@link " + simpleName(clazz) + "}";
+ return "{@link " + getUnqualifiedName(clazz) + "}";
}
private static String docLink(GroupMember member) {
@@ -1655,7 +1657,7 @@
}
private static String docEnumLink(EnumReference enumRef) {
- return "{@link " + simpleName(enumRef.enumClass) + "#" + enumRef.enumValue + "}";
+ return "{@link " + getUnqualifiedName(enumRef.enumClass) + "#" + enumRef.enumValue + "}";
}
private static String docEnumLinkList(EnumReference... values) {
@@ -1835,7 +1837,7 @@
withIndent(
() -> {
generateAnnotationConstants(USED_BY_NATIVE);
- println("// Content is the same as " + simpleName(USED_BY_REFLECTION) + ".");
+ println("// Content is the same as " + getUnqualifiedName(USED_BY_REFLECTION) + ".");
});
println("}");
println();
@@ -2088,7 +2090,7 @@
() -> {
generateAnnotationConstants(CLASS_NAME_PATTERN);
classNamePatternFullNameGroup().generateConstants(this);
- classNamePatternSimpleNameGroup().generateConstants(this);
+ classNamePatternUnqualifiedNameGroup().generateConstants(this);
classNamePatternPackageGroup().generateConstants(this);
});
println("}");
diff --git a/src/test/java/com/android/tools/r8/naming/IdentifierNameStringVerticallyMergedClassTest.java b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringVerticallyMergedClassTest.java
new file mode 100644
index 0000000..022d375
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/IdentifierNameStringVerticallyMergedClassTest.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.naming;
+
+import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class IdentifierNameStringVerticallyMergedClassTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addKeepClassAndMembersRulesWithAllowObfuscation(A.class)
+ .addKeepRules("-identifiernamestring class " + Main.class.getTypeName() + " { <fields>; }")
+ .addVerticallyMergedClassesInspector(
+ inspector -> inspector.assertMergedIntoSubtype(I.class).assertNoOtherClassesMerged())
+ .enableMemberValuePropagationAnnotations()
+ .setMinApi(parameters)
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A");
+ }
+
+ static class Main {
+
+ @NeverPropagateValue
+ static String s = "com.android.tools.r8.naming.IdentifierNameStringVerticallyMergedClassTest$I";
+
+ public static void main(String[] args) throws Exception {
+ String key = System.currentTimeMillis() > 0 ? s : args[0];
+ A a = (A) createInstanceMap().get(key);
+ System.out.println(a);
+ }
+
+ static Map<String, Object> createInstanceMap() {
+ Map<String, Object> map = new HashMap<>();
+ map.put(I.class.getName(), new A());
+ return map;
+ }
+ }
+
+ interface I {}
+
+ public static class A implements I {
+
+ @Override
+ public String toString() {
+ return "A";
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/regress/Regress339371242.java b/src/test/java/com/android/tools/r8/regress/Regress339371242.java
new file mode 100644
index 0000000..2201a1a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/regress/Regress339371242.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2024, 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.regress;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress339371242 extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDefaultRuntimes().withAllApiLevels().build();
+ }
+
+ public Regress339371242(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(TestClass.class, WithLibraryFinalizer.class)
+ .addLibraryClasses(LibraryClassWithFinalizer.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters)
+ .compile()
+ .inspect(
+ codeInspector -> {
+ ClassSubject clazz = codeInspector.clazz(TestClass.class);
+ assertThat(clazz, isPresent());
+ assertThat(clazz.uniqueFieldWithOriginalName("handler"), isPresent());
+ });
+ }
+
+ public static class LibraryClassWithFinalizer {
+
+ @Override
+ protected void finalize() throws Throwable {
+ super.finalize();
+ }
+ }
+
+ public static class WithLibraryFinalizer extends LibraryClassWithFinalizer {}
+
+ public static class TestClass {
+ private final WithLibraryFinalizer handler;
+
+ public static void main(String[] args) {
+ new TestClass().foo();
+ }
+
+ public TestClass() {
+ handler = new WithLibraryFinalizer();
+ }
+
+ public void foo() {
+ System.out.println("ab");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/ArrayCloneInDefaultInterfaceMethodAfterClassInliningTest.java b/src/test/java/com/android/tools/r8/resolution/ArrayCloneInDefaultInterfaceMethodAfterClassInliningTest.java
new file mode 100644
index 0000000..a81e419
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/ArrayCloneInDefaultInterfaceMethodAfterClassInliningTest.java
@@ -0,0 +1,80 @@
+// Copyright (c) 2024, 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.resolution;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+// Regression test for b/342802978 after R8 class inlining.
+@RunWith(Parameterized.class)
+public class ArrayCloneInDefaultInterfaceMethodAfterClassInliningTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestParameters.builder()
+ .withAllRuntimes()
+ .withApiLevel(apiLevelWithDefaultInterfaceMethodsSupport())
+ .build();
+ }
+
+ @Parameter public TestParameters parameters;
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(I.class, A.class, B.class, TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .apply(this::checkOutput);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .enableInliningAnnotations()
+ .setMinApi(parameters)
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassAndMembersRules(I.class)
+ .addProgramClasses(I.class, A.class, B.class, TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .apply(this::checkOutput);
+ }
+
+ private void checkOutput(SingleTestRunResult<?> r) {
+ r.assertSuccessWithOutputLines("0");
+ }
+
+ interface I {
+
+ default String[] myClone(String[] strings) {
+ return new B().myClone(strings);
+ }
+ }
+
+ static class A implements I {}
+
+ static class B {
+
+ @NeverInline
+ public String[] myClone(String[] strings) {
+ return strings.clone();
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ I i = System.nanoTime() > 0 ? new A() : null;
+ System.out.println(i.myClone(args).length);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/resolution/ArrayCloneInStaticInterfaceMethodAfterMethodInliningTest.java b/src/test/java/com/android/tools/r8/resolution/ArrayCloneInStaticInterfaceMethodAfterMethodInliningTest.java
new file mode 100644
index 0000000..edae17d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/resolution/ArrayCloneInStaticInterfaceMethodAfterMethodInliningTest.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2024, 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.resolution;
+
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+// Regression test for b/342802978, but with R8 giving rise to the issue after inlining.
+@RunWith(Parameterized.class)
+public class ArrayCloneInStaticInterfaceMethodAfterMethodInliningTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestParameters.builder()
+ .withAllRuntimes()
+ .withApiLevel(apiLevelWithDefaultInterfaceMethodsSupport())
+ .build();
+ }
+
+ @Parameter public TestParameters parameters;
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClasses(I.class, A.class, TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputLines("0");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters)
+ .addKeepMainRule(TestClass.class)
+ .addProgramClasses(I.class, A.class, TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .apply(this::checkOutput);
+ }
+
+ private void checkOutput(SingleTestRunResult<?> r) {
+ r.assertSuccessWithOutputLines("0");
+ }
+
+ interface I {
+
+ static String[] myClone(String[] strings) {
+ try {
+ return A.inlinedClone(strings);
+ } catch (RuntimeException e) {
+ // Extra code to avoid simple inlining.
+ System.out.println("Unexpected exception: " + e);
+ throw e;
+ }
+ }
+ }
+
+ static class A {
+
+ public static String[] inlinedClone(String[] strings) {
+ return strings.clone();
+ }
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ int count = 0;
+ // Repeated calls to avoid single-caller inlining of the interface method.
+ count += I.myClone(args).length;
+ count += I.myClone(args).length;
+ count += I.myClone(args).length;
+ count += I.myClone(args).length;
+ count += I.myClone(args).length;
+ System.out.println(count);
+ }
+ }
+}
diff --git a/src/test/testbase/java/com/android/tools/r8/ToolHelper.java b/src/test/testbase/java/com/android/tools/r8/ToolHelper.java
index cb49489..2061f8f 100644
--- a/src/test/testbase/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/testbase/java/com/android/tools/r8/ToolHelper.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.TestRuntime.CfRuntime;
import com.android.tools.r8.ToolHelper.DexVm.Kind;
import com.android.tools.r8.benchmarks.BenchmarkResults;
+import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.CustomConversionVersion;
import com.android.tools.r8.dex.ApplicationReader;
import com.android.tools.r8.dex.Marker.Tool;
@@ -26,6 +27,7 @@
import com.android.tools.r8.graph.AssemblyWriter;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DirectMappedDexApplication;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.Position;
@@ -1601,6 +1603,11 @@
"Unexpected configuration with identical test and test-base path" + resolveTestPath);
}
if (foundTestPath && foundBasePath) {
+ if (identicalClassesIgnoringClassFileVersion(resolveTestPath, resolveBasePath)) {
+ System.out.println(
+ "Ambiguous yet identical test file: " + resolveTestPath + " and " + resolveBasePath);
+ return resolveBasePath;
+ }
throw new RuntimeException(
"Ambiguous test file: " + resolveTestPath + " and " + resolveBasePath);
}
@@ -1617,6 +1624,27 @@
+ resolveBasePath);
}
+ private static boolean identicalClassesIgnoringClassFileVersion(
+ Path resolveBasePath, Path resolveTestPath) {
+ InternalOptions options = new InternalOptions();
+
+ try {
+ AndroidApp input1 = AndroidApp.builder().addProgramFiles(resolveBasePath).build();
+ DexApplication app1 = new ApplicationReader(input1, options, Timing.empty()).read();
+ DexProgramClass clazz1 = app1.classes().iterator().next();
+ clazz1.downgradeInitialClassFileVersion(CfVersion.V1_8);
+
+ AndroidApp input2 = AndroidApp.builder().addProgramFiles(resolveTestPath).build();
+ DexApplication app2 = new ApplicationReader(input2, options, Timing.empty()).read();
+ DexProgramClass clazz2 = app2.classes().iterator().next();
+ clazz2.downgradeInitialClassFileVersion(CfVersion.V1_8);
+
+ return clazz1.compareTo(clazz2) == 0;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
public static Collection<Path> getClassFilesForInnerClasses(Collection<Class<?>> classes)
throws IOException {
return getClassFilesForInnerClasses(computeLegacyOrGradleSpecifiedLocation(), classes);
diff --git a/src/test/testbase/java/com/android/tools/r8/benchmarks/BenchmarkResults.java b/src/test/testbase/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
index 3e52ba7..9dcfa97 100644
--- a/src/test/testbase/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
+++ b/src/test/testbase/java/com/android/tools/r8/benchmarks/BenchmarkResults.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.benchmarks;
import com.android.tools.r8.utils.StringUtils;
+import java.io.PrintStream;
public interface BenchmarkResults {
// Append a runtime result. This may be summed or averaged depending on the benchmark set up.
@@ -19,7 +20,9 @@
// This will throw if called on a benchmark without sub-benchmarks.
BenchmarkResults getSubResults(String name);
- void printResults(ResultMode resultMode);
+ void printResults(ResultMode resultMode, boolean failOnCodeSizeDifferences);
+
+ void writeResults(PrintStream out);
static String prettyTime(long nanoTime) {
return "" + (nanoTime / 1000000) + " ms";
diff --git a/tools/dex2oat.py b/tools/dex2oat.py
index 8df8f86..58b2418 100755
--- a/tools/dex2oat.py
+++ b/tools/dex2oat.py
@@ -152,9 +152,10 @@
append_dex2oat_verbose_flags(options, cmd)
utils.PrintCmd(cmd)
subprocess.check_call(cmd)
- cmd = adb_cmd(serial, 'logcat', '-d', '-s', 'dex2oat')
+ cmd = adb_cmd(serial, 'logcat', '-d', '-s', 'dex2oat', 'dex2oat_original')
utils.PrintCmd(cmd)
subprocess.check_call(cmd)
+
return 0
diff --git a/tools/historic_run.py b/tools/historic_run.py
index 37c3dae..9822648 100755
--- a/tools/historic_run.py
+++ b/tools/historic_run.py
@@ -34,6 +34,10 @@
result.add_option('--output',
default='build',
help='Directory where to output results')
+ result.add_option('--timeout',
+ default=1000,
+ help='Timeout in seconds (-1 for no timeout)',
+ type=int)
return result.parse_args(argv)
@@ -52,16 +56,30 @@
def __repr__(self):
return self.__str__()
+ def hash(self):
+ return self.git_hash
+
+ def title(self):
+ result = subprocess.check_output(
+ ['git', 'show-branch', '--no-name', self.git_hash]).decode('utf-8')
+ return result.strip()
+
+ def author_name(self):
+ result = subprocess.check_output([
+ 'git', 'show', '--no-notes', '--no-patch', '--pretty=%an',
+ self.git_hash
+ ]).decode('utf-8')
+ return result.strip()
+
+ def committer_timestamp(self):
+ return self.timestamp
+
def git_commit_from_hash(hash):
- commit_timestamp = subprocess.check_output([
- 'git',
- 'show',
- '--no-patch',
- '--no-notes',
- '--pretty=\'%ct\'',
- hash
- ]).decode().strip().strip('\'')
+ commit_timestamp_str = subprocess.check_output(
+ ['git', 'show', '--no-patch', '--no-notes', '--pretty=%ct',
+ hash]).decode('utf-8').strip()
+ commit_timestamp = int(commit_timestamp_str)
destination_dir = '%s/%s/' % (MASTER_COMMITS, hash)
destination = '%s%s' % (destination_dir, 'r8.jar')
commit = GitCommit(hash, destination_dir, destination, commit_timestamp)
@@ -70,9 +88,12 @@
def enumerate_git_commits(top, bottom):
if bottom is None:
- output = subprocess.check_output(['git', 'rev-list', '--first-parent', '-n', 1000, top])
+ output = subprocess.check_output(
+ ['git', 'rev-list', '--first-parent', '-n', 1000, top])
else:
- output = subprocess.check_output(['git', 'rev-list', '--first-parent', '%s^..%s' % (bottom, top)])
+ output = subprocess.check_output(
+ ['git', 'rev-list', '--first-parent',
+ '%s^..%s' % (bottom, top)])
commits = []
for c in output.decode().splitlines():
commit_hash = c.strip()
@@ -81,8 +102,8 @@
def get_available_commits(commits):
- cloud_commits = subprocess.check_output(
- ['gsutil.py', 'ls', MASTER_COMMITS]).decode().splitlines()
+ cloud_commits = subprocess.check_output(['gsutil.py', 'ls', MASTER_COMMITS
+ ]).decode().splitlines()
available_commits = []
for commit in commits:
if commit.destination_dir in cloud_commits:
@@ -129,7 +150,7 @@
count = 0
for index in commit_permutations:
count += 1
- print('Running commit %s out of %s' % (count, len(commits)))
+ print('\nRunning commit %s out of %s' % (count, len(commits)))
commit = commits[index]
if not utils.cloud_storage_exists(commit.destination):
# We may have a directory, but no r8.jar
@@ -164,11 +185,13 @@
def run_cmd(options, commit):
- cmd = [options.cmd, commit.git_hash]
+ cmd = options.cmd.split(' ')
+ cmd.append(commit.git_hash)
output_path = options.output or 'build'
time_commit = '%s_%s' % (commit.timestamp, commit.git_hash)
time_commit_path = os.path.join(output_path, time_commit)
print(' '.join(cmd))
+ status = None
if not options.dry_run:
if not os.path.exists(time_commit_path):
os.makedirs(time_commit_path)
@@ -177,15 +200,20 @@
with open(stdout_path, 'w') as stdout:
with open(stderr_path, 'w') as stderr:
process = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
- timeout = 1000
- while process.poll() is None and timeout > 0:
+ timeout = options.timeout
+ while process.poll() is None and timeout != 0:
time.sleep(1)
timeout -= 1
if process.poll() is None:
process.kill()
print("Task timed out")
stderr.write("timeout\n")
- print('Wrote outputs to: %s' % time_commit_path)
+ status = 'TIMED OUT'
+ else:
+ returncode = process.returncode
+ status = 'SUCCESS' if returncode == 0 else f'FAILED ({returncode})'
+ print(f'Wrote outputs to: {time_commit_path}')
+ print(status)
def main(argv):
diff --git a/tools/perf.py b/tools/perf.py
index 3762c5d..0186435 100755
--- a/tools/perf.py
+++ b/tools/perf.py
@@ -4,14 +4,24 @@
# BSD-style license that can be found in the LICENSE file.
import argparse
+import compiledump
+import json
import os
+import shutil
import subprocess
import sys
+import upload_benchmark_data_to_google_storage
import utils
-DEFAULT_ITERATIONS = 10
-BUCKET = "r8-test-results"
+BUCKET = "r8-perf-results"
+SAMPLE_BENCHMARK_RESULT_JSON = {
+ 'benchmark_name': '<benchmark_name>',
+ 'results': [{
+ 'code_size': 0,
+ 'runtime': 0
+ }]
+}
# Result structure on cloud storage
# gs://bucket/benchmark_results/APP/TARGET/GIT_HASH/results
@@ -19,68 +29,174 @@
# where results simply contains the result lines and
# meta contains information about the execution (machine)
+
def ParseOptions():
result = argparse.ArgumentParser()
result.add_argument('--app',
- help='Specific app to measure.',
- default='NowInAndroidApp')
+ help='Specific app(s) to measure.',
+ action='append')
+ result.add_argument('--iterations',
+ help='How many times run_benchmark is run.',
+ type=int,
+ default=1)
+ result.add_argument('--iterations-inner',
+ help='How many iterations to run inside run_benchmark.',
+ type=int,
+ default=10)
+ result.add_argument('--outdir',
+ help='Output directory for running locally.')
+ result.add_argument('--skip-if-output-exists',
+ help='Skip if output exists.',
+ action='store_true',
+ default=False)
result.add_argument('--target',
help='Specific target to run on.',
default='r8-full',
choices=['d8', 'r8-full', 'r8-force', 'r8-compat'])
- result.add_argument('--iterations',
- help='How many iterations to run.',
- type=int,
- default=DEFAULT_ITERATIONS)
- return result.parse_known_args()
+ result.add_argument('--verbose',
+ help='To enable verbose logging.',
+ action='store_true',
+ default=False)
+ result.add_argument('--version',
+ '-v',
+ help='Use R8 hash for the run (default local build)',
+ default=None)
+ options, args = result.parse_known_args()
+ options.apps = options.app or ['NowInAndroidApp', 'TiviApp']
+ options.quiet = not options.verbose
+ del options.app
+ return options, args
-def ParseOutput(output, options, log_array):
- for line in output.decode('utf-8').splitlines():
- print(" -- " + line)
- # Output lines look like:
- # Benchmark results for NowInAndroidApp on target r8-full
- # warmup reporting mode: average
- # warmup iterations: 1
- # warmup total time: 58580 ms
- # benchmark reporting mode: average
- # benchmark iterations: 1
- # benchmark total time: 39613 ms
- # NowInAndroidApp(RunTimeRaw): 39154 ms
- # NowInAndroidApp(CodeSize): 5102196
- if line.startswith(options.app + "("):
- log_array.append(line)
+def MergeBenchmarkResultJsonFiles(benchmark_result_json_files):
+ merged_benchmark_result_json = None
+ for benchmark_result_json_file in benchmark_result_json_files:
+ benchmark_result_json = ParseBenchmarkResultJsonFile(
+ benchmark_result_json_file)
+ if merged_benchmark_result_json is None:
+ merged_benchmark_result_json = benchmark_result_json
+ else:
+ MergeBenchmarkResultJsonFile(merged_benchmark_result_json,
+ benchmark_result_json)
+ return merged_benchmark_result_json
-def GetGSLocation(app, target, filename):
- return "gs://%s/%s/%s/%s/%s" % (BUCKET, app, target,
- utils.get_HEAD_sha1(), filename)
+def MergeBenchmarkResultJsonFile(merged_benchmark_result_json,
+ benchmark_result_json):
+ assert benchmark_result_json.keys() == SAMPLE_BENCHMARK_RESULT_JSON.keys()
+ assert merged_benchmark_result_json[
+ 'benchmark_name'] == benchmark_result_json['benchmark_name']
+ merged_benchmark_result_json['results'].extend(
+ benchmark_result_json['results'])
+
+def ParseBenchmarkResultJsonFile(result_json_file):
+ with open(result_json_file, 'r') as f:
+ lines = f.readlines()
+ return json.loads(''.join(lines))
+
+
+def GetArtifactLocation(app, target, version, filename):
+ version_or_head = version or utils.get_HEAD_sha1()
+ return f'{app}/{target}/{version_or_head}/{filename}'
+
+
+def GetGSLocation(filename, bucket=BUCKET):
+ return f'gs://{bucket}/{filename}'
+
+
+def ArchiveOutputFile(file, dest, bucket=BUCKET, header=None, outdir=None):
+ if outdir:
+ dest_in_outdir = os.path.join(outdir, dest)
+ os.makedirs(os.path.dirname(dest_in_outdir), exist_ok=True)
+ shutil.copyfile(file, dest_in_outdir)
+ else:
+ utils.upload_file_to_cloud_storage(file,
+ GetGSLocation(dest, bucket=bucket),
+ header=header)
+
+
+# Usage with historic_run.py:
+# ./tools/historic_run.py
+# --cmd "perf.py --skip-if-output-exists --version"
+# --timeout -1
+# --top 3373fd18453835bf49bff9f02523a507a2ebf317
+# --bottom 7486f01e0622cb5935b77a92b59ddf1ca8dbd2e2
def main():
- (options, args) = ParseOptions()
- cmd = ['tools/run_benchmark.py', '--target', options.target,
- '--benchmark', options.app]
- log_array = []
- # Build and warmup
- output = subprocess.check_output(cmd)
- ParseOutput(output, options, log_array)
- cmd.append('--no-build')
- for i in range(options.iterations):
- output = subprocess.check_output(cmd)
- ParseOutput(output, options, log_array)
+ options, args = ParseOptions()
with utils.TempDir() as temp:
- result_file = os.path.join(temp, "result_file")
- with open(result_file, 'w') as f:
- for l in log_array:
- f.write(l + "\n")
- utils.upload_file_to_cloud_storage(result_file,
- GetGSLocation(options.app, options.target, 'results'))
- if os.environ.get('SWARMING_BOT_ID'):
- meta_file = os.path.join(temp, "meta")
- with open(meta_file, 'w') as f:
- f.write("Produced by: " + os.environ.get('SWARMING_BOT_ID'))
- utils.upload_file_to_cloud_storage(meta_file,
- GetGSLocation(options.app, options.target, 'meta'))
+ if options.version:
+ # Download r8.jar once instead of once per run_benchmark.py invocation.
+ download_options = argparse.Namespace(no_build=True, nolib=True)
+ r8jar = compiledump.download_distribution(options.version,
+ download_options, temp)
+
+ for app in options.apps:
+ if options.skip_if_output_exists:
+ if options.outdir:
+ raise NotImplementedError
+ output = GetGSLocation(
+ GetArtifactLocation(app, options.target, options.version,
+ 'result.json'))
+ if utils.cloud_storage_exists(output):
+ print(f'Skipping run, {output} already exists.')
+ continue
+
+ base_cmd = [
+ 'tools/run_benchmark.py', '--benchmark', app, '--target',
+ options.target
+ ]
+ if options.verbose:
+ base_cmd.append('--verbose')
+ if options.version:
+ base_cmd.extend([
+ '--version', options.version, '--version-jar', r8jar,
+ '--nolib'
+ ])
+
+ # Build
+ utils.Print(f'Preparing {app}', quiet=options.quiet)
+ build_cmd = base_cmd + ['--iterations', '0']
+ subprocess.check_output(build_cmd)
+
+ # Run benchmark.
+ benchmark_result_json_files = []
+ for i in range(options.iterations):
+ utils.Print(f'Benchmarking {app} ({i+1}/{options.iterations})',
+ quiet=options.quiet)
+ benchhmark_result_file = os.path.join(temp, f'result_file_{i}')
+ iteration_cmd = base_cmd + [
+ '--iterations',
+ str(options.iterations_inner), '--output',
+ benchhmark_result_file, '--no-build'
+ ]
+ subprocess.check_output(iteration_cmd)
+ benchmark_result_json_files.append(benchhmark_result_file)
+
+ # Merge results and write output.
+ result_file = os.path.join(temp, 'result_file')
+ with open(result_file, 'w') as f:
+ json.dump(
+ MergeBenchmarkResultJsonFiles(benchmark_result_json_files),
+ f)
+ ArchiveOutputFile(result_file,
+ GetArtifactLocation(app, options.target,
+ options.version,
+ 'result.json'),
+ outdir=options.outdir)
+
+ # Write metadata.
+ if utils.is_bot():
+ meta_file = os.path.join(temp, "meta")
+ with open(meta_file, 'w') as f:
+ f.write("Produced by: " + os.environ.get('SWARMING_BOT_ID'))
+ ArchiveOutputFile(meta_file,
+ GetArtifactLocation(app, options.target,
+ options.version, 'meta'),
+ outdir=options.outdir)
+
+ if utils.is_bot():
+ upload_benchmark_data_to_google_storage.run()
if __name__ == '__main__':
diff --git a/tools/perf/benchmark_data.json b/tools/perf/benchmark_data.json
new file mode 100644
index 0000000..9d18466
--- /dev/null
+++ b/tools/perf/benchmark_data.json
@@ -0,0 +1,24 @@
+[{
+ "author": "Christoffer Adamsen",
+ "hash": "e6a33325a4a4bac599ffce1f75cab8505a35d02a",
+ "submitted": "Thu Jun 06 13:07:44 2024 +0200",
+ "title": "Allow compiling CompileDumpCompatR8 in isolation",
+ "benchmarks": {
+ "NowInAndroidApp": {
+ "benchmark_name": "NowInAndroidApp",
+ "results": [
+ { "code_size": 42, "runtime": 1 },
+ { "code_size": 42, "runtime": 2 },
+ { "code_size": 42, "runtime": 3 }
+ ]
+ },
+ "TiviApp": {
+ "benchmark_name": "TiviApp",
+ "results": [
+ { "code_size": 84, "runtime": 4 },
+ { "code_size": 84, "runtime": 5 },
+ { "code_size": 84, "runtime": 6 }
+ ]
+ }
+ }
+}]
\ No newline at end of file
diff --git a/tools/perf/index.html b/tools/perf/index.html
new file mode 100644
index 0000000..8d3089c
--- /dev/null
+++ b/tools/perf/index.html
@@ -0,0 +1,337 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>R8 perf</title>
+</head>
+<body>
+ <select id="benchmark-selector"></select>
+ <div>
+ <canvas id="myChart"></canvas>
+ </div>
+ <div>
+ <div style="float: left; width: 50%">
+ <button type="button" id="show-more-left" disabled>⇐</button>
+ <button type="button" id="show-less-left">⇒</button>
+ </div>
+ <div style="float: left; text-align: right; width: 50%">
+ <button type="button" id="show-less-right">⇐</button>
+ <button type="button" id="show-more-right" disabled>⇒</button>
+ </div>
+ </div>
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js"></script>
+ <script type="module">
+ // Utility methods.
+ Array.prototype.any = function(predicate) {
+ for (const element of this.values()) {
+ if (predicate(element)) {
+ return true;
+ }
+ }
+ return false;
+ };
+ Array.prototype.first = function() {
+ return this[0];
+ };
+ Array.prototype.avg = function() {
+ return this.reduce(function(x, y) { return x + y; }, 0) / this.length;
+ };
+ Array.prototype.min = function() {
+ return this.reduce(function(x, y) { return x === null ? y : Math.min(x, y); }, null);
+ };
+ Array.prototype.reverseInPlace = function() {
+ for (var i = 0; i < Math.floor(this.length / 2); i++) {
+ var temp = this[i];
+ this[i] = this[this.length - i - 1];
+ this[this.length - i - 1] = temp;
+ }
+ };
+ Number.prototype.ns_to_s = function() {
+ const seconds = this/10E8;
+ const seconds_with_one_decimal = Math.round(seconds*10)/10;
+ return seconds;
+ };
+
+ // Import and reverse commits so that newest are last.
+ import commits from "./benchmark_data.json" with { type: "json" };
+ commits.reverseInPlace()
+
+ // Amend the commits with their unique index.
+ for (var i = 0; i < commits.length; i++) {
+ commits[i].index = i;
+ }
+
+ // DOM references.
+ const benchmarkSelector = document.getElementById('benchmark-selector')
+ const canvas = document.getElementById('myChart');
+ const showMoreLeft = document.getElementById('show-more-left');
+ const showLessLeft = document.getElementById('show-less-left');
+ const showLessRight = document.getElementById('show-less-right');
+ const showMoreRight = document.getElementById('show-more-right');
+
+ // Initialize benchmark selector.
+ const benchmarks = new Set();
+ for (const commit of commits.values()) {
+ for (const benchmark in commit.benchmarks) {
+ benchmarks.add(benchmark)
+ }
+ }
+ var selectedBenchmark = window.location.hash.substring(1)
+ if (!benchmarks.has(selectedBenchmark)) {
+ selectedBenchmark = benchmarks.values().next().value;
+ }
+ for (const benchmark of benchmarks.values()) {
+ const opt = document.createElement('option');
+ opt.value = benchmark;
+ opt.innerHTML = benchmark;
+ benchmarkSelector.appendChild(opt);
+ if (benchmark == selectedBenchmark) {
+ benchmarkSelector.selectedIndex = benchmarkSelector.options.length - 1
+ }
+ }
+ benchmarkSelector.onchange = function(event) {
+ selectedBenchmark =
+ benchmarkSelector.options[benchmarkSelector.selectedIndex].value;
+ updateChart();
+ window.location.hash = selectedBenchmark;
+ };
+
+ // Chart data provider.
+ function getData(start = 0, end = commits.length) {
+ const filteredCommits =
+ commits
+ .slice(start, end);
+ //.filter(
+ // commit =>
+ // selectedBenchmark in commit.benchmarks
+ // && commit.benchmarks[selectedBenchmark].results.length > 0);
+ const labels = filteredCommits.map((c, i) => i);
+ const codeSizeData =
+ filteredCommits.map(
+ (c, i) =>
+ selectedBenchmark in filteredCommits[i].benchmarks
+ ? filteredCommits[i]
+ .benchmarks[selectedBenchmark]
+ .results
+ .first()
+ .code_size
+ : NaN);
+ const codeSizeScatterData = [];
+ for (const commit of filteredCommits.values()) {
+ if (!(selectedBenchmark in commit.benchmarks)) {
+ continue;
+ }
+ const codeSizes =
+ commit.benchmarks[selectedBenchmark].results.map(result => result.code_size)
+ const expectedCodeSize = codeSizes.first();
+ if (codeSizes.any(codeSize => codeSize != expectedCodeSize)) {
+ const seen = new Set();
+ seen.add(expectedCodeSize);
+ for (const codeSize of codeSizes.values()) {
+ if (!seen.has(codeSize)) {
+ codeSizeScatterData.push({ x: commit.index, y: codeSize });
+ seen.add(codeSize);
+ }
+ }
+ }
+ }
+ const runtimeData =
+ filteredCommits.map(
+ (c, i) =>
+ selectedBenchmark in filteredCommits[i].benchmarks
+ ? filteredCommits[i]
+ .benchmarks[selectedBenchmark]
+ .results
+ .map(result => result.runtime)
+ .min()
+ .ns_to_s()
+ : NaN);
+ const runtimeScatterData = [];
+ for (const commit of filteredCommits.values()) {
+ if (!(selectedBenchmark in commit.benchmarks)) {
+ continue;
+ }
+ const runtimes =
+ commit.benchmarks[selectedBenchmark].results.map(result => result.runtime)
+ for (const runtime of runtimes.values()) {
+ runtimeScatterData.push({ x: commit.index, y: runtime.ns_to_s() });
+ }
+ }
+
+ const skipped = (ctx, value) => ctx.p0.skip || ctx.p1.skip ? value : undefined;
+ return {
+ labels: labels,
+ datasets: [
+ {
+ type: 'line',
+ label: 'Code size',
+ data: codeSizeData,
+ tension: 0.1,
+ segment: {
+ borderColor: ctx =>
+ skipped(ctx, 'rgba(54, 162, 235, 0.5)'),
+ borderDash: ctx => skipped(ctx, [6, 6]),
+ },
+ spanGaps: true
+ },
+ {
+ type: 'scatter',
+ label: 'Nondeterminism',
+ data: codeSizeScatterData,
+ pointBackgroundColor: 'red'
+ },
+ {
+ type: 'line',
+ label: 'Runtime',
+ data: runtimeData,
+ tension: 0.1,
+ yAxisID: 'y2',
+ segment: {
+ borderColor: ctx =>
+ skipped(ctx, 'rgba(255, 160, 64, 0.5)'),
+ borderDash: ctx => skipped(ctx, [6, 6]),
+ },
+ spanGaps: true
+ },
+ {
+ type: 'scatter',
+ label: 'Runtime variance',
+ data: runtimeScatterData,
+ yAxisID: 'y2'
+ }
+ ],
+ };
+ }
+
+ // Chart options.
+ const options = {
+ onHover: (event, chartElement) =>
+ event.native.target.style.cursor =
+ chartElement[0] ? 'pointer' : 'default',
+ plugins: {
+ tooltip: {
+ callbacks: {
+ title: (context) => {
+ const elementInfo = context[0];
+ var commit;
+ if (elementInfo.dataset.type == 'line') {
+ commit = commits[elementInfo.dataIndex];
+ } else {
+ console.assert(elementInfo.dataset.type == 'scatter');
+ commit = commits[elementInfo.raw.x];
+ }
+ return commit.title;
+ },
+ footer: (context) => {
+ const elementInfo = context[0];
+ var commit;
+ if (elementInfo.dataset.type == 'line') {
+ commit = commits[elementInfo.dataIndex];
+ } else {
+ console.assert(elementInfo.dataset.type == 'scatter');
+ commit = commits[elementInfo.raw.x];
+ }
+ return `Author: ${commit.author}\n`
+ + `Submitted: ${new Date(commit.submitted * 1000).toLocaleString()}\n`
+ + `Hash: ${commit.hash}`;
+ }
+ }
+ }
+ },
+ responsive: true,
+ scales: {
+ x: {},
+ y: {
+ position: 'left',
+ title: {
+ display: true,
+ text: 'Code size (bytes)'
+ }
+ },
+ y2: {
+ position: 'right',
+ title: {
+ display: true,
+ text: 'Runtime (seconds)'
+ }
+ }
+ }
+ };
+
+ // Create chart.
+ const myChart = new Chart(canvas, {
+ data: getData(),
+ options: options
+ });
+
+ // Setup click handler.
+ canvas.onclick = function (event) {
+ const points =
+ myChart.getElementsAtEventForMode(
+ event, 'nearest', { intersect: true }, true);
+ if (points.length > 0) {
+ const point = points[0];
+ const commit = commits[point.index];
+ window.open('https://r8.googlesource.com/r8/+/' + commit.hash, '_blank');
+ }
+ };
+
+ // Setup chart navigation.
+ var left = 0;
+ var right = commits.length;
+
+ showMoreLeft.onclick = function (event) {
+ if (left == 0) {
+ return;
+ }
+ const currentSize = right - left;
+ left = left - currentSize;
+ if (left < 0) {
+ left = 0;
+ }
+ updateChart();
+ };
+
+ showLessLeft.onclick = function (event) {
+ const currentSize = right - left;
+ left = left + Math.floor(currentSize / 2);
+ if (left >= right) {
+ left = right - 1;
+ }
+ updateChart();
+ };
+
+ showLessRight.onclick = function (event) {
+ if (right == 0) {
+ return;
+ }
+ const currentSize = right - left;
+ right = right - Math.floor(currentSize / 2);
+ if (right < left) {
+ right = left;
+ }
+ updateChart();
+ };
+
+ showMoreRight.onclick = function (event) {
+ const currentSize = right - left;
+ right = right + currentSize;
+ if (right > commits.length) {
+ right = commits.length;
+ }
+ updateChart();
+ };
+
+ function updateChart() {
+ console.assert(left <= right);
+ const newData = getData(left, right);
+ Object.assign(myChart.data, newData);
+ myChart.update();
+ showMoreLeft.disabled = left == 0;
+ showLessLeft.disabled = left == right - 1;
+ showLessRight.disabled = left == right - 1;
+ showMoreRight.disabled = right == commits.length;
+ }
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/tools/run_benchmark.py b/tools/run_benchmark.py
index 3a1efbf..6bf0882 100755
--- a/tools/run_benchmark.py
+++ b/tools/run_benchmark.py
@@ -63,6 +63,12 @@
help='Enable assertions when running',
default=False,
action='store_true')
+ result.add_argument('--iterations',
+ '-i',
+ help='Number of iterations to run',
+ type=int)
+ result.add_argument('--output',
+ help='Output path where to write the result')
result.add_argument('--print-times',
help='Print timing information from r8',
default=False,
@@ -72,17 +78,35 @@
'-v',
help='Use R8 version/hash for the run (default local build)',
default=None)
+ result.add_argument(
+ '--version-jar',
+ help='The r8.jar corresponding to the version given at --version.',
+ default=None)
result.add_argument('--temp',
help='A directory to use for temporaries and outputs.',
default=None)
- return result.parse_known_args(argv)
+ result.add_argument('--verbose',
+ help='To enable verbose logging.',
+ action='store_true',
+ default=False)
+ options, args = result.parse_known_args(argv)
+ options.quiet = not options.verbose
+ # We must download the non-lib distribution when running with a specific
+ # version, since BenchmarkMainEntryRunner is using R8 internals.
+ # TODO(b/346477461): Look into removing this limitation.
+ assert options.version is None or options.nolib
+ return options, args
def main(argv, temp):
- (options, args) = parse_options(argv)
+ options, args = parse_options(argv)
+
+ if options.output:
+ options.output = os.path.abspath(options.output)
if options.temp:
temp = options.temp
+ os.makedirs(temp, exist_ok=True)
if options.golem:
options.no_build = True
@@ -92,11 +116,14 @@
if options.nolib:
testBuildTargets = [
- utils.GRADLE_TASK_TEST_JAR, utils.GRADLE_TASK_TEST_DEPS_JAR
+ utils.GRADLE_TASK_TEST_JAR, utils.GRADLE_TASK_TEST_DEPS_JAR,
+ utils.GRADLE_TASK_TEST_UNZIP_TESTBASE
]
buildTargets = [utils.GRADLE_TASK_R8] + testBuildTargets
r8jar = utils.R8_JAR
- testjars = [utils.R8_TESTS_JAR, utils.R8_TESTS_DEPS_JAR, utils.R8_TESTBASE_JAR]
+ testjars = [
+ utils.R8_TESTS_JAR, utils.R8_TESTS_DEPS_JAR, utils.R8_TESTBASE_JAR
+ ]
else:
testBuildTargets = GOLEM_BUILD_TARGETS_TESTS
buildTargets = GOLEM_BUILD_TARGETS
@@ -107,11 +134,11 @@
os.path.join(utils.R8LIB_TESTBASE_JAR)
]
- if options.version:
+ if options.version or options.version_jar:
# r8 is downloaded so only test jar needs to be built.
buildTargets = testBuildTargets
- r8jar = compiledump.download_distribution(options.version,
- options.nolib, temp)
+ r8jar = options.version_jar or compiledump.download_distribution(
+ options.version, options, temp)
if not options.no_build:
gradle.RunGradle(buildTargets + ['-Pno_internal'])
@@ -127,7 +154,11 @@
def run(options, r8jar, testjars):
jdkhome = get_jdk_home(options, options.benchmark)
- cmd = [jdk.GetJavaExecutable(jdkhome)]
+ cmd = [
+ jdk.GetJavaExecutable(jdkhome), '-Xms8g', '-Xmx8g',
+ '-XX:+TieredCompilation', '-XX:TieredStopAtLevel=4',
+ '-DBENCHMARK_IGNORE_CODE_SIZE_DIFFERENCES'
+ ]
if options.enable_assertions:
cmd.append('-ea')
if options.print_times:
@@ -137,6 +168,12 @@
f'-DTEST_DATA_LOCATION={utils.REPO_ROOT}/d8_r8/test_modules/tests_java_8/build/classes/java/test',
f'-DTESTBASE_DATA_LOCATION={utils.REPO_ROOT}/d8_r8/test_modules/testbase/build/classes/java/main',
])
+ if options.iterations is not None:
+ if options.iterations == 0:
+ return
+ cmd.append(f'-DBENCHMARK_ITERATIONS={options.iterations}')
+ if options.output:
+ cmd.append(f'-DBENCHMARK_OUTPUT={options.output}')
cmd.extend(['-cp', ':'.join([r8jar] + testjars)])
cmd.extend([
'com.android.tools.r8.benchmarks.BenchmarkMainEntryRunner',
@@ -146,6 +183,7 @@
# repository root as an argument. The runner can then setup dependencies.
'golem' if options.golem else utils.REPO_ROOT,
])
+ utils.PrintCmd(cmd, quiet=options.quiet)
return subprocess.check_call(cmd)
diff --git a/tools/upload_benchmark_data_to_google_storage.py b/tools/upload_benchmark_data_to_google_storage.py
new file mode 100755
index 0000000..4fb9444
--- /dev/null
+++ b/tools/upload_benchmark_data_to_google_storage.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+# Copyright (c) 2024, 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.
+
+import historic_run
+import json
+import os
+import perf
+import time
+import utils
+
+import sys
+
+APPS = ['NowInAndroidApp', 'TiviApp']
+TARGETS = ['r8-full']
+NUM_COMMITS = 250
+
+INDEX_HTML = os.path.join(utils.TOOLS_DIR, 'perf/index.html')
+
+
+def DownloadCloudBucket(dest):
+ os.makedirs(dest)
+ utils.download_file_from_cloud_storage(perf.GetGSLocation('*'),
+ dest,
+ concurrent=True,
+ flags=['-R'])
+
+
+def ParseJsonFromCloudStorage(filename, local_bucket):
+ abs_path = os.path.join(local_bucket, filename)
+ if not os.path.exists(abs_path):
+ return None
+ with open(abs_path, 'r') as f:
+ lines = f.readlines()
+ content = ''.join(lines)
+ try:
+ return json.loads(content)
+ except:
+ return None
+
+
+def run():
+ # Get the N most recent commits sorted by newest first.
+ top = utils.get_sha1_from_revision('origin/main')
+ bottom = utils.get_nth_sha1_from_revision(NUM_COMMITS - 1, 'origin/main')
+ commits = historic_run.enumerate_git_commits(top, bottom)
+ assert len(commits) == NUM_COMMITS
+
+ # Download all benchmark data from the cloud bucket to a temp folder.
+ with utils.TempDir() as temp:
+ local_bucket = os.path.join(temp, perf.BUCKET)
+ DownloadCloudBucket(local_bucket)
+
+ # Aggregate all the result.json files into a single benchmark_data.json file
+ # that has the same format as tools/perf/benchmark_data.json.
+ benchmark_data = []
+ for commit in commits:
+ benchmarks = {}
+ for app in APPS:
+ for target in TARGETS:
+ filename = perf.GetArtifactLocation(app, target,
+ commit.hash(),
+ 'result.json')
+ app_benchmark_data = ParseJsonFromCloudStorage(
+ filename, local_bucket)
+ if app_benchmark_data:
+ benchmarks[app] = app_benchmark_data
+ if benchmarks or benchmark_data:
+ benchmark_data.append({
+ 'author': commit.author_name(),
+ 'hash': commit.hash(),
+ 'submitted': commit.committer_timestamp(),
+ 'title': commit.title(),
+ 'benchmarks': benchmarks
+ })
+
+ # Trim data.
+ new_benchmark_data_len = len(benchmark_data)
+ while new_benchmark_data_len > 0:
+ candidate_len = new_benchmark_data_len - 1
+ if not benchmark_data[candidate_len]['benchmarks']:
+ new_benchmark_data_len = candidate_len
+ else:
+ break
+ benchmark_data = benchmark_data[0:new_benchmark_data_len]
+
+ # Serialize JSON to temp file.
+ benchmark_data_file = os.path.join(temp, 'benchmark_data.json')
+ with open(benchmark_data_file, 'w') as f:
+ json.dump(benchmark_data, f)
+
+ # Write output files to public bucket.
+ perf.ArchiveOutputFile(benchmark_data_file,
+ 'perf/benchmark_data.json',
+ header='Cache-Control:no-store')
+ perf.ArchiveOutputFile(INDEX_HTML, 'perf/index.html')
+
+
+def main():
+ run()
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/tools/utils.py b/tools/utils.py
index 6faecc8..2b77ed5 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -56,6 +56,7 @@
GRADLE_TASK_TESTBASE_WITH_APPLY_MAPPING_JAR = ':test:rewriteTestBaseForR8LibWithRelocatedDeps'
GRADLE_TASK_TEST_DEPS_JAR = ':test:packageTestDeps'
GRADLE_TASK_TEST_JAR = ':test:relocateTestsForR8LibWithRelocatedDeps'
+GRADLE_TASK_TEST_UNZIP_TESTBASE = ':test:unzipTestBase'
R8 = 'r8'
R8LIB = 'r8lib'
@@ -67,8 +68,10 @@
R8_SRC_JAR = os.path.join(LIBS, 'r8-src.jar')
R8LIB_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8lib-exclude-deps.jar')
R8_FULL_EXCLUDE_DEPS_JAR = os.path.join(LIBS, 'r8-full-exclude-deps.jar')
-THREADING_MODULE_BLOCKING_JAR = os.path.join(LIBS, 'threading-module-blocking.jar')
-THREADING_MODULE_SINGLE_THREADED_JAR = os.path.join(LIBS, 'threading-module-single-threaded.jar')
+THREADING_MODULE_BLOCKING_JAR = os.path.join(LIBS,
+ 'threading-module-blocking.jar')
+THREADING_MODULE_SINGLE_THREADED_JAR = os.path.join(
+ LIBS, 'threading-module-single-threaded.jar')
R8_TESTS_JAR = os.path.join(LIBS, 'r8tests.jar')
R8_TESTBASE_JAR = os.path.join(LIBS, 'r8test_base.jar')
R8LIB_TESTBASE_JAR = os.path.join(LIBS, 'r8libtestbase-cf.jar')
@@ -81,7 +84,8 @@
LIBRARY_DESUGAR_CONVERSIONS_ZIP = os.path.join(
CUSTOM_CONVERSION_DIR, 'library_desugar_conversions.jar')
KEEPANNO_ANNOTATIONS_JAR = os.path.join(LIBS, 'keepanno-annotations.jar')
-KEEPANNO_ANNOTATIONS_DOC = os.path.join('d8_r8', 'keepanno', 'build', 'docs', 'javadoc')
+KEEPANNO_ANNOTATIONS_DOC = os.path.join('d8_r8', 'keepanno', 'build', 'docs',
+ 'javadoc')
DESUGAR_CONFIGURATION = os.path.join('src', 'library_desugar',
'desugar_jdk_libs.json')
@@ -346,6 +350,14 @@
subprocess.check_output(cmd)
+def get_nth_sha1_from_revision(n, revision):
+ result = subprocess.check_output([
+ 'git', 'log', revision, f'--skip={n}', '--max-count=1',
+ '--pretty=format:%H'
+ ]).decode('utf-8')
+ return result.strip()
+
+
def get_sha1(filename):
sha1 = hashlib.sha1()
with open(filename, 'rb') as f:
@@ -357,6 +369,13 @@
return sha1.hexdigest()
+def get_sha1_from_revision(revision):
+ cmd = ['git', 'rev-parse', revision]
+ PrintCmd(cmd)
+ with ChangedWorkingDirectory(REPO_ROOT):
+ return subprocess.check_output(cmd).decode('utf-8').strip()
+
+
def get_HEAD_branch():
result = subprocess.check_output(
['git', 'rev-parse', '--abbrev-ref', 'HEAD']).decode('utf-8')
@@ -364,20 +383,13 @@
def get_HEAD_sha1():
- return get_HEAD_sha1_for_checkout(REPO_ROOT)
+ return get_sha1_from_revision('HEAD')
def get_HEAD_diff_stat():
return subprocess.check_output(['git', 'diff', '--stat']).decode('utf-8')
-def get_HEAD_sha1_for_checkout(checkout):
- cmd = ['git', 'rev-parse', 'HEAD']
- PrintCmd(cmd)
- with ChangedWorkingDirectory(checkout):
- return subprocess.check_output(cmd).decode('utf-8').strip()
-
-
def makedirs_if_needed(path):
try:
os.makedirs(path)
@@ -390,12 +402,15 @@
return 'gsutil.py' if os.name != 'nt' else 'gsutil.py.bat'
-def upload_file_to_cloud_storage(source, destination):
- cmd = [get_gsutil(), 'cp']
- cmd += [source, destination]
+def upload_file_to_cloud_storage(source, destination, header=None):
+ cmd = [get_gsutil()]
+ if header:
+ cmd.extend(['-h', header])
+ cmd.extend(['cp', source, destination])
PrintCmd(cmd)
subprocess.check_call(cmd)
+
def check_dir_args(source, destination):
# We require that the dirname of the paths coincide, e.g., src/dirname and dst/dirname
# The target is then stripped so the upload command will be: cp -R src/dirname dst/
@@ -406,10 +421,11 @@
f'{source} and {destination}')
if len(destination_parent.strip()) == 0:
raise Exception(
- 'Attempt to upload directory to empty destination directory: '
- + destination)
+ 'Attempt to upload directory to empty destination directory: ' +
+ destination)
return destination_parent
+
def upload_directory_to_cloud_storage(source, destination, parallel=True):
destination_parent = check_dir_args(source, destination)
cmd = [get_gsutil()]
@@ -420,6 +436,7 @@
PrintCmd(cmd)
subprocess.check_call(cmd)
+
def rsync_directory_to_cloud_storage(source, destination, parallel=True):
check_dir_args(source, destination)
cmd = [get_gsutil()]
@@ -430,6 +447,7 @@
PrintCmd(cmd)
subprocess.check_call(cmd)
+
def delete_file_from_cloud_storage(destination):
cmd = [get_gsutil(), 'rm', destination]
PrintCmd(cmd)
@@ -460,8 +478,18 @@
return subprocess.call(cmd) == 0
-def download_file_from_cloud_storage(source, destination, quiet=False):
- cmd = [get_gsutil(), 'cp', source, destination]
+def download_file_from_cloud_storage(source,
+ destination,
+ concurrent=False,
+ flags=None,
+ quiet=False):
+ cmd = [get_gsutil()]
+ if concurrent:
+ cmd.append('-m')
+ cmd.append('cp')
+ if flags:
+ cmd.extend(flags)
+ cmd.extend([source, destination])
PrintCmd(cmd, quiet=quiet)
subprocess.check_call(cmd)