Merge commit '00173804646ce1c429d934373d61d7c033a802f9' into dev-release
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 87fd416..b916f47 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -111,6 +111,7 @@
def CheckChange(input_api, output_api):
branch = (
check_output(['git', 'cl', 'upstream'])
+ .decode('utf-8')
.strip()
.replace('refs/heads/', ''))
results = []
diff --git a/build.gradle b/build.gradle
index ea3f498..5df3bb4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -33,7 +33,6 @@
espressoVersion = '3.0.0'
fastutilVersion = '7.2.1'
guavaVersion = '31.1-jre'
- joptSimpleVersion = '4.6'
gsonVersion = '2.7'
junitVersion = '4.13-beta-2'
mockitoVersion = '2.10.0'
@@ -141,9 +140,9 @@
srcDirs = ['src/test/examplesJava17']
}
}
- examplesJava18 {
+ examplesJava20 {
java {
- srcDirs = ['src/test/examplesJava18']
+ srcDirs = ['src/test/examplesJava20']
}
}
examplesTestNGRunner {
@@ -214,7 +213,6 @@
}
dependencies {
- implementation "net.sf.jopt-simple:jopt-simple:$joptSimpleVersion"
implementation "com.google.code.gson:gson:$gsonVersion"
// Include all of guava when compiling the code, but exclude annotations that we don't
// need from the packaging.
@@ -233,7 +231,6 @@
implementation group: 'org.ow2.asm', name: 'asm-analysis', version: asmVersion
implementation group: 'org.ow2.asm', name: 'asm-util', version: asmVersion
- main17Implementation "net.sf.jopt-simple:jopt-simple:$joptSimpleVersion"
main17Implementation "com.google.code.gson:gson:$gsonVersion"
// Include all of guava when compiling the code, but exclude annotations that we don't
// need from the packaging.
@@ -405,20 +402,20 @@
"openjdk/jdk8/linux-x86",
"openjdk/jdk-11/linux",
"openjdk/jdk-17/linux",
- "openjdk/jdk-18/linux"],
+ "openjdk/jdk-20/linux"],
],
osx: [
"third_party": ["openjdk/openjdk-9.0.4/osx",
"openjdk/jdk8/darwin-x86",
"openjdk/jdk-11/osx",
"openjdk/jdk-17/osx",
- "openjdk/jdk-18/osx"],
+ "openjdk/jdk-20/osx"],
],
windows: [
"third_party": ["openjdk/openjdk-9.0.4/windows",
"openjdk/jdk-11/windows",
"openjdk/jdk-17/windows",
- "openjdk/jdk-18/windows"],
+ "openjdk/jdk-20/windows"],
],
]
@@ -487,16 +484,6 @@
}
}
-task downloadProguard {
- cloudDependencies.each { entry ->
- entry.value.each { entryFile ->
- if (entryFile.contains("proguard")) {
- dependsOn "${getDownloadDepsTaskName(entry.key, entryFile)}"
- }
- }
- }
-}
-
task downloadOpenJDKrt {
cloudDependencies.each { entry ->
entry.value.each { entryFile ->
@@ -652,8 +639,8 @@
JavaVersion.VERSION_17,
false)
setJdkCompilationWithCompatibility(
- sourceSets.examplesJava18.compileJavaTaskName,
- 'jdk-18',
+ sourceSets.examplesJava20.compileJavaTaskName,
+ 'jdk-20',
// TODO(b/218293990): Update Gradle to get JavaVersion.VERSION_18.
JavaVersion.VERSION_17,
false)
@@ -876,8 +863,6 @@
"--map",
"com.google.thirdparty->com.android.tools.r8.com.google.thirdparty",
"--map",
- "joptsimple->com.android.tools.r8.joptsimple",
- "--map",
"org.objectweb.asm->com.android.tools.r8.org.objectweb.asm",
"--map",
"it.unimi.dsi.fastutil->com.android.tools.r8.it.unimi.dsi.fastutil",
@@ -1342,14 +1327,7 @@
task buildExampleJars {
- dependsOn downloadProguard
def examplesDir = file("src/test/examples")
- def proguardScript
- if (OperatingSystem.current().isWindows()) {
- proguardScript = "third_party/proguard/proguard5.2.1/bin/proguard.bat"
- } else {
- proguardScript = "third_party/proguard/proguard5.2.1/bin/proguard.sh"
- }
task extractExamplesRuntime(type: Sync) {
dependsOn configurations.examplesRuntime
from { configurations.examplesRuntime.collect { zipTree(it) } }
@@ -1402,76 +1380,29 @@
dependsOn "jar_example_${name}_debuginfo_none"
dependsOn "extractExamplesRuntime"
def runtimeDependencies = copySpec { }
- // The "throwing" test verifies debugging/stack info on the post-proguarded output.
- def proguardConfigPath = "${dir}/proguard.cfg"
- if (new File(proguardConfigPath).exists()) {
- task "pre_proguard_example_${name}"(type: Jar, dependsOn: "compile_examples") {
- archiveName = "${name}_pre_proguard.jar"
- destinationDir = exampleOutputDir
- from "build/test/examples/classes"
- include name + "/**/*.class"
- with runtimeDependencies
- includeEmptyDirs false
- }
- def jarPath = files(tasks.getByPath("pre_proguard_example_${name}")).files.first();
- def proguardJarPath = "${exampleOutputDir}/${jarName}"
- def proguardMapPath = "${exampleOutputDir}/${name}/${name}.map"
- task "jar_example_${name}"(type: Exec, dependsOn: "pre_proguard_example_${name}") {
- inputs.files files(
- tasks.getByPath("pre_proguard_example_${name}"),
- proguardConfigPath)
- // Enable these to get stdout and stderr redirected to files...
- // standardOutput = new FileOutputStream('proguard.stdout')
- // errorOutput = new FileOutputStream('proguard.stderr')
- def proguardArguments = "-verbose -dontwarn java.** -injars ${jarPath}" +
- " -outjars ${proguardJarPath}" +
- " -include ${proguardConfigPath}" +
- " -printmapping ${proguardMapPath}"
- if (OperatingSystem.current().isWindows()) {
- executable "${proguardScript}"
- args "${proguardArguments}"
- } else {
- executable "bash"
- args "-c", "${proguardScript} '${proguardArguments}'"
- }
- outputs.file proguardJarPath
- }
- // TODO: Consider performing distinct proguard compilations.
- task "jar_example_${name}_debuginfo_all"(type: Copy, dependsOn: "jar_example_${name}") {
- from "${exampleOutputDir}/${name}.jar"
- into "${exampleOutputDir}"
- rename(".*", "${name}_debuginfo_all.jar")
- }
- task "jar_example_${name}_debuginfo_none"(type: Copy, dependsOn: "jar_example_${name}") {
- from "${exampleOutputDir}/${name}.jar"
- into "${exampleOutputDir}"
- rename(".*", "${name}_debuginfo_none.jar")
- }
- } else {
- task "jar_example_${name}"(type: Jar, dependsOn: "compile_examples") {
- archiveName = "${name}.jar"
- destinationDir = exampleOutputDir
- from "build/test/examples/classes"
- include name + "/**/*"
- with runtimeDependencies
- includeEmptyDirs true
- }
- task "jar_example_${name}_debuginfo_all"(type: Jar, dependsOn: "compile_examples_debuginfo_all") {
- archiveName = "${name}_debuginfo_all.jar"
- destinationDir = exampleOutputDir
- from "build/test/examples/classes_debuginfo_all"
- include name + "/**/*.class"
- with runtimeDependencies
- includeEmptyDirs false
- }
- task "jar_example_${name}_debuginfo_none"(type: Jar, dependsOn: "compile_examples_debuginfo_none") {
- archiveName = "${name}_debuginfo_none.jar"
- destinationDir = exampleOutputDir
- from "build/test/examples/classes_debuginfo_none"
- include name + "/**/*.class"
- with runtimeDependencies
- includeEmptyDirs false
- }
+ task "jar_example_${name}"(type: Jar, dependsOn: "compile_examples") {
+ archiveName = "${name}.jar"
+ destinationDir = exampleOutputDir
+ from "build/test/examples/classes"
+ include name + "/**/*"
+ with runtimeDependencies
+ includeEmptyDirs true
+ }
+ task "jar_example_${name}_debuginfo_all"(type: Jar, dependsOn: "compile_examples_debuginfo_all") {
+ archiveName = "${name}_debuginfo_all.jar"
+ destinationDir = exampleOutputDir
+ from "build/test/examples/classes_debuginfo_all"
+ include name + "/**/*.class"
+ with runtimeDependencies
+ includeEmptyDirs false
+ }
+ task "jar_example_${name}_debuginfo_none"(type: Jar, dependsOn: "compile_examples_debuginfo_none") {
+ archiveName = "${name}_debuginfo_none.jar"
+ destinationDir = exampleOutputDir
+ from "build/test/examples/classes_debuginfo_none"
+ include name + "/**/*.class"
+ with runtimeDependencies
+ includeEmptyDirs false
}
}
}
@@ -1631,7 +1562,7 @@
buildExampleJarsCreateTask("Java10", sourceSets.examplesJava10)
buildExampleJarsCreateTask("Java11", sourceSets.examplesJava11)
buildExampleJarsCreateTask("Java17", sourceSets.examplesJava17)
-buildExampleJarsCreateTask("Java18", sourceSets.examplesJava18)
+buildExampleJarsCreateTask("Java20", sourceSets.examplesJava20)
task provideArtFrameworksDependencies {
cloudDependencies.tools.forEach({ art ->
@@ -1693,7 +1624,7 @@
dependsOn buildExampleJava10Jars
dependsOn buildExampleJava11Jars
dependsOn buildExampleJava17Jars
- dependsOn buildExampleJava18Jars
+ dependsOn buildExampleJava20Jars
dependsOn buildExampleAndroidApi
def examplesDir = file("src/test/examples")
examplesDir.eachDir { dir ->
diff --git a/scripts/add-openjdk.sh b/scripts/add-openjdk.sh
index e8714f3..4aac626 100755
--- a/scripts/add-openjdk.sh
+++ b/scripts/add-openjdk.sh
@@ -18,7 +18,7 @@
# Now run script with fingers crossed!
-JDK_VERSION="18"
+JDK_VERSION="20.0.1"
JDK_VERSION_FULL=${JDK_VERSION}
# For ea versions the full version name has a postfix.
# JDK_VERSION_FULL="${JDK_VERSION}-ea+33"
diff --git a/src/main/java/com/android/tools/r8/BaseCommand.java b/src/main/java/com/android/tools/r8/BaseCommand.java
index 893bc64..dd6a740 100644
--- a/src/main/java/com/android/tools/r8/BaseCommand.java
+++ b/src/main/java/com/android/tools/r8/BaseCommand.java
@@ -89,7 +89,7 @@
}
}
- private static class LibraryInputOrigin extends InputFileOrigin {
+ static class LibraryInputOrigin extends InputFileOrigin {
public LibraryInputOrigin(Path file) {
super("library input", file);
diff --git a/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java b/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
index 2efea81..e3a2cbb 100644
--- a/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
+++ b/src/main/java/com/android/tools/r8/GlobalSyntheticsGenerator.java
@@ -3,13 +3,65 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
+
+import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.androidapi.AndroidApiLevelCompute;
+import com.android.tools.r8.androidapi.AndroidApiLevelDatabaseHelper;
+import com.android.tools.r8.androidapi.AndroidApiUnknownReferenceDiagnosticHelper;
+import com.android.tools.r8.androidapi.ApiReferenceStubber;
+import com.android.tools.r8.androidapi.ApiReferenceStubberEventConsumer;
+import com.android.tools.r8.androidapi.ComputedApiLevel.KnownApiLevel;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.ApplicationWriter;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.graph.DexAnnotationSet;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeList;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.EnclosingMethodAttribute;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature;
+import com.android.tools.r8.graph.MethodCollection.MethodCollectionFactory;
+import com.android.tools.r8.graph.NestHostClassAttribute;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.graph.ThrowExceptionCode;
+import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.desugar.TypeRewriter;
+import com.android.tools.r8.ir.desugar.records.RecordDesugaring;
+import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaring;
+import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaringEventConsumer;
+import com.android.tools.r8.naming.RecordRewritingNamingLens;
+import com.android.tools.r8.naming.VarHandleDesugaringRewritingNamingLens;
import com.android.tools.r8.origin.CommandLineOrigin;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.shaking.MainDexInfo;
+import com.android.tools.r8.synthesis.SyntheticFinalization;
+import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
+import com.android.tools.r8.synthesis.SyntheticNaming;
+import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
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.SelfRetraceTest;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
/**
@@ -18,6 +70,18 @@
*/
public class GlobalSyntheticsGenerator {
+ private static boolean ensureAllGlobalSyntheticsModeled(SyntheticNaming naming) {
+ for (SyntheticKind kind : naming.kinds()) {
+ assert !kind.isGlobal()
+ || !kind.isMayOverridesNonProgramType()
+ || kind == naming.RECORD_TAG
+ || kind == naming.API_MODEL_STUB
+ || kind == naming.METHOD_HANDLES_LOOKUP
+ || kind == naming.VAR_HANDLE;
+ }
+ return true;
+ }
+
/**
* Main API entry for the global synthetics generator.
*
@@ -48,16 +112,205 @@
private static void run(AndroidApp app, InternalOptions options, ExecutorService executorService)
throws CompilationFailedException {
try {
- ExceptionUtils.withD8CompilationHandler(
+ ExceptionUtils.withCompilationHandler(
options.reporter,
() -> {
- throw new RuntimeException("Implement GlobalSyntheticsGenerator");
+ Timing timing = Timing.create("GlobalSyntheticsGenerator " + Version.LABEL, options);
+ try {
+ timing.begin("Read input app");
+ AppView<AppInfo> appView = readApp(app, options, executorService, timing);
+ timing.end();
+
+ timing.begin("Create global synthetics");
+ createGlobalSynthetics(appView, timing, executorService);
+ timing.end();
+
+ ApplicationWriter.create(appView, options.getMarker()).write(executorService, app);
+ } catch (ExecutionException e) {
+ throw unwrapExecutionException(e);
+ } catch (IOException e) {
+ throw new CompilationError(e.getMessage(), e);
+ } finally {
+ options.signalFinishedToConsumers();
+ // Dump timings.
+ if (options.printTimes) {
+ timing.report();
+ }
+ }
});
} finally {
executorService.shutdown();
}
}
+ private static AppView<AppInfo> readApp(
+ AndroidApp inputApp, InternalOptions options, ExecutorService executor, Timing timing)
+ throws IOException {
+ timing.begin("Application read");
+ ApplicationReader applicationReader = new ApplicationReader(inputApp, options, timing);
+ DirectMappedDexApplication app = applicationReader.read(executor).toDirect();
+ timing.end();
+ TypeRewriter typeRewriter = options.getTypeRewriter();
+ AppInfo appInfo =
+ timing.time(
+ "Create app-info",
+ () ->
+ AppInfo.createInitialAppInfo(
+ app, GlobalSyntheticsStrategy.forSingleOutputMode(), MainDexInfo.none()));
+ // Now that the dex-application is fully loaded, close any internal archive providers.
+ inputApp.closeInternalArchiveProviders();
+ return timing.time("Create app-view", () -> AppView.createForD8(appInfo, typeRewriter, timing));
+ }
+
+ private static void createGlobalSynthetics(
+ AppView<AppInfo> appView, Timing timing, ExecutorService executorService)
+ throws ExecutionException, IOException {
+ assert ensureAllGlobalSyntheticsModeled(appView.getSyntheticItems().getNaming());
+ Set<DexProgramClass> synthesizingContext =
+ ImmutableSet.of(createSynthesizingContext(appView.dexItemFactory()));
+
+ List<ProgramMethod> methodsToProcess = new ArrayList<>();
+ // Add global synthetic class for records.
+ RecordDesugaring.ensureRecordClassHelper(
+ appView,
+ synthesizingContext,
+ recordTagClass -> recordTagClass.programMethods().forEach(methodsToProcess::add));
+
+ VarHandleDesugaringEventConsumer varHandleEventConsumer =
+ new VarHandleDesugaringEventConsumer() {
+ @Override
+ public void acceptVarHandleDesugaringClass(DexProgramClass clazz) {
+ clazz.programMethods().forEach(methodsToProcess::add);
+ }
+
+ @Override
+ public void acceptVarHandleDesugaringClassContext(
+ DexProgramClass clazz, ProgramDefinition context) {}
+ };
+
+ // Add global synthetic class for var handles.
+ VarHandleDesugaring.ensureVarHandleClass(appView, varHandleEventConsumer, synthesizingContext);
+
+ // Add global synthetic class for method handles lookup.
+ VarHandleDesugaring.ensureMethodHandlesLookupClass(
+ appView, varHandleEventConsumer, synthesizingContext);
+
+ IRConverter converter = new IRConverter(appView);
+ converter.processSimpleSynthesizeMethods(methodsToProcess, executorService);
+
+ appView
+ .withoutClassHierarchy()
+ .setAppInfo(
+ new AppInfo(
+ appView.appInfo().getSyntheticItems().commit(appView.app()),
+ appView.appInfo().getMainDexInfo()));
+
+ timing.time(
+ "Finalize synthetics",
+ () -> SyntheticFinalization.finalize(appView, timing, executorService));
+
+ appView.setNamingLens(RecordRewritingNamingLens.createRecordRewritingNamingLens(appView));
+ appView.setNamingLens(
+ VarHandleDesugaringRewritingNamingLens.createVarHandleDesugaringRewritingNamingLens(
+ appView));
+
+ // Add global synthetic classes for api stubs.
+ createAllApiStubs(appView, synthesizingContext, executorService);
+
+ appView
+ .withoutClassHierarchy()
+ .setAppInfo(
+ new AppInfo(
+ appView.appInfo().getSyntheticItems().commit(appView.app()),
+ appView.appInfo().getMainDexInfo()));
+ }
+
+ private static DexProgramClass createSynthesizingContext(DexItemFactory factory) {
+ return new DexProgramClass(
+ factory.createType("Lcom/android/tools/r8/GlobalSynthetics$$SynthesizingContext;"),
+ Kind.CF,
+ Origin.unknown(),
+ ClassAccessFlags.fromCfAccessFlags(1057),
+ factory.objectType,
+ DexTypeList.empty(),
+ factory.createString("GlobalSynthetics$$SynthesizingContext.java"),
+ NestHostClassAttribute.none(),
+ Collections.emptyList(),
+ Collections.emptyList(),
+ Collections.emptyList(),
+ EnclosingMethodAttribute.none(),
+ Collections.emptyList(),
+ ClassSignature.noSignature(),
+ DexAnnotationSet.empty(),
+ DexEncodedField.EMPTY_ARRAY,
+ DexEncodedField.EMPTY_ARRAY,
+ MethodCollectionFactory.empty(),
+ factory.getSkipNameValidationForTesting(),
+ DexProgramClass::invalidChecksumRequest);
+ }
+
+ private static void createAllApiStubs(
+ AppView<?> appView, Set<DexProgramClass> synthesizingContext, ExecutorService executorService)
+ throws ExecutionException {
+ AndroidApiLevelCompute apiLevelCompute = appView.apiLevelCompute();
+
+ Set<String> notModeledTypes = AndroidApiLevelDatabaseHelper.notModeledTypes();
+
+ DexItemFactory factory = appView.dexItemFactory();
+ ThrowExceptionCode throwExceptionCode =
+ ThrowExceptionCode.create(appView.dexItemFactory().noClassDefFoundErrorType);
+ ApiReferenceStubberEventConsumer apiReferenceStubberEventConsumer =
+ ApiReferenceStubberEventConsumer.empty();
+ ThreadUtils.processItems(
+ appView.app().asDirect().libraryClasses(),
+ libraryClass -> {
+ if (notModeledTypes.contains(libraryClass.getClassReference().getTypeName())) {
+ return;
+ }
+ if (ApiReferenceStubber.isJavaType(libraryClass.getType(), factory)) {
+ return;
+ }
+ KnownApiLevel knownApiLevel =
+ apiLevelCompute
+ .computeApiLevelForLibraryReference(libraryClass.getReference())
+ .asKnownApiLevel();
+ if (knownApiLevel == null) {
+ appView
+ .reporter()
+ .warning(
+ AndroidApiUnknownReferenceDiagnosticHelper.createInternal(
+ libraryClass.getReference()));
+ return;
+ }
+ if (knownApiLevel.getApiLevel().isLessThanOrEqualTo(appView.options().getMinApiLevel())) {
+ return;
+ }
+ if (libraryClass.isFinal() && !isExceptionType(appView, libraryClass)) {
+ return;
+ }
+ ApiReferenceStubber.mockMissingLibraryClass(
+ appView,
+ ignored -> synthesizingContext,
+ libraryClass,
+ throwExceptionCode,
+ apiReferenceStubberEventConsumer);
+ },
+ executorService);
+ }
+
+ private static boolean isExceptionType(AppView<?> appView, DexLibraryClass libraryClass) {
+ DexType throwableType = appView.dexItemFactory().throwableType;
+ DexType currentType = libraryClass.getType();
+ while (currentType != null) {
+ if (currentType == throwableType) {
+ return true;
+ }
+ DexClass superClass = appView.appInfo().definitionForWithoutExistenceAssert(currentType);
+ currentType = superClass == null ? null : superClass.getSuperType();
+ }
+ return false;
+ }
+
private static void run(String[] args) throws CompilationFailedException {
GlobalSyntheticsGeneratorCommand command =
GlobalSyntheticsGeneratorCommand.parse(args, CommandLineOrigin.INSTANCE).build();
@@ -67,7 +320,7 @@
return;
}
if (command.isPrintVersion()) {
- System.out.println("GlobalSynthetics " + Version.getVersionString());
+ System.out.println("GlobalSyntheticsGenerator " + Version.getVersionString());
return;
}
run(command);
@@ -77,7 +330,7 @@
* Command-line entry to GlobalSynthetics.
*
* <p>See {@link GlobalSyntheticsGeneratorCommandParser#getUsageMessage()} or run {@code
- * globalsynthetics --help} for usage information.
+ * globalsyntheticsgenerator --help} for usage information.
*/
public static void main(String[] args) {
if (args.length == 0) {
diff --git a/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommand.java b/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommand.java
index 2187b67..4ae00bd 100644
--- a/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommand.java
+++ b/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommand.java
@@ -3,53 +3,74 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import com.android.tools.r8.BaseCommand.LibraryInputOrigin;
+import com.android.tools.r8.dex.Marker.Tool;
+import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.DexFileOverflowDiagnostic;
-import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AbortException;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.InternalOptions.DesugarState;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import java.nio.file.Path;
+import java.util.Arrays;
import java.util.Collection;
/**
* Immutable command structure for an invocation of the {@link GlobalSyntheticsGenerator} compiler.
*/
-public final class GlobalSyntheticsGeneratorCommand extends BaseCommand {
+public final class GlobalSyntheticsGeneratorCommand {
private final ProgramConsumer programConsumer;
- private final StringConsumer classNameConsumer;
private final Reporter reporter;
private final int minApiLevel;
+ private final boolean printHelp;
+ private final boolean printVersion;
+
+ private final AndroidApp inputApp;
+
private final DexItemFactory factory = new DexItemFactory();
private GlobalSyntheticsGeneratorCommand(
- AndroidApp androidApp,
- ProgramConsumer programConsumer,
- StringConsumer ClassNameConsumer,
- Reporter reporter,
- int minApiLevel) {
- super(androidApp);
+ AndroidApp inputApp, ProgramConsumer programConsumer, Reporter reporter, int minApiLevel) {
+ this.inputApp = inputApp;
this.programConsumer = programConsumer;
- this.classNameConsumer = ClassNameConsumer;
this.minApiLevel = minApiLevel;
this.reporter = reporter;
+ this.printHelp = false;
+ this.printVersion = false;
}
private GlobalSyntheticsGeneratorCommand(boolean printHelp, boolean printVersion) {
- super(printHelp, printVersion);
+ this.printHelp = printHelp;
+ this.printVersion = printVersion;
+
+ this.inputApp = null;
this.programConsumer = null;
- this.classNameConsumer = null;
this.minApiLevel = AndroidApiLevel.B.getLevel();
reporter = new Reporter();
}
+ public AndroidApp getInputApp() {
+ return inputApp;
+ }
+
+ public boolean isPrintHelp() {
+ return printHelp;
+ }
+
+ public boolean isPrintVersion() {
+ return printVersion;
+ }
+
/**
* Parse the GlobalSyntheticsGenerator command-line.
*
@@ -102,7 +123,6 @@
return new Builder(diagnosticsHandler);
}
- @Override
InternalOptions getInternalOptions() {
InternalOptions internal = new InternalOptions(factory, reporter);
assert !internal.debug;
@@ -117,6 +137,12 @@
assert !internal.isMinifying();
assert !internal.passthroughDexCode;
+ internal.tool = Tool.GlobalSyntheticsGenerator;
+ internal.desugarState = DesugarState.ON;
+ internal.enableVarHandleDesugaring = true;
+
+ internal.getArtProfileOptions().setEnableCompletenessCheckForTesting(false);
+
return internal;
}
@@ -125,13 +151,14 @@
*
* <p>A builder is obtained by calling {@link GlobalSyntheticsGeneratorCommand#builder}.
*/
- public static class Builder
- extends BaseCommand.Builder<GlobalSyntheticsGeneratorCommand, Builder> {
+ public static class Builder {
private ProgramConsumer programConsumer = null;
- private StringConsumer globalSyntheticClassesListConsumer = null;
- private Reporter reporter;
+ private final Reporter reporter;
private int minApiLevel = AndroidApiLevel.B.getLevel();
+ private boolean printHelp = false;
+ private boolean printVersion = false;
+ private final AndroidApp.Builder appBuilder = AndroidApp.builder();
private Builder() {
this(new DefaultR8DiagnosticsHandler());
@@ -141,55 +168,46 @@
this.reporter = new Reporter(diagnosticsHandler);
}
- @Override
- Builder self() {
+ /** Set the min api level. */
+ public Builder setMinApiLevel(int minApiLevel) {
+ this.minApiLevel = minApiLevel;
return this;
}
- public Builder setReporter(Reporter reporter) {
- this.reporter = reporter;
- return self();
+ /** Set the value of the print-help flag. */
+ public Builder setPrintHelp(boolean printHelp) {
+ this.printHelp = printHelp;
+ return this;
}
- public Builder setMinApiLevel(int minApiLevel) {
- this.minApiLevel = minApiLevel;
- return self();
+ /** Set the value of the print-version flag. */
+ public Builder setPrintVersion(boolean printVersion) {
+ this.printVersion = printVersion;
+ return this;
}
- @Override
- void validate() {
- if (isPrintHelp() || isPrintVersion()) {
- return;
- }
- if (!(programConsumer instanceof DexIndexedConsumer)) {
- reporter.error("G8 does not support compiling to dex per class or class files");
- }
+ /** Add library file resources. */
+ public Builder addLibraryFiles(Path... files) {
+ addLibraryFiles(Arrays.asList(files));
+ return this;
}
- @Override
- public GlobalSyntheticsGeneratorCommand makeCommand() {
- if (isPrintHelp() || isPrintVersion()) {
- return new GlobalSyntheticsGeneratorCommand(isPrintHelp(), isPrintVersion());
- }
- validate();
- return new GlobalSyntheticsGeneratorCommand(
- getAppBuilder().build(),
- programConsumer,
- globalSyntheticClassesListConsumer,
- reporter,
- minApiLevel);
+ /** Add library file resources. */
+ public Builder addLibraryFiles(Collection<Path> files) {
+ guard(
+ () -> {
+ for (Path path : files) {
+ try {
+ appBuilder.addLibraryFile(path);
+ } catch (CompilationError e) {
+ error(new LibraryInputOrigin(path), e);
+ }
+ }
+ });
+ return this;
}
- public Builder setGlobalSyntheticClassesListOutput(Path path) {
- return setGlobalSyntheticClassesListConsumer(new StringConsumer.FileConsumer(path));
- }
-
- public Builder setGlobalSyntheticClassesListConsumer(
- StringConsumer globalSyntheticClassesListOutput) {
- this.globalSyntheticClassesListConsumer = globalSyntheticClassesListOutput;
- return self();
- }
-
+ /** Set an output path to consume the resulting program. */
public Builder setProgramConsumerOutput(Path path) {
return setProgramConsumer(
FileUtils.isArchive(path)
@@ -197,64 +215,54 @@
: new DexIndexedConsumer.DirectoryConsumer(path, false));
}
+ /** Set a consumer for obtaining the resulting program. */
public Builder setProgramConsumer(ProgramConsumer programConsumer) {
this.programConsumer = programConsumer;
- return self();
+ return this;
}
- @Override
- public Builder addProgramFiles(Collection<Path> files) {
- throw new Unreachable("Should not be used for global synthetics generation");
+ public GlobalSyntheticsGeneratorCommand build() {
+ validate();
+ if (isPrintHelpOrPrintVersion()) {
+ return new GlobalSyntheticsGeneratorCommand(printHelp, printVersion);
+ }
+ return new GlobalSyntheticsGeneratorCommand(
+ appBuilder.build(), programConsumer, reporter, minApiLevel);
}
- @Override
- public Builder addProgramResourceProvider(ProgramResourceProvider programProvider) {
- throw new Unreachable("Should not be used for global synthetics generation");
+ private boolean isPrintHelpOrPrintVersion() {
+ return printHelp || printVersion;
}
- @Override
- public Builder addClasspathFiles(Path... files) {
- throw new Unreachable("Should not be used for global synthetics generation");
+ private void validate() {
+ if (isPrintHelpOrPrintVersion()) {
+ return;
+ }
+ if (!(programConsumer instanceof DexIndexedConsumer)) {
+ reporter.error(
+ "GlobalSyntheticsGenerator does not support compiling to dex per class or class files");
+ }
}
- @Override
- public Builder addClasspathFiles(Collection<Path> files) {
- throw new Unreachable("Should not be used for global synthetics generation");
+ // Helper to guard and handle exceptions.
+ private void guard(Runnable action) {
+ try {
+ action.run();
+ } catch (CompilationError e) {
+ reporter.error(e.toStringDiagnostic());
+ } catch (AbortException e) {
+ // Error was reported and exception will be thrown by build.
+ }
}
- @Override
- public Builder addClasspathResourceProvider(ClassFileResourceProvider provider) {
- throw new Unreachable("Should not be used for global synthetics generation");
+ /** Signal an error. */
+ public void error(Diagnostic diagnostic) {
+ reporter.error(diagnostic);
}
- @Override
- public Builder addClassProgramData(byte[] data, Origin origin) {
- throw new Unreachable("Should not be used for global synthetics generation");
- }
-
- @Override
- Builder addDexProgramData(byte[] data, Origin origin) {
- throw new Unreachable("Should not be used for global synthetics generation");
- }
-
- @Override
- public Builder addMainDexListFiles(Path... files) {
- throw new Unreachable("Should not be used for global synthetics generation");
- }
-
- @Override
- public Builder addMainDexListFiles(Collection<Path> files) {
- throw new Unreachable("Should not be used for global synthetics generation");
- }
-
- @Override
- public Builder addMainDexClasses(String... classes) {
- throw new Unreachable("Should not be used for global synthetics generation");
- }
-
- @Override
- public Builder addMainDexClasses(Collection<String> classes) {
- throw new Unreachable("Should not be used for global synthetics generation");
+ // Helper to signify an error.
+ public void error(Origin origin, Throwable throwable) {
+ reporter.error(new ExceptionDiagnostic(throwable, origin));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommandParser.java b/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommandParser.java
index 36e5bbd..9022e28 100644
--- a/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommandParser.java
+++ b/src/main/java/com/android/tools/r8/GlobalSyntheticsGeneratorCommandParser.java
@@ -19,7 +19,7 @@
public class GlobalSyntheticsGeneratorCommandParser {
- private static final String LOWER_CASE_NAME = "globalsynthetics";
+ private static final String LOWER_CASE_NAME = "globalsyntheticsgenerator";
private static final String MIN_API_FLAG = "--min-api";
private static final String USAGE_MESSAGE =
@@ -30,9 +30,6 @@
.add(ParseFlagInfoImpl.getMinApi())
.add(ParseFlagInfoImpl.getLib())
.add(ParseFlagInfoImpl.flag1("--output", "<dex-file>", "Output result in <dex-file>."))
- .add(
- ParseFlagInfoImpl.flag1(
- "--classes-list-output", "<file>", "Output list of generated classes in <file>"))
.add(ParseFlagInfoImpl.getVersion(LOWER_CASE_NAME))
.add(ParseFlagInfoImpl.getHelp())
.build();
@@ -46,7 +43,7 @@
}
private static final Set<String> OPTIONS_WITH_ONE_PARAMETER =
- ImmutableSet.of("--output", "--lib", MIN_API_FLAG, "---classes-list-output");
+ ImmutableSet.of("--output", "--lib", MIN_API_FLAG);
public static GlobalSyntheticsGeneratorCommand.Builder parse(String[] args, Origin origin) {
return new GlobalSyntheticsGeneratorCommandParser()
@@ -101,8 +98,6 @@
}
} else if (arg.equals("--lib")) {
builder.addLibraryFiles(Paths.get(nextArg));
- } else if (arg.equals("--classes-list-output")) {
- builder.setGlobalSyntheticClassesListOutput(Paths.get(nextArg));
} else if (arg.startsWith("--")) {
builder.error(new StringDiagnostic("Unknown option: " + arg, origin));
}
diff --git a/src/main/java/com/android/tools/r8/JarSizeCompare.java b/src/main/java/com/android/tools/r8/JarSizeCompare.java
index 0be3461..8e8388b 100644
--- a/src/main/java/com/android/tools/r8/JarSizeCompare.java
+++ b/src/main/java/com/android/tools/r8/JarSizeCompare.java
@@ -61,7 +61,6 @@
.put("com.google.common", "com.android.tools.r8.com.google.common")
.put("com.google.gson", "com.android.tools.r8.com.google.gson")
.put("com.google.thirdparty", "com.android.tools.r8.com.google.thirdparty")
- .put("joptsimple", "com.android.tools.r8.joptsimple")
.put("org.apache.commons", "com.android.tools.r8.org.apache.commons")
.put("org.objectweb.asm", "com.android.tools.r8.org.objectweb.asm")
.put("it.unimi.dsi.fastutil", "com.android.tools.r8.it.unimi.dsi.fastutil")
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 79d8121..d44a39b 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -68,6 +68,7 @@
import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
import com.android.tools.r8.optimize.MemberRebindingIdentityLensFactory;
import com.android.tools.r8.optimize.bridgehoisting.BridgeHoisting;
+import com.android.tools.r8.optimize.fields.FieldFinalizer;
import com.android.tools.r8.optimize.interfaces.analysis.CfOpenClosedInterfacesAnalysis;
import com.android.tools.r8.optimize.proto.ProtoNormalizer;
import com.android.tools.r8.optimize.redundantbridgeremoval.RedundantBridgeRemover;
@@ -316,21 +317,13 @@
appView.getSyntheticItems().commit(appView.appInfo().app())));
}
- List<ProguardConfigurationRule> synthesizedProguardRules = new ArrayList<>();
timing.begin("Strip unused code");
timing.begin("Before enqueuer");
RuntimeTypeCheckInfo.Builder classMergingEnqueuerExtensionBuilder =
new RuntimeTypeCheckInfo.Builder(appView);
+ List<ProguardConfigurationRule> synthesizedProguardRules;
try {
- // Add synthesized -assumenosideeffects from min api if relevant.
- if (options.isGeneratingDex()) {
- if (!ProguardConfigurationUtils.hasExplicitAssumeValuesOrAssumeNoSideEffectsRuleForMinSdk(
- options.itemFactory, options.getProguardConfiguration().getRules())) {
- synthesizedProguardRules.add(
- ProguardConfigurationUtils.buildAssumeNoSideEffectsRuleForApiLevel(
- options.itemFactory, options.getMinApiLevel()));
- }
- }
+ synthesizedProguardRules = ProguardConfigurationUtils.synthesizeRules(appView);
ProfileCollectionAdditions profileCollectionAdditions =
ProfileCollectionAdditions.create(appView);
AssumeInfoCollection.Builder assumeInfoCollectionBuilder = AssumeInfoCollection.builder();
@@ -640,6 +633,9 @@
// Synthesize fields for triggering class initializers.
new ClassInitFieldSynthesizer(appViewWithLiveness).run(executorService);
+
+ // Finalize fields.
+ FieldFinalizer.run(appViewWithLiveness, executorService, timing);
}
} finally {
timing.end();
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java
index 546b40b..58c1ae3 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelDatabaseHelper.java
@@ -9,9 +9,21 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.AndroidApiLevel;
+import java.util.HashSet;
+import java.util.Set;
import java.util.function.BiConsumer;
-class AndroidApiLevelDatabaseHelper {
+public class AndroidApiLevelDatabaseHelper {
+
+ public static Set<String> notModeledTypes() {
+ // The below types are known not to be modeled by any api-versions.
+ Set<String> notModeledTypes = new HashSet<>();
+ notModeledTypes.add("androidx.annotation.RecentlyNullable");
+ notModeledTypes.add("androidx.annotation.RecentlyNonNull");
+ notModeledTypes.add("android.annotation.Nullable");
+ notModeledTypes.add("android.annotation.NonNull");
+ return notModeledTypes;
+ }
static void visitAdditionalKnownApiReferences(
DexItemFactory factory, BiConsumer<DexReference, AndroidApiLevel> apiLevelConsumer) {
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiUnknownReferenceDiagnosticHelper.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiUnknownReferenceDiagnosticHelper.java
new file mode 100644
index 0000000..dc8015c
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiUnknownReferenceDiagnosticHelper.java
@@ -0,0 +1,14 @@
+// Copyright (c) 2023, 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.androidapi;
+
+import com.android.tools.r8.graph.DexReference;
+
+public class AndroidApiUnknownReferenceDiagnosticHelper {
+
+ public static AndroidApiUnknownReferenceDiagnostic createInternal(DexReference reference) {
+ return new AndroidApiUnknownReferenceDiagnostic(reference);
+ }
+}
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 c42f8c6..07261a9 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -18,12 +18,15 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexLibraryClass;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.LibraryClass;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ThrowExceptionCode;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.synthesis.CommittedItems;
import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.WorkList;
@@ -35,6 +38,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
+import java.util.function.Function;
/**
* The only instructions we do not outline is constant classes, instance-of/checkcast and exception
@@ -75,16 +79,19 @@
libraryClassesToMock.forEach(
clazz ->
mockMissingLibraryClass(
+ appView,
+ referencingContexts::get,
clazz,
ThrowExceptionCode.create(appView.dexItemFactory().noClassDefFoundErrorType),
eventConsumer));
// Commit the synthetic items.
- CommittedItems committedItems = appView.getSyntheticItems().commit(appView.appInfo().app());
if (appView.hasLiveness()) {
+ CommittedItems committedItems = appView.getSyntheticItems().commit(appView.appInfo().app());
AppView<AppInfoWithLiveness> appInfoWithLivenessAppView = appView.withLiveness();
appInfoWithLivenessAppView.setAppInfo(
appInfoWithLivenessAppView.appInfo().rebuildWithLiveness(committedItems));
} else if (appView.hasClassHierarchy()) {
+ CommittedItems committedItems = appView.getSyntheticItems().commit(appView.appInfo().app());
appView
.withClassHierarchy()
.setAppInfo(
@@ -116,9 +123,13 @@
// We cannot reliably create a stub that will have the same throwing behavior for all VMs.
// Only create stubs for exceptions to allow them being present in catch handlers and super
// types of existing program classes. See b/258270051 and b/259076765 for more information.
- clazz
- .allImmediateSupertypes()
- .forEach(superType -> findReferencedLibraryClasses(superType, clazz));
+ // Also, for L devices we can have verification issues if there are super invokes to missing
+ // members on stubbed classes. See b/279780940 for more information.
+ if (appView.options().getMinApiLevel().isGreaterThan(AndroidApiLevel.L)) {
+ clazz
+ .allImmediateSupertypes()
+ .forEach(superType -> findReferencedLibraryClasses(superType, clazz));
+ }
clazz.forEachProgramMethodMatching(
DexEncodedMethod::hasCode,
method -> {
@@ -136,7 +147,7 @@
}
private void findReferencedLibraryClasses(DexType type, DexProgramClass context) {
- if (!type.isClassType() || isJavaType(type)) {
+ if (!type.isClassType() || isJavaType(type, appView.dexItemFactory())) {
return;
}
WorkList.newIdentityWorkList(type, seenTypes)
@@ -160,18 +171,25 @@
});
}
- private boolean isJavaType(DexType type) {
- return type == appView.dexItemFactory().objectType
- || type.getDescriptor().startsWith(appView.dexItemFactory().javaDescriptorPrefix);
+ public static boolean isJavaType(DexType type, DexItemFactory factory) {
+ DexString typeDescriptor = type.getDescriptor();
+ return type == factory.objectType
+ || typeDescriptor.startsWith(factory.comSunDescriptorPrefix)
+ || typeDescriptor.startsWith(factory.javaDescriptorPrefix)
+ || typeDescriptor.startsWith(factory.javaxDescriptorPrefix)
+ || typeDescriptor.startsWith(factory.jdkDescriptorPrefix)
+ || typeDescriptor.startsWith(factory.sunDescriptorPrefix);
}
- private void mockMissingLibraryClass(
+ public static void mockMissingLibraryClass(
+ AppView<?> appView,
+ Function<LibraryClass, Set<DexProgramClass>> referencingContextSupplier,
DexLibraryClass libraryClass,
ThrowExceptionCode throwExceptionCode,
ApiReferenceStubberEventConsumer eventConsumer) {
DexItemFactory factory = appView.dexItemFactory();
// Do not stub the anything starting with java (including the object type).
- if (isJavaType(libraryClass.getType())) {
+ if (isJavaType(libraryClass.getType(), factory)) {
return;
}
// Check if desugared library will bridge the type.
@@ -181,7 +199,7 @@
.isSupported(libraryClass.getType())) {
return;
}
- Set<DexProgramClass> contexts = referencingContexts.get(libraryClass);
+ Set<DexProgramClass> contexts = referencingContextSupplier.apply(libraryClass);
if (contexts == null) {
throw new Unreachable("Attempt to create a global synthetic with no contexts");
}
diff --git a/src/main/java/com/android/tools/r8/bisect/Bisect.java b/src/main/java/com/android/tools/r8/bisect/Bisect.java
index c4c8170..9bfe7c3 100644
--- a/src/main/java/com/android/tools/r8/bisect/Bisect.java
+++ b/src/main/java/com/android/tools/r8/bisect/Bisect.java
@@ -209,7 +209,7 @@
options = BisectOptions.parse(args);
} catch (CompilationError e) {
System.err.println(e.getMessage());
- BisectOptions.printHelp(System.err);
+ BisectOptions.printHelp();
return;
}
if (options == null) {
diff --git a/src/main/java/com/android/tools/r8/bisect/BisectOptions.java b/src/main/java/com/android/tools/r8/bisect/BisectOptions.java
index 4f59900..0b38e3a 100644
--- a/src/main/java/com/android/tools/r8/bisect/BisectOptions.java
+++ b/src/main/java/com/android/tools/r8/bisect/BisectOptions.java
@@ -5,23 +5,20 @@
import com.android.tools.r8.errors.CompilationError;
import java.io.IOException;
-import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import joptsimple.OptionParser;
-import joptsimple.OptionSet;
-import joptsimple.OptionSpec;
public class BisectOptions {
- private static final String HELP_FLAG = "help";
- public static final String BUILD_GOOD_FLAG = "good";
- public static final String BUILD_BAD_FLAG = "bad";
- public static final String RESULT_GOOD_FLAG = "result-good";
- public static final String RESULT_BAD_FLAG = "result-bad";
- public static final String STATE_FLAG = "state";
- public static final String OUTPUT_FLAG = "output";
- public static final String COMMAND_FLAG = "command";
+
+ private static final String HELP_FLAG = "--help";
+ public static final String BUILD_GOOD_FLAG = "--good";
+ public static final String BUILD_BAD_FLAG = "--bad";
+ public static final String RESULT_GOOD_FLAG = "--result-good";
+ public static final String RESULT_BAD_FLAG = "--result-bad";
+ public static final String STATE_FLAG = "--state";
+ public static final String OUTPUT_FLAG = "--output";
+ public static final String COMMAND_FLAG = "--command";
public final Path goodBuild;
public final Path badBuild;
@@ -30,52 +27,10 @@
public final Path output;
public final Result result;
- public enum Result { UNKNOWN, GOOD, BAD }
-
- private static class ParserSpec {
- OptionSpec<String> goodBuild;
- OptionSpec<String> badBuild;
- OptionSpec<String> command;
- OptionSpec<String> stateFile;
- OptionSpec<String> output;
- OptionSpec<Void> resultGood;
- OptionSpec<Void> resultBad;
- OptionSpec<Void> help;
-
- void init(OptionParser parser) {
- help = parser.accepts(HELP_FLAG).forHelp();
- resultGood = parser.accepts(RESULT_GOOD_FLAG, "Bisect again assuming previous run was good.");
- resultBad = parser.accepts(RESULT_BAD_FLAG, "Bisect again assuming previous run was bad.");
- goodBuild = parser.accepts(BUILD_GOOD_FLAG, "Known good APK.")
- .withRequiredArg()
- .describedAs("apk");
- badBuild = parser.accepts(BUILD_BAD_FLAG, "Known bad APK.")
- .withRequiredArg()
- .describedAs("apk");
- stateFile = parser.accepts(STATE_FLAG, "Bisection state.")
- .requiredIf(resultGood, resultBad)
- .withRequiredArg()
- .describedAs("file");
- output = parser.accepts(OUTPUT_FLAG, "Output directory.")
- .withRequiredArg()
- .describedAs("dir");
- command = parser.accepts(COMMAND_FLAG, "Command to run after each bisection.")
- .requiredUnless(stateFile)
- .withRequiredArg()
- .describedAs("file");
- }
-
- OptionSet parse(String[] args) {
- OptionParser parser = new OptionParser();
- init(parser);
- return parser.parse(args);
- }
-
- static void printHelp(OutputStream out) throws IOException {
- OptionParser parser = new OptionParser();
- new ParserSpec().init(parser);
- parser.printHelpOn(out);
- }
+ public enum Result {
+ UNKNOWN,
+ GOOD,
+ BAD
}
private BisectOptions(
@@ -89,65 +44,100 @@
}
public static BisectOptions parse(String[] args) throws IOException {
- ParserSpec parser = new ParserSpec();
- OptionSet options = parser.parse(args);
- if (options.has(parser.help)) {
- printHelp(System.out);
- return null;
- }
- Path goodBuild = exists(require(options, parser.goodBuild, BUILD_GOOD_FLAG), BUILD_GOOD_FLAG);
- Path badBuild = exists(require(options, parser.badBuild, BUILD_BAD_FLAG), BUILD_BAD_FLAG);
+ Path badBuild = null;
+ Path goodBuild = null;
Path stateFile = null;
- if (options.valueOf(parser.stateFile) != null) {
- stateFile = exists(options.valueOf(parser.stateFile), STATE_FLAG);
- }
Path command = null;
- if (options.valueOf(parser.command) != null) {
- command = exists(options.valueOf(parser.command), COMMAND_FLAG);
- }
Path output = null;
- if (options.valueOf(parser.output) != null) {
- output = directoryExists(options.valueOf(parser.output), OUTPUT_FLAG);
- }
Result result = Result.UNKNOWN;
- if (options.has(parser.resultGood)) {
- result = Result.GOOD;
- }
- if (options.has(parser.resultBad)) {
- if (result == Result.GOOD) {
- throw new CompilationError("Cannot specify --" + RESULT_GOOD_FLAG
- + " and --" + RESULT_BAD_FLAG + " simultaneously");
+
+ for (int i = 0; i < args.length; i++) {
+ String arg = args[i].trim();
+ if (arg.equals(HELP_FLAG)) {
+ printHelp();
+ return null;
+ } else if (arg.equals(BUILD_BAD_FLAG)) {
+ i = nextArg(i, args, BUILD_BAD_FLAG);
+ badBuild = Paths.get(args[i]);
+ } else if (arg.equals(BUILD_GOOD_FLAG)) {
+ i = nextArg(i, args, BUILD_GOOD_FLAG);
+ goodBuild = Paths.get(args[i]);
+ } else if (arg.equals(COMMAND_FLAG)) {
+ i = nextArg(i, args, COMMAND_FLAG);
+ command = Paths.get(args[i]);
+ } else if (arg.equals(OUTPUT_FLAG)) {
+ i = nextArg(i, args, OUTPUT_FLAG);
+ output = Paths.get(args[i]);
+ } else if (arg.equals(RESULT_BAD_FLAG)) {
+ result = checkSingleResult(result, Result.BAD);
+ } else if (arg.equals(RESULT_GOOD_FLAG)) {
+ result = checkSingleResult(result, Result.GOOD);
+ } else if (arg.equals(STATE_FLAG)) {
+ i = nextArg(i, args, STATE_FLAG);
+ stateFile = Paths.get(args[i]);
}
- result = Result.BAD;
}
+ exists(require(badBuild, BUILD_BAD_FLAG), BUILD_BAD_FLAG);
+ exists(require(goodBuild, BUILD_GOOD_FLAG), BUILD_GOOD_FLAG);
+ if (stateFile != null) {
+ exists(stateFile, STATE_FLAG);
+ }
+ if (command != null) {
+ exists(command, COMMAND_FLAG);
+ }
+ if (output != null) {
+ directoryExists(output, OUTPUT_FLAG);
+ }
+
return new BisectOptions(goodBuild, badBuild, stateFile, command, output, result);
}
- private static <T> T require(OptionSet options, OptionSpec<T> option, String flag) {
- T value = options.valueOf(option);
- if (value != null) {
- return value;
+ private static int nextArg(int index, String[] args, String flag) {
+ if (args.length == index + 1) {
+ throw new CompilationError("Missing argument for: " + flag);
}
- throw new CompilationError("Missing required option: --" + flag);
+ return index + 1;
}
- private static Path exists(String path, String flag) {
- Path file = Paths.get(path);
- if (Files.exists(file)) {
- return file;
+ private static Path require(Path value, String flag) {
+ if (value == null) {
+ throw new CompilationError("Missing required option: " + flag);
}
- throw new CompilationError("File --" + flag + ": " + file + " does not exist");
+ return value;
}
- private static Path directoryExists(String path, String flag) {
- Path file = Paths.get(path);
- if (Files.exists(file) && Files.isDirectory(file)) {
- return file;
+ private static Path exists(Path path, String flag) {
+ if (Files.exists(path)) {
+ return path;
}
- throw new CompilationError("File --" + flag + ": " + file + " is not a valid directory");
+ throw new CompilationError("File " + flag + ": " + path + " does not exist");
}
- public static void printHelp(OutputStream out) throws IOException {
- ParserSpec.printHelp(out);
+ private static Path directoryExists(Path path, String flag) {
+ if (Files.exists(path) && Files.isDirectory(path)) {
+ return path;
+ }
+ throw new CompilationError("File " + flag + ": " + path + " is not a valid directory");
+ }
+
+ private static Result checkSingleResult(Result current, Result result) {
+ if (current != Result.UNKNOWN) {
+ throw new CompilationError(
+ "Cannot specify " + RESULT_GOOD_FLAG + " and " + RESULT_BAD_FLAG + " simultaneously");
+ }
+ return result;
+ }
+
+ public static void printHelp() throws IOException {
+ System.out.println("--bad <apk> Known bad APK.");
+ System.out.println("--command <file> Command to run after each bisection.");
+ System.out.println("--good <apk> Known good APK.");
+ System.out.println("--help");
+ System.out.println("--output <dir> Output directory.");
+ System.out.println(
+ "--result-bad Bisect again assuming previous run was\n" + " bad.");
+ System.out.println(
+ "--result-good Bisect again assuming previous run was\n" + " good.");
+ System.out.println("--state <file> Bisection state.");
}
}
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 8c6f33a..686d327 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -40,6 +40,7 @@
import com.android.tools.r8.graph.DexTypeList;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.DexWritableCode;
+import com.android.tools.r8.graph.DexWritableCode.DexWritableCacheKey;
import com.android.tools.r8.graph.IndexedDexItem;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.ParameterAnnotationsList;
@@ -56,6 +57,7 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.LebUtils;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
@@ -67,6 +69,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
@@ -223,7 +226,8 @@
Collection<ProgramMethod> codes = mixedSectionLayoutStrategy.getCodeLayout();
// Output the debug_info_items first, as they have no dependencies.
- dest.moveTo(layout.getCodesOffset() + sizeOfCodeItems(codes));
+ SizeAndCount sizeAndCountOfCodeItems = sizeAndCountOfCodeItems(codes);
+ dest.moveTo(layout.getCodesOffset() + sizeAndCountOfCodeItems.size);
if (mixedSectionOffsets.getDebugInfos().isEmpty()) {
layout.setDebugInfosOffset(0);
} else {
@@ -245,7 +249,25 @@
// Now output the code.
dest.moveTo(layout.getCodesOffset());
assert dest.isAligned(4);
- writeItems(codes, layout::alreadySetOffset, this::writeCodeItem, 4);
+ Map<DexWritableCacheKey, Integer> offsetCache = new HashMap<>();
+ for (ProgramMethod method : codes) {
+ DexWritableCode dexWritableCode = method.getDefinition().getCode().asDexWritableCode();
+ if (!options.canUseCanonicalizedCodeObjects()) {
+ writeCodeItem(method, dexWritableCode);
+ } else {
+ DexWritableCacheKey cacheLookupKey =
+ dexWritableCode.getCacheLookupKey(method, appView.dexItemFactory());
+ Integer offsetOrNull = offsetCache.get(cacheLookupKey);
+ if (offsetOrNull != null) {
+ mixedSectionOffsets.setOffsetFor(method.getDefinition(), offsetOrNull);
+ } else {
+ offsetCache.put(cacheLookupKey, writeCodeItem(method, dexWritableCode));
+ }
+ }
+ }
+ assert sizeAndCountOfCodeItems.getCount()
+ == ImmutableSet.copyOf(mixedSectionOffsets.codes.values()).size();
+ layout.setCodeCount(sizeAndCountOfCodeItems.getCount());
assert layout.getDebugInfosOffset() == 0 || dest.position() == layout.getDebugInfosOffset();
// Now the type lists and rest.
@@ -434,13 +456,32 @@
}
}
- private int sizeOfCodeItems(Iterable<ProgramMethod> methods) {
- int size = 0;
- for (ProgramMethod method : methods) {
- size = alignSize(4, size);
- size += sizeOfCodeItem(method.getDefinition().getCode().asDexWritableCode());
+ static class SizeAndCount {
+
+ private int size = 0;
+ private int count = 0;
+
+ public int getCount() {
+ return count;
}
- return size;
+
+ public int getSize() {
+ return size;
+ }
+ }
+
+ private SizeAndCount sizeAndCountOfCodeItems(Iterable<ProgramMethod> methods) {
+ SizeAndCount sizeAndCount = new SizeAndCount();
+ Set<DexWritableCacheKey> cache = new HashSet<>();
+ for (ProgramMethod method : methods) {
+ DexWritableCode code = method.getDefinition().getCode().asDexWritableCode();
+ if (!options.canUseCanonicalizedCodeObjects()
+ || cache.add(code.getCacheLookupKey(method, appView.dexItemFactory()))) {
+ sizeAndCount.count++;
+ sizeAndCount.size = alignSize(4, sizeAndCount.size) + sizeOfCodeItem(code);
+ }
+ }
+ return sizeAndCount;
}
private int sizeOfCodeItem(DexWritableCode code) {
@@ -525,12 +566,9 @@
dest.putBytes(new DebugBytecodeWriter(debugInfo, mapping, graphLens).generate());
}
- private void writeCodeItem(ProgramMethod method) {
- writeCodeItem(method, method.getDefinition().getCode().asDexWritableCode());
- }
-
- private void writeCodeItem(ProgramMethod method, DexWritableCode code) {
- mixedSectionOffsets.setOffsetFor(method.getDefinition(), code, dest.align(4));
+ private int writeCodeItem(ProgramMethod method, DexWritableCode code) {
+ int codeOffset = dest.align(4);
+ mixedSectionOffsets.setOffsetFor(method.getDefinition(), codeOffset);
// Fixed size header information.
dest.putShort((short) code.getRegisterSize(method));
dest.putShort((short) code.getIncomingRegisterSize(method));
@@ -580,6 +618,7 @@
// And move to the end.
dest.moveTo(endOfCodeOffset);
}
+ return codeOffset;
}
private void writeTypeList(DexTypeList list) {
@@ -943,6 +982,7 @@
private int encodedArraysOffset = NOT_SET;
private int mapOffset = NOT_SET;
private int endOfFile = NOT_SET;
+ private int codeCount = NOT_SET;
private Layout(
int headerOffset,
@@ -1024,6 +1064,15 @@
this.codesOffset = codesOffset;
}
+ public void setCodeCount(int codeCount) {
+ assert this.codeCount == NOT_SET;
+ this.codeCount = codeCount;
+ }
+
+ public int getCodeCount() {
+ return codeCount;
+ }
+
public int getDebugInfosOffset() {
assert isValidOffset(debugInfosOffset, false);
return debugInfosOffset;
@@ -1185,11 +1234,7 @@
Constants.TYPE_METHOD_HANDLE_ITEM,
methodHandleIdsOffset,
fileWriter.mapping.getMethodHandles().size()));
- mapItems.add(
- new MapItem(
- Constants.TYPE_CODE_ITEM,
- getCodesOffset(),
- fileWriter.mixedSectionOffsets.getCodes().size()));
+ mapItems.add(new MapItem(Constants.TYPE_CODE_ITEM, getCodesOffset(), codeCount));
mapItems.add(
new MapItem(
Constants.TYPE_DEBUG_INFO_ITEM,
@@ -1601,7 +1646,7 @@
setOffsetFor(debugInfo, offset, debugInfos);
}
- void setOffsetFor(DexEncodedMethod method, DexWritableCode code, int offset) {
+ void setOffsetFor(DexEncodedMethod method, int offset) {
setOffsetFor(method, offset, codes);
}
diff --git a/src/main/java/com/android/tools/r8/dex/Marker.java b/src/main/java/com/android/tools/r8/dex/Marker.java
index 4367a38..4470baf 100644
--- a/src/main/java/com/android/tools/r8/dex/Marker.java
+++ b/src/main/java/com/android/tools/r8/dex/Marker.java
@@ -26,13 +26,13 @@
public static final String BACKEND = "backend";
public static final String PG_MAP_ID = "pg-map-id";
public static final String R8_MODE = "r8-mode";
- private static final String NO_LIBRARY_DESUGARING = "<no-library-desugaring>";
private static final String ANDROID_PLATFORM_BUILD = "platform";
public enum Tool {
D8,
- R8,
+ GlobalSyntheticsGenerator,
L8,
+ R8,
Relocator,
TraceReferences;
diff --git a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
index 06fd92e..b0e0aba 100644
--- a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
@@ -236,7 +236,8 @@
return getMaxLocals(method);
}
- static DexMethod getParentConstructor(DexClassAndMethod method, DexItemFactory dexItemFactory) {
+ public static DexMethod getParentConstructor(
+ DexClassAndMethod method, DexItemFactory dexItemFactory) {
return dexItemFactory.createInstanceInitializer(method.getHolder().getSuperType());
}
@@ -397,6 +398,15 @@
return toString();
}
+ @Override
+ public DexWritableCacheKey getCacheLookupKey(ProgramMethod method, DexItemFactory factory) {
+ return new AmendedDexWritableCodeKey<DexMethod>(
+ this,
+ getParentConstructor(method, factory),
+ getIncomingRegisterSize(method),
+ getRegisterSize(method));
+ }
+
static class DefaultInstanceInitializerSourceCode extends SyntheticStraightLineSourceCode {
DefaultInstanceInitializerSourceCode(DexMethod method) {
diff --git a/src/main/java/com/android/tools/r8/graph/Definition.java b/src/main/java/com/android/tools/r8/graph/Definition.java
index 05ad2d1..e03b8e8 100644
--- a/src/main/java/com/android/tools/r8/graph/Definition.java
+++ b/src/main/java/com/android/tools/r8/graph/Definition.java
@@ -155,4 +155,12 @@
default ProgramMethod asProgramMethod() {
return null;
}
+
+ default boolean isSamePackage(Definition definition) {
+ return isSamePackage(definition.getReference());
+ }
+
+ default boolean isSamePackage(DexReference reference) {
+ return getReference().isSamePackage(reference);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index c79cb1b..1d7c8cf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -31,6 +31,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
@@ -817,8 +818,8 @@
}
public boolean validInterfaceSignatures() {
- return getClassSignature().superInterfaceSignatures().isEmpty()
- || interfaces.values.length == getClassSignature().superInterfaceSignatures.size();
+ return getClassSignature().getSuperInterfaceSignatures().isEmpty()
+ || interfaces.values.length == getClassSignature().getSuperInterfaceSignatures().size();
}
public void forEachImmediateInterfaceWithSignature(
@@ -826,7 +827,7 @@
assert validInterfaceSignatures();
// If there is no generic signature information don't pass any type arguments.
- if (getClassSignature().superInterfaceSignatures().isEmpty()) {
+ if (getClassSignature().getSuperInterfaceSignatures().isEmpty()) {
forEachImmediateInterface(
superInterface ->
consumer.accept(superInterface, new ClassTypeSignature(superInterface)));
@@ -835,7 +836,7 @@
Iterator<DexType> interfaceIterator = Arrays.asList(interfaces.values).iterator();
Iterator<ClassTypeSignature> interfaceSignatureIterator =
- getClassSignature().superInterfaceSignatures().iterator();
+ getClassSignature().getSuperInterfaceSignatures().iterator();
while (interfaceIterator.hasNext()) {
assert interfaceSignatureIterator.hasNext();
@@ -846,9 +847,9 @@
}
public void forEachImmediateSupertypeWithSignature(
- BiConsumer<DexType, ClassTypeSignature> consumer) {
+ DexItemFactory factory, BiConsumer<DexType, ClassTypeSignature> consumer) {
if (superType != null) {
- consumer.accept(superType, classSignature.superClassSignature);
+ consumer.accept(superType, classSignature.getSuperClassSignatureOrObject(factory));
}
forEachImmediateInterfaceWithSignature(consumer);
}
@@ -859,7 +860,7 @@
assert validInterfaceSignatures();
// If there is no generic signature information don't pass any type arguments.
- if (getClassSignature().superInterfaceSignatures().size() == 0) {
+ if (getClassSignature().getSuperInterfaceSignatures().isEmpty()) {
forEachImmediateInterface(
superInterface -> consumer.accept(superInterface, ImmutableList.of()));
return;
@@ -867,7 +868,7 @@
Iterator<DexType> interfaceIterator = Arrays.asList(interfaces.values).iterator();
Iterator<ClassTypeSignature> interfaceSignatureIterator =
- getClassSignature().superInterfaceSignatures().iterator();
+ getClassSignature().getSuperInterfaceSignatures().iterator();
while (interfaceIterator.hasNext()) {
assert interfaceSignatureIterator.hasNext();
@@ -890,17 +891,18 @@
BiConsumer<DexType, List<FieldTypeSignature>> consumer) {
if (superType != null) {
consumer.accept(
- superType, applyTypeArguments(getClassSignature().superClassSignature, typeArguments));
+ superType,
+ applyTypeArguments(getClassSignature().getSuperClassSignatureOrNull(), typeArguments));
}
forEachImmediateInterfaceWithAppliedTypeArguments(typeArguments, consumer);
}
private List<FieldTypeSignature> applyTypeArguments(
ClassTypeSignature superInterfaceSignatures, List<FieldTypeSignature> appliedTypeArguments) {
- ImmutableList.Builder<FieldTypeSignature> superTypeArgumentsBuilder = ImmutableList.builder();
- if (superInterfaceSignatures.type.toSourceString().equals("java.util.Map")) {
- System.currentTimeMillis();
+ if (superInterfaceSignatures == null) {
+ return Collections.emptyList();
}
+ ImmutableList.Builder<FieldTypeSignature> superTypeArgumentsBuilder = ImmutableList.builder();
superInterfaceSignatures
.typeArguments()
.forEach(
@@ -1150,11 +1152,11 @@
return fieldCollection.hasInstanceFields();
}
- public List<DexEncodedField> getDirectAndIndirectInstanceFields(AppView<?> appView) {
- List<DexEncodedField> result = new ArrayList<>();
+ public List<DexClassAndField> getDirectAndIndirectInstanceFields(AppView<?> appView) {
+ List<DexClassAndField> result = new ArrayList<>();
DexClass current = this;
while (current != null && current.type != appView.dexItemFactory().objectType) {
- result.addAll(current.instanceFields());
+ current.forEachClassFieldMatching(DexEncodedField::isInstance, result::add);
current = appView.definitionFor(current.superType);
}
return result;
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
index 30179b3..1dc176f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClassAndField.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
public abstract class DexClassAndField extends DexClassAndMember<DexEncodedField, DexField> {
@@ -59,4 +60,13 @@
public DexClassAndField asMember() {
return this;
}
+
+ public final boolean isFinalOrEffectivelyFinal(AppView<?> appView) {
+ return getAccessFlags().isFinal()
+ || (appView.hasLiveness() && isEffectivelyFinal(appView.withLiveness()));
+ }
+
+ public boolean isEffectivelyFinal(AppView<AppInfoWithLiveness> appView) {
+ return false;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index ae96c5e..039a0f0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.graph.DexDebugEvent.SetPositionFrame;
import com.android.tools.r8.graph.DexDebugEvent.StartLocal;
import com.android.tools.r8.graph.DexDebugInfo.EventBasedDebugInfo;
+import com.android.tools.r8.graph.DexWritableCode.DexWritableCacheKey;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeInstructionMetadata;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadata;
import com.android.tools.r8.graph.lens.GraphLens;
@@ -62,7 +63,8 @@
import java.util.function.Consumer;
// DexCode corresponds to code item in dalvik/dex-format.html
-public class DexCode extends Code implements DexWritableCode, StructuralItem<DexCode> {
+public class DexCode extends Code
+ implements DexWritableCode, StructuralItem<DexCode>, DexWritableCacheKey {
public static final String FAKE_THIS_PREFIX = "_";
public static final String FAKE_THIS_SUFFIX = "this";
@@ -283,6 +285,7 @@
if (debugInfoForWriting != null) {
debugInfoForWriting = null;
}
+ flushCachedValues();
}
public DexDebugInfo debugInfoWithFakeThisParameter(DexItemFactory factory) {
@@ -858,6 +861,11 @@
}
}
+ @Override
+ public DexWritableCacheKey getCacheLookupKey(ProgramMethod method, DexItemFactory factory) {
+ return this;
+ }
+
public static class Try extends DexItem implements StructuralItem<Try> {
public static final Try[] EMPTY_ARRAY = new Try[0];
diff --git a/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
index fca76e5..22985dc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
@@ -91,6 +91,11 @@
return definitionFor(type) != null;
}
+ default DexClassAndField definitionFor(DexField field) {
+ DexClass holder = definitionFor(field.getHolderType());
+ return holder != null ? holder.lookupClassField(field) : null;
+ }
+
default DexClassAndMethod definitionFor(DexMethod method) {
DexClass holder = definitionFor(method.getHolderType());
return holder != null ? holder.lookupClassMethod(method) : null;
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 c6f2634..5d63bdc 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedField.java
@@ -4,7 +4,6 @@
package com.android.tools.r8.graph;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo;
import com.android.tools.r8.androidapi.ComputedApiLevel;
@@ -12,18 +11,12 @@
import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature;
import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.TypeElement;
-import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.ir.analysis.value.SingleValue;
-import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
import com.android.tools.r8.ir.optimize.info.DefaultFieldOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MutableFieldOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.kotlin.KotlinFieldLevelInfo;
import com.android.tools.r8.kotlin.KotlinMetadataUtils;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.structural.StructuralItem;
import com.android.tools.r8.utils.structural.StructuralMapping;
@@ -193,6 +186,15 @@
return fieldConsumer.apply(this);
}
+ public DexClassAndField asClassField(DexDefinitionSupplier definitions) {
+ assert getHolderType().isClassType();
+ DexProgramClass clazz = asProgramClassOrNull(definitions.definitionForHolder(getReference()));
+ if (clazz != null) {
+ return DexClassAndField.create(clazz, this);
+ }
+ return null;
+ }
+
public ProgramField asProgramField(DexDefinitionSupplier definitions) {
assert getHolderType().isClassType();
DexProgramClass clazz = asProgramClassOrNull(definitions.definitionForHolder(getReference()));
@@ -210,6 +212,10 @@
return accessFlags.isFinal();
}
+ public boolean isInstance() {
+ return !isStatic();
+ }
+
@Override
public boolean isStatic() {
return accessFlags.isStatic();
@@ -257,51 +263,6 @@
return staticValue == null ? DexValue.defaultForType(getReference().type) : staticValue;
}
- /**
- * Returns a const instructions if this field is a compile time final const.
- *
- * <p>NOTE: It is the responsibility of the caller to check if this field is pinned or not.
- */
- public Instruction valueAsConstInstruction(
- IRCode code, DebugLocalInfo local, AppView<AppInfoWithLiveness> appView) {
- boolean isWritten = appView.appInfo().isFieldWrittenByFieldPutInstruction(this);
- if (!isWritten) {
- // Since the field is not written, we can simply return the default value for the type.
- DexValue value = isStatic() ? getStaticValue() : DexValue.defaultForType(getReference().type);
- return value.asConstInstruction(appView, code, local);
- }
-
- // Check if we have a single value for the field according to the field optimization info.
- AbstractValue abstractValue = getOptimizationInfo().getAbstractValue();
- if (abstractValue.isSingleValue()) {
- SingleValue singleValue = abstractValue.asSingleValue();
- if (singleValue.isSingleFieldValue()
- && singleValue.asSingleFieldValue().getField() == getReference()) {
- return null;
- }
- if (singleValue.isMaterializableInContext(appView, code.context())) {
- TypeElement type = TypeElement.fromDexType(getReference().type, maybeNull(), appView);
- return singleValue.createMaterializingInstruction(
- appView, code, TypeAndLocalInfoSupplier.create(type, local));
- }
- }
-
- // The only way to figure out whether the static value contains the final value is ensure the
- // value is not the default or check that <clinit> is not present.
- if (accessFlags.isFinal() && isStatic()) {
- DexClass clazz = appView.definitionFor(getReference().holder);
- if (clazz == null || clazz.hasClassInitializer()) {
- return null;
- }
- DexValue staticValue = getStaticValue();
- if (!staticValue.isDefault(getReference().type)) {
- return staticValue.asConstInstruction(appView, code, local);
- }
- }
-
- return null;
- }
-
public DexEncodedField toTypeSubstitutedField(AppView<?> appView, DexField field) {
return toTypeSubstitutedField(appView, field, ConsumerUtils.emptyConsumer());
}
@@ -442,14 +403,6 @@
return this;
}
- public Builder setAbstractValue(
- AbstractValue abstractValue, AppView<AppInfoWithLiveness> appView) {
- return addBuildConsumer(
- fixedUpField ->
- OptimizationFeedbackSimple.getInstance()
- .recordFieldHasAbstractValue(fixedUpField, appView, abstractValue));
- }
-
public Builder clearDynamicType() {
return addBuildConsumer(
fixedUpField ->
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 164797a..41ca6f5 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -159,7 +159,11 @@
public final DexString shortDescriptor = createString("S");
public final DexString voidDescriptor = createString("V");
public final DexString descriptorSeparator = createString("/");
+ public final DexString comSunDescriptorPrefix = createString("Lcom/sun/");
public final DexString javaDescriptorPrefix = createString("Ljava/");
+ public final DexString javaxDescriptorPrefix = createString("Ljavax/");
+ public final DexString jdkDescriptorPrefix = createString("Ljdk/");
+ public final DexString sunDescriptorPrefix = createString("Lsun/");
public final DexString jDollarDescriptorPrefix = createString("Lj$/");
private final DexString booleanArrayDescriptor = createString("[Z");
@@ -1990,26 +1994,28 @@
return field == nameField || field == ordinalField;
}
- public boolean isEnumFieldCandidate(DexEncodedField staticField) {
- assert staticField.isStatic();
- return staticField.isEnum() && staticField.isFinal();
+ public boolean isEnumFieldCandidate(DexClassAndField staticField) {
+ FieldAccessFlags accessFlags = staticField.getAccessFlags();
+ assert accessFlags.isStatic();
+ return accessFlags.isEnum() && accessFlags.isFinal();
}
// In some case, the enum field may be respecialized to an enum subtype. In this case, one
// can pass the encoded field as well as the field with the super enum type for the checks.
public boolean isEnumField(
- DexEncodedField staticField, DexType enumType, Set<DexType> subtypes) {
- assert staticField.isStatic();
+ DexClassAndField staticField, DexType enumType, Set<DexType> subtypes) {
+ assert staticField.getAccessFlags().isStatic();
return (staticField.getType() == enumType || subtypes.contains(staticField.getType()))
&& isEnumFieldCandidate(staticField);
}
- public boolean isValuesFieldCandidate(DexEncodedField staticField, DexType enumType) {
- assert staticField.isStatic();
+ public boolean isValuesFieldCandidate(DexClassAndField staticField, DexType enumType) {
+ FieldAccessFlags accessFlags = staticField.getAccessFlags();
+ assert accessFlags.isStatic();
return staticField.getType().isArrayType()
&& staticField.getType().toArrayElementType(DexItemFactory.this) == enumType
- && staticField.isSynthetic()
- && staticField.isFinal();
+ && accessFlags.isSynthetic()
+ && accessFlags.isFinal();
}
}
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 fc4c4a6..1aa866f 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -27,7 +27,6 @@
import com.android.tools.r8.utils.structural.StructuralItem;
import com.android.tools.r8.utils.structural.StructuralMapping;
import com.android.tools.r8.utils.structural.StructuralSpecification;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Arrays;
@@ -35,6 +34,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -406,7 +406,13 @@
public TraversalContinuation<?, ?> traverseProgramFields(
Function<? super ProgramField, TraversalContinuation<?, ?>> fn) {
- return traverseFields(field -> fn.apply(new ProgramField(this, field)));
+ return getFieldCollection().traverse(field -> fn.apply(field.asProgramField()));
+ }
+
+ public <BT, CT> TraversalContinuation<BT, CT> traverseProgramFields(
+ BiFunction<? super ProgramField, CT, TraversalContinuation<BT, CT>> fn, CT initialValue) {
+ return getFieldCollection()
+ .traverse((field, value) -> fn.apply(field.asProgramField(), value), initialValue);
}
public TraversalContinuation<?, ?> traverseProgramMethods(
@@ -489,7 +495,7 @@
if (hasMethodsOrFields()) {
collector.add(this);
methodCollection.forEachMethod(m -> m.collectMixedSectionItems(collector));
- fieldCollection.forEachField(f -> f.collectMixedSectionItems(collector));
+ fieldCollection.forEachField(f -> f.getDefinition().collectMixedSectionItems(collector));
}
annotations().collectMixedSectionItems(collector);
if (interfaces != null) {
@@ -732,12 +738,12 @@
methodCollection.replaceVirtualMethod(virtualMethod, replacement);
}
- public void addExtraInterfaces(List<ClassTypeSignature> extraInterfaces) {
+ public void addExtraInterfaces(List<ClassTypeSignature> extraInterfaces, DexItemFactory factory) {
if (extraInterfaces.isEmpty()) {
return;
}
addExtraInterfacesToInterfacesArray(extraInterfaces);
- addExtraInterfacesToSignatureIfPresent(extraInterfaces);
+ addExtraInterfacesToSignatureIfPresent(extraInterfaces, factory);
}
private void addExtraInterfacesToInterfacesArray(List<ClassTypeSignature> extraInterfaces) {
@@ -749,21 +755,20 @@
interfaces = new DexTypeList(newInterfaces);
}
- private void addExtraInterfacesToSignatureIfPresent(List<ClassTypeSignature> extraInterfaces) {
+ private void addExtraInterfacesToSignatureIfPresent(
+ List<ClassTypeSignature> extraInterfaces, DexItemFactory factory) {
+ assert !extraInterfaces.isEmpty();
// We introduce the extra interfaces to the generic signature.
- if (classSignature.hasNoSignature() || extraInterfaces.isEmpty()) {
+ if (classSignature.hasNoSignature()) {
return;
}
- ImmutableList.Builder<ClassTypeSignature> interfacesBuilder =
- ImmutableList.<ClassTypeSignature>builder().addAll(classSignature.superInterfaceSignatures);
- for (ClassTypeSignature extraInterface : extraInterfaces) {
- interfacesBuilder.add(extraInterface);
- }
classSignature =
- new ClassSignature(
- classSignature.formalTypeParameters,
- classSignature.superClassSignature,
- interfacesBuilder.build());
+ ClassSignature.builder()
+ .addSuperInterfaceSignatures(classSignature.getSuperInterfaceSignatures())
+ .addSuperInterfaceSignatures(extraInterfaces)
+ .setSuperClassSignature(classSignature.getSuperClassSignatureOrNull())
+ .addFormalTypeParameters(classSignature.getFormalTypeParameters())
+ .build(factory);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexReference.java b/src/main/java/com/android/tools/r8/graph/DexReference.java
index 6956170..1e84335 100644
--- a/src/main/java/com/android/tools/r8/graph/DexReference.java
+++ b/src/main/java/com/android/tools/r8/graph/DexReference.java
@@ -83,6 +83,10 @@
return null;
}
+ public boolean isSamePackage(DexReference reference) {
+ return getContextType().isSamePackage(reference.getContextType());
+ }
+
public int referenceTypeOrder() {
if (isDexType()) {
return 1;
diff --git a/src/main/java/com/android/tools/r8/graph/DexWritableCode.java b/src/main/java/com/android/tools/r8/graph/DexWritableCode.java
index 1e8856c..99f8679 100644
--- a/src/main/java/com/android/tools/r8/graph/DexWritableCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexWritableCode.java
@@ -92,6 +92,8 @@
return null;
}
+ DexWritableCacheKey getCacheLookupKey(ProgramMethod method, DexItemFactory factory);
+
/** Rewrites the code to have JumboString bytecode if required by mapping. */
DexWritableCode rewriteCodeWithJumboStrings(
ProgramMethod method, ObjectToOffsetMapping mapping, DexItemFactory factory, boolean force);
@@ -105,4 +107,65 @@
GraphLens codeLens,
LensCodeRewriterUtils lensCodeRewriter,
ObjectToOffsetMapping mapping);
+
+ interface DexWritableCacheKey {}
+
+ class DexWritableCodeKey implements DexWritableCacheKey {
+
+ private final DexWritableCode code;
+ private final int incomingRegisterSize;
+ private final int registerSize;
+
+ public DexWritableCodeKey(DexWritableCode code, int incomingRegisterSize, int registerSize) {
+ this.code = code;
+ this.incomingRegisterSize = incomingRegisterSize;
+ this.registerSize = registerSize;
+ }
+
+ @Override
+ public int hashCode() {
+ return code.hashCode() + incomingRegisterSize * 13 + registerSize * 17;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof DexWritableCodeKey)) {
+ return false;
+ }
+ DexWritableCodeKey that = (DexWritableCodeKey) other;
+ return code.equals(that.code)
+ && incomingRegisterSize == that.incomingRegisterSize
+ && registerSize == that.registerSize;
+ }
+ }
+
+ class AmendedDexWritableCodeKey<S> extends DexWritableCodeKey {
+ private final S extra;
+
+ public AmendedDexWritableCodeKey(
+ DexWritableCode code, S extra, int incomingRegisterSize, int registerSize) {
+ super(code, incomingRegisterSize, registerSize);
+ this.extra = extra;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() + extra.hashCode() * 7;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof AmendedDexWritableCodeKey)) {
+ return false;
+ }
+ AmendedDexWritableCodeKey that = (AmendedDexWritableCodeKey) other;
+ return super.equals(other) && extra.equals(that.extra);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldArrayBacking.java b/src/main/java/com/android/tools/r8/graph/FieldArrayBacking.java
index 4b782b3..dee7446 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldArrayBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldArrayBacking.java
@@ -17,6 +17,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -69,18 +70,48 @@
}
@Override
- TraversalContinuation<?, ?> traverse(Function<DexEncodedField, TraversalContinuation<?, ?>> fn) {
- for (int i = 0; i < staticFields.length; i++) {
- if (fn.apply(staticFields[i]).shouldBreak()) {
- return TraversalContinuation.doBreak();
+ <BT, CT> TraversalContinuation<BT, CT> traverse(
+ DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) {
+ TraversalContinuation<BT, CT> traversalContinuation = TraversalContinuation.doContinue();
+ for (DexEncodedField definition : staticFields) {
+ DexClassAndField field = DexClassAndField.create(holder, definition);
+ traversalContinuation = fn.apply(field);
+ if (traversalContinuation.shouldBreak()) {
+ return traversalContinuation;
}
}
- for (int i = 0; i < instanceFields.length; i++) {
- if (fn.apply(instanceFields[i]).shouldBreak()) {
- return TraversalContinuation.doBreak();
+ for (DexEncodedField definition : instanceFields) {
+ DexClassAndField field = DexClassAndField.create(holder, definition);
+ traversalContinuation = fn.apply(field);
+ if (traversalContinuation.shouldBreak()) {
+ return traversalContinuation;
}
}
- return TraversalContinuation.doContinue();
+ return traversalContinuation;
+ }
+
+ @Override
+ <BT, CT> TraversalContinuation<BT, CT> traverse(
+ DexClass holder,
+ BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
+ CT initialValue) {
+ TraversalContinuation<BT, CT> traversalContinuation =
+ TraversalContinuation.doContinue(initialValue);
+ for (DexEncodedField definition : staticFields) {
+ DexClassAndField field = DexClassAndField.create(holder, definition);
+ traversalContinuation = fn.apply(field, traversalContinuation.asContinue().getValue());
+ if (traversalContinuation.shouldBreak()) {
+ return traversalContinuation;
+ }
+ }
+ for (DexEncodedField definition : instanceFields) {
+ DexClassAndField field = DexClassAndField.create(holder, definition);
+ traversalContinuation = fn.apply(field, traversalContinuation.asContinue().getValue());
+ if (traversalContinuation.shouldBreak()) {
+ return traversalContinuation;
+ }
+ }
+ return traversalContinuation;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/FieldCollection.java b/src/main/java/com/android/tools/r8/graph/FieldCollection.java
index e2cf122..729b238 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldCollection.java
@@ -8,6 +8,7 @@
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
+import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -49,8 +50,8 @@
return backing.size();
}
- public void forEachField(Consumer<DexEncodedField> fn) {
- backing.traverse(
+ public void forEachField(Consumer<DexClassAndField> fn) {
+ traverse(
field -> {
fn.accept(field);
return TraversalContinuation.doContinue();
@@ -61,6 +62,17 @@
return backing.fields(predicate);
}
+ public <BT, CT> TraversalContinuation<BT, CT> traverse(
+ Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) {
+ return backing.traverse(holder, fn);
+ }
+
+ public <BT, CT> TraversalContinuation<BT, CT> traverse(
+ BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
+ CT initialValue) {
+ return backing.traverse(holder, fn, initialValue);
+ }
+
public boolean verify() {
forEachField(
field -> {
@@ -70,6 +82,11 @@
return true;
}
+ private boolean verifyCorrectnessOfFieldHolder(DexClassAndField field) {
+ assert verifyCorrectnessOfFieldHolder(field.getDefinition());
+ return true;
+ }
+
private boolean verifyCorrectnessOfFieldHolder(DexEncodedField field) {
assert field.getHolderType() == holder.type
: "Expected field `"
@@ -163,18 +180,13 @@
public List<DexEncodedField> allFieldsSorted() {
List<DexEncodedField> sorted = new ArrayList<>(size());
- forEachField(sorted::add);
+ forEachField(field -> sorted.add(field.getDefinition()));
sorted.sort(Comparator.comparing(DexEncodedMember::getReference));
return sorted;
}
public boolean hasAnnotations() {
- return backing
- .traverse(
- field ->
- field.hasAnnotations()
- ? TraversalContinuation.doBreak()
- : TraversalContinuation.doContinue())
+ return traverse(field -> TraversalContinuation.breakIf(field.getDefinition().hasAnnotations()))
.shouldBreak();
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldCollectionBacking.java b/src/main/java/com/android/tools/r8/graph/FieldCollectionBacking.java
index 570996f..522748e 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldCollectionBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldCollectionBacking.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.utils.TraversalContinuation;
import java.util.Collection;
import java.util.List;
+import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -25,8 +26,13 @@
// Traversal methods.
- abstract TraversalContinuation<?, ?> traverse(
- Function<DexEncodedField, TraversalContinuation<?, ?>> fn);
+ abstract <BT, CT> TraversalContinuation<BT, CT> traverse(
+ DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn);
+
+ abstract <BT, CT> TraversalContinuation<BT, CT> traverse(
+ DexClass holder,
+ BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
+ CT initialValue);
// Collection methods.
diff --git a/src/main/java/com/android/tools/r8/graph/FieldMapBacking.java b/src/main/java/com/android/tools/r8/graph/FieldMapBacking.java
index 3bac4e0..6b74673 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldMapBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldMapBacking.java
@@ -11,6 +11,7 @@
import java.util.Collections;
import java.util.List;
import java.util.SortedMap;
+import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -46,14 +47,34 @@
}
@Override
- TraversalContinuation<?, ?> traverse(Function<DexEncodedField, TraversalContinuation<?, ?>> fn) {
- for (DexEncodedField field : fieldMap.values()) {
- TraversalContinuation<?, ?> result = fn.apply(field);
- if (result.shouldBreak()) {
- return result;
+ <BT, CT> TraversalContinuation<BT, CT> traverse(
+ DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) {
+ TraversalContinuation<BT, CT> traversalContinuation = TraversalContinuation.doContinue();
+ for (DexEncodedField definition : fieldMap.values()) {
+ DexClassAndField field = DexClassAndField.create(holder, definition);
+ traversalContinuation = fn.apply(field);
+ if (traversalContinuation.shouldBreak()) {
+ return traversalContinuation;
}
}
- return TraversalContinuation.doContinue();
+ return traversalContinuation;
+ }
+
+ @Override
+ <BT, CT> TraversalContinuation<BT, CT> traverse(
+ DexClass holder,
+ BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
+ CT initialValue) {
+ TraversalContinuation<BT, CT> traversalContinuation =
+ TraversalContinuation.doContinue(initialValue);
+ for (DexEncodedField definition : fieldMap.values()) {
+ DexClassAndField field = DexClassAndField.create(holder, definition);
+ traversalContinuation = fn.apply(field, traversalContinuation.asContinue().getValue());
+ if (traversalContinuation.shouldBreak()) {
+ return traversalContinuation;
+ }
+ }
+ return traversalContinuation;
}
@Override
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 f1122b8..d669d502 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignature.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignature.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.GenericSignature.ClassSignature.ClassSignatureBuilder;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -17,6 +18,7 @@
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
import java.util.function.Predicate;
/**
@@ -207,35 +209,44 @@
public static class ClassSignature implements DexDefinitionSignature<DexClass> {
private static final ClassSignature NO_CLASS_SIGNATURE =
- new ClassSignature(EMPTY_TYPE_PARAMS, NO_FIELD_TYPE_SIGNATURE, EMPTY_SUPER_INTERFACES);
+ new ClassSignature(EMPTY_TYPE_PARAMS, null, EMPTY_SUPER_INTERFACES);
- final List<FormalTypeParameter> formalTypeParameters;
- final ClassTypeSignature superClassSignature;
- final List<ClassTypeSignature> superInterfaceSignatures;
+ public static ClassSignature noSignature() {
+ return NO_CLASS_SIGNATURE;
+ }
+
+ private final List<FormalTypeParameter> formalTypeParameters;
+ private final ClassTypeSignature superClassSignatureOrNullForObject;
+ private final List<ClassTypeSignature> superInterfaceSignatures;
ClassSignature(
List<FormalTypeParameter> formalTypeParameters,
ClassTypeSignature superClassSignature,
List<ClassTypeSignature> superInterfaceSignatures) {
assert formalTypeParameters != null;
- assert superClassSignature != null;
assert superInterfaceSignatures != null;
this.formalTypeParameters = formalTypeParameters;
- this.superClassSignature = superClassSignature;
+ this.superClassSignatureOrNullForObject = superClassSignature;
this.superInterfaceSignatures = superInterfaceSignatures;
}
- public ClassTypeSignature superClassSignature() {
- return superClassSignature;
+ public ClassTypeSignature getSuperClassSignatureOrNull() {
+ return superClassSignatureOrNullForObject;
}
- public List<ClassTypeSignature> superInterfaceSignatures() {
+ public ClassTypeSignature getSuperClassSignatureOrObject(DexItemFactory factory) {
+ return superClassSignatureOrNullForObject != null
+ ? superClassSignatureOrNullForObject
+ : new ClassTypeSignature(factory.objectType);
+ }
+
+ public List<ClassTypeSignature> getSuperInterfaceSignatures() {
return superInterfaceSignatures;
}
@Override
public boolean hasSignature() {
- return this != NO_CLASS_SIGNATURE;
+ return this != noSignature();
}
@Override
@@ -258,21 +269,32 @@
return formalTypeParameters;
}
- public ClassSignature visit(GenericSignatureVisitor visitor) {
+ public ClassSignature visit(GenericSignatureVisitor visitor, DexItemFactory factory) {
if (hasNoSignature()) {
return this;
}
List<FormalTypeParameter> rewrittenParameters =
visitor.visitFormalTypeParameters(formalTypeParameters);
- ClassTypeSignature rewrittenSuperClass = visitor.visitSuperClass(superClassSignature);
+ ClassTypeSignature rewrittenSuperClass =
+ visitor.visitSuperClass(superClassSignatureOrNullForObject);
List<ClassTypeSignature> rewrittenInterfaces =
visitor.visitSuperInterfaces(superInterfaceSignatures);
if (formalTypeParameters == rewrittenParameters
- && superClassSignature == rewrittenSuperClass
+ && superClassSignatureOrNullForObject == rewrittenSuperClass
&& superInterfaceSignatures == rewrittenInterfaces) {
return this;
}
- return new ClassSignature(rewrittenParameters, rewrittenSuperClass, rewrittenInterfaces);
+ return ClassSignature.builder()
+ .addFormalTypeParameters(rewrittenParameters)
+ .setSuperClassSignature(rewrittenSuperClass)
+ .addSuperInterfaceSignatures(rewrittenInterfaces)
+ .build(factory);
+ }
+
+ public void visitWithoutRewrite(GenericSignatureVisitor visitor) {
+ visitor.visitFormalTypeParameters(formalTypeParameters);
+ visitor.visitSuperClass(superClassSignatureOrNullForObject);
+ visitor.visitSuperInterfaces(superInterfaceSignatures);
}
public String toRenamedString(NamingLens namingLens, Predicate<DexType> isTypeMissing) {
@@ -290,14 +312,12 @@
return toRenamedString(NamingLens.getIdentityLens(), alwaysTrue());
}
- public static ClassSignature noSignature() {
- return NO_CLASS_SIGNATURE;
- }
-
- public List<FieldTypeSignature> getGenericArgumentsToSuperType(DexType type) {
+ public List<FieldTypeSignature> getGenericArgumentsToSuperType(
+ DexType type, DexItemFactory factory) {
assert hasSignature();
- if (superClassSignature.type == type) {
- return superClassSignature.typeArguments;
+ ClassTypeSignature superClassSig = getSuperClassSignatureOrObject(factory);
+ if (superClassSig.type == type) {
+ return superClassSig.typeArguments;
}
for (ClassTypeSignature superInterfaceSignature : superInterfaceSignatures) {
if (superInterfaceSignature.type == type) {
@@ -319,6 +339,11 @@
private ClassSignatureBuilder() {}
+ public ClassSignatureBuilder addFormalTypeParameter(FormalTypeParameter formal) {
+ formalTypeParameters.add(formal);
+ return this;
+ }
+
public ClassSignatureBuilder addFormalTypeParameters(List<FormalTypeParameter> formals) {
formalTypeParameters.addAll(formals);
return this;
@@ -329,12 +354,32 @@
return this;
}
- public ClassSignatureBuilder addInterface(ClassTypeSignature iface) {
+ public ClassSignatureBuilder addSuperInterfaceSignature(ClassTypeSignature iface) {
superInterfaceSignatures.add(iface);
return this;
}
- public ClassSignature build() {
+ public ClassSignatureBuilder addSuperInterfaceSignatures(List<ClassTypeSignature> ifaces) {
+ superInterfaceSignatures.addAll(ifaces);
+ return this;
+ }
+
+ public ClassSignature build(DexItemFactory factory) {
+ // Any trivial super class signature is always represented by the null value.
+ if (superClassSignature != null) {
+ if (superClassSignature.type() == factory.objectType) {
+ assert !superClassSignature.hasTypeVariableArguments();
+ superClassSignature = null;
+ } else if (superClassSignature.hasNoSignature()) {
+ superClassSignature = null;
+ }
+ }
+ // Any trivial class signature is represented by the "no signature" singleton.
+ if (superClassSignature == null
+ && formalTypeParameters.isEmpty()
+ && superInterfaceSignatures.isEmpty()) {
+ return ClassSignature.noSignature();
+ }
return new ClassSignature(
formalTypeParameters, superClassSignature, superInterfaceSignatures);
}
@@ -346,7 +391,7 @@
private final String genericSignatureString;
InvalidClassSignature(String genericSignatureString) {
- super(EMPTY_TYPE_PARAMS, NO_FIELD_TYPE_SIGNATURE, EMPTY_SUPER_INTERFACES);
+ super(EMPTY_TYPE_PARAMS, null, EMPTY_SUPER_INTERFACES);
this.genericSignatureString = genericSignatureString;
}
@@ -367,12 +412,17 @@
}
@Override
- public ClassSignature visit(GenericSignatureVisitor visitor) {
+ public ClassSignature visit(GenericSignatureVisitor visitor, DexItemFactory factory) {
assert false : "Should not visit an invalid signature";
return this;
}
@Override
+ public void visitWithoutRewrite(GenericSignatureVisitor visitor) {
+ assert false : "Should not visit an invalid signature";
+ }
+
+ @Override
public boolean isInvalid() {
return true;
}
@@ -943,7 +993,7 @@
DexItemFactory factory,
DiagnosticsHandler diagnosticsHandler) {
if (signature == null || signature.isEmpty()) {
- return ClassSignature.NO_CLASS_SIGNATURE;
+ return ClassSignature.noSignature();
}
Parser parser = new Parser(factory);
try {
@@ -951,7 +1001,7 @@
} catch (GenericSignatureFormatError e) {
diagnosticsHandler.warning(
GenericSignatureFormatDiagnostic.invalidClassSignature(signature, className, origin, e));
- return ClassSignature.NO_CLASS_SIGNATURE;
+ return ClassSignature.noSignature();
}
}
@@ -1100,34 +1150,28 @@
private ClassSignature parseClassSignature() {
// ClassSignature ::= FormalTypeParameters? SuperclassSignature SuperinterfaceSignature*.
-
- List<FormalTypeParameter> formalTypeParameters = parseOptFormalTypeParameters();
-
+ ClassSignatureBuilder signatureBuilder = ClassSignature.builder();
+ parseOptFormalTypeParameters(signatureBuilder::addFormalTypeParameter);
// SuperclassSignature ::= ClassTypeSignature.
- ClassTypeSignature superClassSignature = parseClassTypeSignature();
-
- ImmutableList.Builder<ClassTypeSignature> builder = ImmutableList.builder();
+ signatureBuilder.setSuperClassSignature(parseClassTypeSignature());
while (symbol > 0) {
// SuperinterfaceSignature ::= ClassTypeSignature.
- builder.add(parseClassTypeSignature());
+ signatureBuilder.addSuperInterfaceSignature(parseClassTypeSignature());
}
-
- return new ClassSignature(formalTypeParameters, superClassSignature, builder.build());
+ return signatureBuilder.build(factory);
}
- private List<FormalTypeParameter> parseOptFormalTypeParameters() {
+ private void parseOptFormalTypeParameters(Consumer<FormalTypeParameter> consumer) {
// FormalTypeParameters ::= "<" FormalTypeParameter+ ">".
if (symbol != '<') {
- return EMPTY_TYPE_PARAMS;
+ return;
}
scanSymbol();
- ImmutableList.Builder<FormalTypeParameter> builder = ImmutableList.builder();
while ((symbol != '>') && (symbol > 0)) {
- builder.add(updateFormalTypeParameter());
+ consumer.accept(updateFormalTypeParameter());
}
expect('>');
- return builder.build();
}
private FormalTypeParameter updateFormalTypeParameter() {
@@ -1288,7 +1332,8 @@
private MethodTypeSignature parseMethodTypeSignature() {
// MethodTypeSignature ::=
// FormalTypeParameters? "(" TypeSignature* ")" ReturnType ThrowsSignature*.
- List<FormalTypeParameter> formalTypeParameters = parseOptFormalTypeParameters();
+ ImmutableList.Builder<FormalTypeParameter> formalsBuilder = ImmutableList.builder();
+ parseOptFormalTypeParameters(formalsBuilder::add);
expect('(');
@@ -1315,7 +1360,7 @@
}
return new MethodTypeSignature(
- formalTypeParameters,
+ formalsBuilder.build(),
parameterSignatureBuilder.build(),
returnType,
throwsSignatureBuilder.build());
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
index 7864ee8..8bac211 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureCorrectnessHelper.java
@@ -250,26 +250,24 @@
return VALID;
}
SignatureEvaluationResult signatureEvaluationResult =
- evaluateFormalTypeParameters(classSignature.formalTypeParameters, typeParameterContext);
+ evaluateFormalTypeParameters(
+ classSignature.getFormalTypeParameters(), typeParameterContext);
if (signatureEvaluationResult.isInvalid()) {
return signatureEvaluationResult;
}
- if (context.superType == appView.dexItemFactory().objectType
- && classSignature.superClassSignature().hasNoSignature()) {
- // We represent no signature as object.
- } else if (context.superType != classSignature.superClassSignature().type()) {
+ ClassTypeSignature superClassSignature =
+ classSignature.getSuperClassSignatureOrObject(appView.dexItemFactory());
+ if (context.superType != superClassSignature.type()) {
assert mode.doNotVerify() : "Super type inconsistency in generic signature";
return INVALID_SUPER_TYPE;
}
signatureEvaluationResult =
evaluateTypeArgumentsAppliedToType(
- classSignature.superClassSignature().typeArguments(),
- context.superType,
- typeParameterContext);
+ superClassSignature.typeArguments(), context.superType, typeParameterContext);
if (signatureEvaluationResult.isInvalid()) {
return signatureEvaluationResult;
}
- List<ClassTypeSignature> superInterfaces = classSignature.superInterfaceSignatures();
+ List<ClassTypeSignature> superInterfaces = classSignature.getSuperInterfaceSignatures();
if (context.interfaces.size() != superInterfaces.size()) {
assert mode.doNotVerify();
return INVALID_INTERFACE_COUNT;
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignaturePartialTypeArgumentApplier.java b/src/main/java/com/android/tools/r8/graph/GenericSignaturePartialTypeArgumentApplier.java
index 12ca041..503000f 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignaturePartialTypeArgumentApplier.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignaturePartialTypeArgumentApplier.java
@@ -70,7 +70,7 @@
if (classSignature.hasNoSignature() || classSignature.isInvalid()) {
return classSignature;
}
- return classSignature.visit(this);
+ return classSignature.visit(this, appView.dexItemFactory());
}
@Override
@@ -206,8 +206,11 @@
}
@Override
- public ClassTypeSignature visitSuperClass(ClassTypeSignature classTypeSignature) {
- return classTypeSignature.visit(this);
+ public ClassTypeSignature visitSuperClass(ClassTypeSignature classTypeSignatureOrNullForObject) {
+ if (classTypeSignatureOrNullForObject == null) {
+ return classTypeSignatureOrNullForObject;
+ }
+ return classTypeSignatureOrNullForObject.visit(this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java b/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
index 9d54705..cff0d21 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignaturePrinter.java
@@ -31,7 +31,8 @@
@Override
public ClassSignature visitClassSignature(ClassSignature classSignature) {
- return classSignature.visit(this);
+ classSignature.visitWithoutRewrite(this);
+ return classSignature;
}
@Override
@@ -108,9 +109,13 @@
}
@Override
- public ClassTypeSignature visitSuperClass(ClassTypeSignature classTypeSignature) {
- printFieldTypeSignature(classTypeSignature, false);
- return classTypeSignature;
+ public ClassTypeSignature visitSuperClass(ClassTypeSignature classTypeSignatureOrNullForObject) {
+ if (classTypeSignatureOrNullForObject == null) {
+ sb.append("Ljava/lang/Object;");
+ } else {
+ printFieldTypeSignature(classTypeSignatureOrNullForObject, false);
+ }
+ return classTypeSignatureOrNullForObject;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
index 7f224bb..cdbc19b 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeRewriter.java
@@ -61,7 +61,7 @@
if (classSignature.hasNoSignature() || classSignature.isInvalid()) {
return classSignature;
}
- return new GenericSignatureRewriter().visitClassSignature(classSignature);
+ return new GenericSignatureRewriter(factory).visitClassSignature(classSignature);
}
public FieldTypeSignature rewrite(FieldTypeSignature fieldTypeSignature) {
@@ -69,7 +69,7 @@
return fieldTypeSignature;
}
FieldTypeSignature rewrittenSignature =
- new GenericSignatureRewriter().visitFieldTypeSignature(fieldTypeSignature);
+ new GenericSignatureRewriter(factory).visitFieldTypeSignature(fieldTypeSignature);
return rewrittenSignature == null ? FieldTypeSignature.noSignature() : rewrittenSignature;
}
@@ -77,20 +77,20 @@
if (methodTypeSignature.hasNoSignature() || methodTypeSignature.isInvalid()) {
return methodTypeSignature;
}
- return new GenericSignatureRewriter().visitMethodSignature(methodTypeSignature);
+ return new GenericSignatureRewriter(factory).visitMethodSignature(methodTypeSignature);
}
private class GenericSignatureRewriter implements GenericSignatureVisitor {
+ private final DexItemFactory factory;
+
+ GenericSignatureRewriter(DexItemFactory factory) {
+ this.factory = factory;
+ }
+
@Override
public ClassSignature visitClassSignature(ClassSignature classSignature) {
- ClassSignature rewritten = classSignature.visit(this);
- if (rewritten.getFormalTypeParameters().isEmpty()
- && rewritten.superInterfaceSignatures.isEmpty()
- && rewritten.superClassSignature.type == factory.objectType) {
- return ClassSignature.noSignature();
- }
- return rewritten;
+ return classSignature.visit(this, factory);
}
@Override
@@ -142,14 +142,12 @@
}
@Override
- public ClassTypeSignature visitSuperClass(ClassTypeSignature classTypeSignature) {
- if (context.superType == factory.objectType) {
- return classTypeSignature.type == factory.objectType
- ? classTypeSignature
- : objectTypeSignature;
+ public ClassTypeSignature visitSuperClass(
+ ClassTypeSignature classTypeSignatureOrNullForObject) {
+ if (classTypeSignatureOrNullForObject == null) {
+ return classTypeSignatureOrNullForObject;
}
- ClassTypeSignature rewritten = classTypeSignature.visit(this);
- return rewritten == null ? objectTypeSignature : rewritten;
+ return classTypeSignatureOrNullForObject.visit(this);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVisitor.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVisitor.java
index a619129..66e9682 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVisitor.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureTypeVisitor.java
@@ -30,7 +30,8 @@
if (classSignature.hasNoSignature()) {
return classSignature;
}
- return classSignature.visit(this);
+ classSignature.visitWithoutRewrite(this);
+ return classSignature;
}
@Override
@@ -87,16 +88,16 @@
}
@Override
- public ClassTypeSignature visitSuperClass(ClassTypeSignature classTypeSignature) {
- return classTypeSignature.visit(this);
+ public ClassTypeSignature visitSuperClass(ClassTypeSignature classTypeSignatureOrNullForObject) {
+ if (classTypeSignatureOrNullForObject == null) {
+ return classTypeSignatureOrNullForObject;
+ }
+ return classTypeSignatureOrNullForObject.visit(this);
}
@Override
public List<ClassTypeSignature> visitSuperInterfaces(
List<ClassTypeSignature> interfaceSignatures) {
- if (interfaceSignatures == null) {
- return null;
- }
interfaceSignatures.forEach(this::visitSuperInterface);
return interfaceSignatures;
}
diff --git a/src/main/java/com/android/tools/r8/graph/GenericSignatureVisitor.java b/src/main/java/com/android/tools/r8/graph/GenericSignatureVisitor.java
index 37b3cbe..4072b82 100644
--- a/src/main/java/com/android/tools/r8/graph/GenericSignatureVisitor.java
+++ b/src/main/java/com/android/tools/r8/graph/GenericSignatureVisitor.java
@@ -49,7 +49,7 @@
throw new Unreachable("Implement if visited");
}
- default ClassTypeSignature visitSuperClass(ClassTypeSignature classTypeSignature) {
+ default ClassTypeSignature visitSuperClass(ClassTypeSignature classTypeSignatureOrNullForObject) {
throw new Unreachable("Implement if visited");
}
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 8cb3ed0..067e0ae 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -899,7 +899,13 @@
parameterNames = new ArrayList<>(parameterCount);
parameterFlags = new ArrayList<>(parameterCount);
}
- parameterNames.add(new DexValueString(parent.application.getFactory().createString(name)));
+ if (name == null) {
+ // The JVM spec defines a null entry as valid and indicating no parameter name.
+ // https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.24
+ parameterNames.add(DexValueNull.NULL);
+ } else {
+ parameterNames.add(new DexValueString(parent.application.getFactory().createString(name)));
+ }
parameterFlags.add(DexValueInt.create(access));
super.visitParameter(name, access);
}
diff --git a/src/main/java/com/android/tools/r8/graph/ProgramField.java b/src/main/java/com/android/tools/r8/graph/ProgramField.java
index 85146d6..e564a79 100644
--- a/src/main/java/com/android/tools/r8/graph/ProgramField.java
+++ b/src/main/java/com/android/tools/r8/graph/ProgramField.java
@@ -6,6 +6,9 @@
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.kotlin.KotlinFieldLevelInfo;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepFieldInfo;
+import com.android.tools.r8.utils.InternalOptions;
public class ProgramField extends DexClassAndField
implements ProgramMember<DexEncodedField, DexField> {
@@ -23,6 +26,24 @@
}
}
+ @Override
+ public boolean isEffectivelyFinal(AppView<AppInfoWithLiveness> appView) {
+ FieldAccessFlags accessFlags = getAccessFlags();
+ FieldAccessInfo accessInfo =
+ appView.appInfo().getFieldAccessInfoCollection().get(getReference());
+ KeepFieldInfo keepInfo = appView.getKeepInfo(this);
+ InternalOptions options = appView.options();
+ return keepInfo.isOptimizationAllowed(options)
+ && keepInfo.isShrinkingAllowed(options)
+ && !accessInfo.hasReflectiveWrite()
+ && !accessInfo.isWrittenFromMethodHandle()
+ && accessInfo.isWrittenOnlyInMethodSatisfying(
+ method ->
+ method.getDefinition().isInitializer()
+ && method.getAccessFlags().isStatic() == accessFlags.isStatic()
+ && method.getHolder() == getHolder());
+ }
+
public boolean isStructurallyEqualTo(ProgramField other) {
return getDefinition() == other.getDefinition() && getHolder() == other.getHolder();
}
diff --git a/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java b/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java
index 383504d..19d78c1 100644
--- a/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java
+++ b/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java
@@ -235,6 +235,12 @@
}
@Override
+ public DexWritableCacheKey getCacheLookupKey(ProgramMethod method, DexItemFactory factory) {
+ return new AmendedDexWritableCodeKey<DexType>(
+ this, exceptionType, getIncomingRegisterSize(method), getRegisterSize(method));
+ }
+
+ @Override
public String toString() {
return "ThrowExceptionCode";
}
diff --git a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
index 82f320c..707e45a 100644
--- a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
+++ b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
@@ -274,6 +274,12 @@
return "ThrowNullCode";
}
+ @Override
+ public DexWritableCacheKey getCacheLookupKey(ProgramMethod method, DexItemFactory factory) {
+ return new AmendedDexWritableCodeKey<DexWritableCode>(
+ this, this, getIncomingRegisterSize(method), getRegisterSize(method));
+ }
+
static class ThrowNullSourceCode extends SyntheticStraightLineSourceCode {
ThrowNullSourceCode(ProgramMethod method) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
index 33ab115..f15e404 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/ValueMayDependOnEnvironmentAnalysis.java
@@ -8,8 +8,9 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
@@ -32,6 +33,7 @@
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.info.initializer.InstanceInitializerInfo;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.WorkList;
@@ -79,12 +81,12 @@
*/
public class ValueMayDependOnEnvironmentAnalysis {
- private final AppView<?> appView;
+ private final AppView<AppInfoWithLiveness> appView;
private final ProgramMethod context;
private final DexItemFactory dexItemFactory;
private final InternalOptions options;
- public ValueMayDependOnEnvironmentAnalysis(AppView<?> appView, IRCode code) {
+ public ValueMayDependOnEnvironmentAnalysis(AppView<AppInfoWithLiveness> appView, IRCode code) {
this.appView = appView;
this.context = code.context();
this.dexItemFactory = appView.dexItemFactory();
@@ -319,21 +321,31 @@
// Find the single constructor invocation.
InvokeDirect constructorInvoke = newInstance.getUniqueConstructorInvoke(dexItemFactory);
- if (constructorInvoke == null || constructorInvoke.getInvokedMethod().holder != clazz.type) {
- // Didn't find a (valid) constructor invocation, give up.
+ if (constructorInvoke == null) {
+ // Didn't find a constructor invocation, give up.
return false;
}
// Check that it is a trivial initializer (otherwise, the constructor could do anything).
- DexEncodedMethod constructor = clazz.lookupMethod(constructorInvoke.getInvokedMethod());
+ DexClassAndMethod constructor =
+ appView
+ .appInfo()
+ .resolveMethod(
+ constructorInvoke.getInvokedMethod(), constructorInvoke.getInterfaceBit())
+ .getResolutionPair();
if (constructor == null) {
return false;
}
+ if (!options.canInitNewInstanceUsingSuperclassConstructor()
+ && constructor.getHolder() != clazz) {
+ return false;
+ }
+
InstanceInitializerInfo initializerInfo =
constructor.getOptimizationInfo().getInstanceInitializerInfo(constructorInvoke);
- List<DexEncodedField> fields = clazz.getDirectAndIndirectInstanceFields(appView);
+ List<DexClassAndField> fields = clazz.getDirectAndIndirectInstanceFields(appView);
if (!fields.isEmpty()) {
if (initializerInfo.instanceFieldInitializationMayDependOnEnvironment()) {
return false;
@@ -348,8 +360,8 @@
// Mark this value as mutable if it has a non-final field.
boolean hasNonFinalField = false;
- for (DexEncodedField field : fields) {
- if (!field.isFinal()) {
+ for (DexClassAndField field : fields) {
+ if (!field.getAccessFlags().isFinal()) {
hasNonFinalField = true;
break;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index 0db5825..0781787 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -77,7 +77,7 @@
private final Map<DexEncodedField, FieldState> fieldStates = new ConcurrentHashMap<>();
private final Map<DexProgramClass, Map<DexEncodedField, AbstractValue>>
- abstractInstanceFieldValues = new ConcurrentHashMap<>();
+ abstractFinalInstanceFieldValues = new ConcurrentHashMap<>();
FieldAssignmentTracker(AppView<AppInfoWithLiveness> appView) {
this.abstractValueFactory = appView.abstractValueFactory();
@@ -97,8 +97,8 @@
* For each class with known allocation sites, adds a mapping from clazz -> instance field ->
* bottom.
*
- * <p>If an entry (clazz, instance field) is missing in {@link #abstractInstanceFieldValues}, it
- * is interpreted as if we known nothing about the value of the field.
+ * <p>If an entry (clazz, instance field) is missing in {@link #abstractFinalInstanceFieldValues},
+ * it is interpreted as if we known nothing about the value of the field.
*/
private void initializeAbstractInstanceFieldValues() {
FieldAccessInfoCollection<?> fieldAccessInfos =
@@ -116,15 +116,21 @@
// No instance fields to track.
return;
}
- Map<DexEncodedField, AbstractValue> abstractInstanceFieldValuesForClass =
+ Map<DexEncodedField, AbstractValue> abstractFinalInstanceFieldValuesForClass =
new IdentityHashMap<>();
- for (DexEncodedField field : clazz.instanceFields()) {
- FieldAccessInfo fieldAccessInfo = fieldAccessInfos.get(field.getReference());
- if (fieldAccessInfo != null && !fieldAccessInfo.hasReflectiveAccess()) {
- abstractInstanceFieldValuesForClass.put(field, BottomValue.getInstance());
- }
+ clazz.forEachProgramInstanceField(
+ field -> {
+ if (field.isFinalOrEffectivelyFinal(appView)) {
+ FieldAccessInfo fieldAccessInfo = fieldAccessInfos.get(field.getReference());
+ if (fieldAccessInfo != null && !fieldAccessInfo.hasReflectiveAccess()) {
+ abstractFinalInstanceFieldValuesForClass.put(
+ field.getDefinition(), BottomValue.getInstance());
+ }
+ }
+ });
+ if (!abstractFinalInstanceFieldValuesForClass.isEmpty()) {
+ abstractFinalInstanceFieldValues.put(clazz, abstractFinalInstanceFieldValuesForClass);
}
- abstractInstanceFieldValues.put(clazz, abstractInstanceFieldValuesForClass);
});
for (DexProgramClass clazz : appView.appInfo().classes()) {
clazz.forEachProgramField(
@@ -237,7 +243,7 @@
void recordAllocationSite(NewInstance instruction, DexProgramClass clazz, ProgramMethod context) {
Map<DexEncodedField, AbstractValue> abstractInstanceFieldValuesForClass =
- abstractInstanceFieldValues.get(clazz);
+ abstractFinalInstanceFieldValues.get(clazz);
if (abstractInstanceFieldValuesForClass == null) {
// We are not tracking the value of any of clazz' instance fields.
return;
@@ -246,14 +252,14 @@
InvokeDirect invoke = instruction.getUniqueConstructorInvoke(dexItemFactory);
if (invoke == null) {
// We just lost track.
- abstractInstanceFieldValues.remove(clazz);
+ abstractFinalInstanceFieldValues.remove(clazz);
return;
}
DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
if (singleTarget == null) {
// We just lost track.
- abstractInstanceFieldValues.remove(clazz);
+ abstractFinalInstanceFieldValues.remove(clazz);
return;
}
@@ -349,11 +355,11 @@
assert WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, field.getType())
== dynamicType;
if (dynamicType.isNotNullType()) {
- feedback.markFieldHasDynamicType(field.getDefinition(), dynamicType);
+ feedback.markFieldHasDynamicType(field, dynamicType);
} else {
DynamicTypeWithUpperBound staticType = field.getType().toDynamicType(appView);
if (dynamicType.asDynamicTypeWithUpperBound().strictlyLessThan(staticType, appView)) {
- feedback.markFieldHasDynamicType(field.getDefinition(), dynamicType);
+ feedback.markFieldHasDynamicType(field, dynamicType);
}
}
}
@@ -408,7 +414,7 @@
private void recordAllAllocationsSitesProcessed(
DexProgramClass clazz, OptimizationFeedbackDelayed feedback) {
Map<DexEncodedField, AbstractValue> abstractInstanceFieldValuesForClass =
- abstractInstanceFieldValues.get(clazz);
+ abstractFinalInstanceFieldValues.get(clazz);
if (abstractInstanceFieldValuesForClass == null) {
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
index 99707bf..c35c35e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/AbstractFieldSet.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.lens.GraphLens;
@@ -45,7 +46,7 @@
return null;
}
- public abstract boolean contains(DexEncodedField field);
+ public abstract boolean contains(DexClassAndField field);
public abstract boolean isEmpty();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
index b18d02a..41ffebf 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/ConcreteMutableFieldSet.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.PrunedItems;
@@ -72,6 +73,11 @@
}
@Override
+ public boolean contains(DexClassAndField field) {
+ return contains(field.getDefinition());
+ }
+
+ @Override
public AbstractFieldSet fixupReadSetAfterParametersChanged(
AppView<AppInfoWithLiveness> appView, ArgumentInfoCollection argumentInfoCollection) {
assert !isEmpty();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
index e4efd96..6151aa1 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/EmptyFieldSet.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.lens.GraphLens;
@@ -38,6 +39,11 @@
}
@Override
+ public boolean contains(DexClassAndField field) {
+ return false;
+ }
+
+ @Override
public boolean isBottom() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
index 9c09ef2..ab97348 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/FieldValueAnalysis.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexValue;
@@ -26,12 +27,13 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.DequeUtils;
import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.collections.DexClassAndFieldMap;
+import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
public abstract class FieldValueAnalysis {
@@ -55,7 +57,8 @@
private DominatorTree dominatorTree;
private Map<BasicBlock, AbstractFieldSet> fieldsMaybeReadBeforeBlockInclusiveCache;
- final Map<DexEncodedField, List<FieldInitializationInfo>> putsPerField = new IdentityHashMap<>();
+ final DexClassAndFieldMap<List<FieldInitializationInfo>> putsPerField =
+ DexClassAndFieldMap.create();
FieldValueAnalysis(
AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) {
@@ -95,16 +98,16 @@
return null;
}
- abstract boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field);
+ abstract boolean isSubjectToOptimizationIgnoringPinning(DexClassAndField field);
- abstract boolean isSubjectToOptimization(DexEncodedField field);
+ abstract boolean isSubjectToOptimization(DexClassAndField field);
- void recordFieldPut(DexEncodedField field, Instruction instruction) {
+ void recordFieldPut(DexClassAndField field, Instruction instruction) {
recordFieldPut(field, instruction, UnknownInstanceFieldInitializationInfo.getInstance());
}
void recordFieldPut(
- DexEncodedField field, Instruction instruction, InstanceFieldInitializationInfo info) {
+ DexClassAndField field, Instruction instruction, InstanceFieldInitializationInfo info) {
putsPerField
.computeIfAbsent(field, ignore -> new ArrayList<>())
.add(new FieldInitializationInfo(instruction, info));
@@ -116,24 +119,19 @@
// Find all the static-put instructions that assign a field in the enclosing class which is
// guaranteed to be assigned only in the current initializer.
- boolean isStraightLineCode = true;
- for (BasicBlock block : code.blocks) {
- if (block.getSuccessors().size() >= 2) {
- isStraightLineCode = false;
- }
+ for (BasicBlock block : code.getBlocks()) {
for (Instruction instruction : block.getInstructions()) {
if (instruction.isFieldPut()) {
FieldInstruction fieldPut = instruction.asFieldInstruction();
- DexField field = fieldPut.getField();
- ProgramField programField = appInfo.resolveField(field).getProgramField();
- if (programField != null) {
- DexEncodedField encodedField = programField.getDefinition();
- if (isSubjectToOptimization(encodedField)) {
- recordFieldPut(encodedField, fieldPut);
+ DexField fieldReference = fieldPut.getField();
+ ProgramField field = appInfo.resolveField(fieldReference).getProgramField();
+ if (field != null) {
+ if (isSubjectToOptimization(field)) {
+ recordFieldPut(field, fieldPut);
} else if (isStaticFieldValueAnalysis()
- && programField.getHolder().isEnum()
- && isSubjectToOptimizationIgnoringPinning(encodedField)) {
- recordFieldPut(encodedField, fieldPut);
+ && field.getHolder().isEnum()
+ && isSubjectToOptimizationIgnoringPinning(field)) {
+ recordFieldPut(field, fieldPut);
}
}
} else if (isInstanceFieldValueAnalysis()
@@ -144,51 +142,52 @@
}
}
+ boolean isStraightLineCode =
+ Iterables.all(code.getBlocks(), block -> block.getSuccessors().size() <= 1);
List<BasicBlock> normalExitBlocks = code.computeNormalExitBlocks();
- for (Entry<DexEncodedField, List<FieldInitializationInfo>> entry : putsPerField.entrySet()) {
- DexEncodedField field = entry.getKey();
- List<FieldInitializationInfo> fieldPuts = entry.getValue();
- if (fieldPuts.size() > 1) {
- continue;
- }
- FieldInitializationInfo info = ListUtils.first(fieldPuts);
- Instruction instruction = info.instruction;
- if (instruction.isInvokeDirect()) {
- asInstanceFieldValueAnalysis()
- .recordInstanceFieldIsInitializedWithInfo(field, info.instanceFieldInitializationInfo);
- continue;
- }
- FieldInstruction fieldPut = instruction.asFieldInstruction();
- if (!isStraightLineCode) {
- if (!getOrCreateDominatorTree().dominatesAllOf(fieldPut.getBlock(), normalExitBlocks)) {
- continue;
- }
- }
- boolean priorReadsWillReadSameValue =
- !classInitializerDefaultsResult.hasStaticValue(field) && fieldPut.value().isZero();
- if (!priorReadsWillReadSameValue && fieldMaybeReadBeforeInstruction(field, fieldPut)) {
- // TODO(b/172528424): Generalize to InstanceFieldValueAnalysis.
- if (isStaticFieldValueAnalysis()) {
- // At this point the value read in the field can be only the default static value, if read
- // prior to the put, or the value put, if read after the put. We still want to record it
- // because the default static value is typically null/0, so code present after a null/0
- // check can take advantage of the optimization.
- DexValue valueBeforePut = classInitializerDefaultsResult.getStaticValue(field);
- asStaticFieldValueAnalysis()
- .updateFieldOptimizationInfoWith2Values(field, fieldPut.value(), valueBeforePut);
- }
- continue;
- }
- updateFieldOptimizationInfo(field, fieldPut, fieldPut.value());
- }
+ putsPerField.forEach(
+ (field, fieldPuts) -> {
+ if (fieldPuts.size() > 1) {
+ return;
+ }
+ FieldInitializationInfo info = ListUtils.first(fieldPuts);
+ Instruction instruction = info.instruction;
+ if (instruction.isInvokeDirect()) {
+ asInstanceFieldValueAnalysis()
+ .recordInstanceFieldIsInitializedWithInfo(
+ field, info.instanceFieldInitializationInfo);
+ return;
+ }
+ FieldInstruction fieldPut = instruction.asFieldInstruction();
+ if (!isStraightLineCode) {
+ if (!getOrCreateDominatorTree().dominatesAllOf(fieldPut.getBlock(), normalExitBlocks)) {
+ return;
+ }
+ }
+ boolean priorReadsWillReadSameValue =
+ !classInitializerDefaultsResult.hasStaticValue(field) && fieldPut.value().isZero();
+ if (!priorReadsWillReadSameValue && fieldMaybeReadBeforeInstruction(field, fieldPut)) {
+ // TODO(b/172528424): Generalize to InstanceFieldValueAnalysis.
+ if (isStaticFieldValueAnalysis()) {
+ // At this point the value read in the field can be only the default static value, if
+ // read prior to the put, or the value put, if read after the put. We still want to
+ // record it because the default static value is typically null/0, so code present
+ // after a null/0 check can take advantage of the optimization.
+ DexValue valueBeforePut = classInitializerDefaultsResult.getStaticValue(field);
+ asStaticFieldValueAnalysis()
+ .updateFieldOptimizationInfoWith2Values(field, fieldPut.value(), valueBeforePut);
+ }
+ return;
+ }
+ updateFieldOptimizationInfo(field, fieldPut, fieldPut.value());
+ });
}
- private boolean fieldMaybeReadBeforeInstruction(
- DexEncodedField encodedField, Instruction instruction) {
+ private boolean fieldMaybeReadBeforeInstruction(DexClassAndField field, Instruction instruction) {
BasicBlock block = instruction.getBlock();
// First check if the field may be read in any of the (transitive) predecessor blocks.
- if (fieldMaybeReadBeforeBlock(encodedField, block)) {
+ if (fieldMaybeReadBeforeBlock(field, block)) {
return true;
}
@@ -200,7 +199,7 @@
if (current == instruction) {
break;
}
- if (current.readSet(appView, context).contains(encodedField)) {
+ if (current.readSet(appView, context).contains(field)) {
return true;
}
}
@@ -209,18 +208,17 @@
return false;
}
- private boolean fieldMaybeReadBeforeBlock(DexEncodedField encodedField, BasicBlock block) {
+ private boolean fieldMaybeReadBeforeBlock(DexClassAndField field, BasicBlock block) {
for (BasicBlock predecessor : block.getPredecessors()) {
- if (fieldMaybeReadBeforeBlockInclusive(encodedField, predecessor)) {
+ if (fieldMaybeReadBeforeBlockInclusive(field, predecessor)) {
return true;
}
}
return false;
}
- private boolean fieldMaybeReadBeforeBlockInclusive(
- DexEncodedField encodedField, BasicBlock block) {
- return getOrCreateFieldsMaybeReadBeforeBlockInclusive().get(block).contains(encodedField);
+ private boolean fieldMaybeReadBeforeBlockInclusive(DexClassAndField field, BasicBlock block) {
+ return getOrCreateFieldsMaybeReadBeforeBlockInclusive().get(block).contains(field);
}
/**
@@ -327,5 +325,5 @@
}
abstract void updateFieldOptimizationInfo(
- DexEncodedField field, FieldInstruction fieldPut, Value value);
+ DexClassAndField field, FieldInstruction fieldPut, Value value);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
index c4061ec..8b00bfb 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/InstanceFieldValueAnalysis.java
@@ -8,8 +8,8 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.code.IRCodeUtils;
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
@@ -119,17 +120,17 @@
}
@Override
- boolean isSubjectToOptimization(DexEncodedField field) {
- return !field.isStatic() && field.getHolderType() == context.getHolderType();
+ boolean isSubjectToOptimization(DexClassAndField field) {
+ return !field.getAccessFlags().isStatic() && field.getHolderType() == context.getHolderType();
}
@Override
- boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field) {
+ boolean isSubjectToOptimizationIgnoringPinning(DexClassAndField field) {
throw new Unreachable("Used by static analysis only.");
}
@Override
- void updateFieldOptimizationInfo(DexEncodedField field, FieldInstruction fieldPut, Value value) {
+ void updateFieldOptimizationInfo(DexClassAndField field, FieldInstruction fieldPut, Value value) {
if (fieldNeverWrittenBetweenInstancePutAndMethodExit(field, fieldPut.asInstancePut())) {
recordInstanceFieldIsInitializedWithValue(field, value);
}
@@ -154,7 +155,7 @@
.getOptimizationInfo()
.getInstanceInitializerInfo(invoke)
.fieldInitializationInfos();
- for (DexEncodedField field :
+ for (DexClassAndField field :
singleTarget.getHolder().getDirectAndIndirectInstanceFields(appView)) {
InstanceFieldInitializationInfo info = infos.get(field);
if (info.isArgumentInitializationInfo()) {
@@ -193,7 +194,7 @@
}
private InstanceFieldInitializationInfo getInstanceFieldInitializationInfo(
- DexEncodedField field, Value value) {
+ DexClassAndField field, Value value) {
Value root = value.getAliasedValue();
if (root.isDefinedByInstructionSatisfying(Instruction::isArgument)) {
Argument argument = root.definition.asArgument();
@@ -203,7 +204,7 @@
if (abstractValue.isSingleValue()) {
return abstractValue.asSingleValue();
}
- DexType fieldType = field.type();
+ DexType fieldType = field.getType();
if (fieldType.isClassType()) {
ClassTypeElement dynamicLowerBoundType = value.getDynamicLowerBoundType(appView);
TypeElement dynamicUpperBoundType = value.getDynamicUpperBoundType(appView);
@@ -216,20 +217,21 @@
}
void recordInstanceFieldIsInitializedWithInfo(
- DexEncodedField field, InstanceFieldInitializationInfo info) {
- if (!info.isUnknown()) {
+ DexClassAndField field, InstanceFieldInitializationInfo info) {
+ if (!info.isUnknown()
+ && appView.appInfo().mayPropagateValueFor(appView, field.getReference())) {
builder.recordInitializationInfo(field, info);
}
}
- void recordInstanceFieldIsInitializedWithValue(DexEncodedField field, Value value) {
+ void recordInstanceFieldIsInitializedWithValue(DexClassAndField field, Value value) {
recordInstanceFieldIsInitializedWithInfo(
field, getInstanceFieldInitializationInfo(field, value));
}
private boolean fieldNeverWrittenBetweenInstancePutAndMethodExit(
- DexEncodedField field, InstancePut instancePut) {
- if (field.isFinal()) {
+ DexClassAndField field, InstancePut instancePut) {
+ if (field.getAccessFlags().isFinal()) {
return true;
}
@@ -238,7 +240,7 @@
}
if (appView.appInfo().isInstanceFieldWrittenOnlyInInstanceInitializers(field)) {
- if (parentConstructorCall.getInvokedMethod().holder != context.getHolderType()) {
+ if (parentConstructorCall.getInvokedMethod().getHolderType() != context.getHolderType()) {
// The field is only written in instance initializers of the enclosing class, and the
// constructor call targets a constructor in the super class.
return true;
@@ -268,13 +270,27 @@
throw new Unreachable();
}
+ // Analyze all subsequent instructions.
+ // TODO(b/279877113): Extend this analysis to analyze the full remainder of this method.
+ if (instancePut.getBlock().getSuccessors().isEmpty()) {
+ InstructionListIterator instructionIterator =
+ instancePut.getBlock().listIterator(code, instancePut);
+ while (instructionIterator.hasNext()) {
+ Instruction instruction = instructionIterator.next();
+ if (instruction.readSet(appView, context).contains(field)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
// Otherwise, conservatively return false.
return false;
}
private boolean fieldNeverWrittenBetweenParentConstructorCallAndMethodExit(
- DexEncodedField field) {
- if (field.isFinal()) {
+ DexClassAndField field) {
+ if (field.isFinalOrEffectivelyFinal(appView)) {
return true;
}
if (appView.appInfo().isFieldOnlyWrittenInMethod(field, parentConstructor.getDefinition())) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/KnownFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/KnownFieldSet.java
index 263101b..83859a1 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/KnownFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/KnownFieldSet.java
@@ -4,12 +4,15 @@
package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexEncodedField;
public interface KnownFieldSet {
boolean contains(DexEncodedField field);
+ boolean contains(DexClassAndField field);
+
default boolean isConcreteFieldSet() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
index 4df6b75..cc62515 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValueAnalysis.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
@@ -113,19 +114,20 @@
} else {
assert false : value.getClass().getName();
}
- });
+ },
+ appView);
}
@Override
- boolean isSubjectToOptimization(DexEncodedField field) {
- return field.isStatic()
+ boolean isSubjectToOptimization(DexClassAndField field) {
+ return field.getAccessFlags().isStatic()
&& field.getHolderType() == context.getHolderType()
&& appView.appInfo().isFieldOnlyWrittenInMethod(field, context.getDefinition());
}
@Override
- boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field) {
- return field.isStatic()
+ boolean isSubjectToOptimizationIgnoringPinning(DexClassAndField field) {
+ return field.getAccessFlags().isStatic()
&& field.getHolderType() == context.getHolderType()
&& appView
.appInfo()
@@ -133,13 +135,13 @@
}
@Override
- void updateFieldOptimizationInfo(DexEncodedField field, FieldInstruction fieldPut, Value value) {
+ void updateFieldOptimizationInfo(DexClassAndField field, FieldInstruction fieldPut, Value value) {
AbstractValue abstractValue = getOrComputeAbstractValue(value, field);
updateFieldOptimizationInfo(field, value, abstractValue, false);
}
void updateFieldOptimizationInfo(
- DexEncodedField field, Value value, AbstractValue abstractValue, boolean maybeNull) {
+ DexClassAndField field, Value value, AbstractValue abstractValue, boolean maybeNull) {
builder.recordStaticField(field, abstractValue, appView.dexItemFactory());
// We cannot modify FieldOptimizationInfo of pinned fields.
@@ -165,7 +167,7 @@
}
public void updateFieldOptimizationInfoWith2Values(
- DexEncodedField field, Value valuePut, DexValue valueBeforePut) {
+ DexClassAndField field, Value valuePut, DexValue valueBeforePut) {
// We are interested in the AbstractValue only if it's null or a value, so we can use the value
// if the code is protected by a null check.
if (valueBeforePut != DexValueNull.NULL) {
@@ -177,7 +179,7 @@
updateFieldOptimizationInfo(field, valuePut, abstractValue, true);
}
- private AbstractValue getOrComputeAbstractValue(Value value, DexEncodedField field) {
+ private AbstractValue getOrComputeAbstractValue(Value value, DexClassAndField field) {
Value root = value.getAliasedValue();
AbstractValue abstractValue = root.getAbstractValue(appView, context);
if (!abstractValue.isSingleValue()) {
@@ -186,7 +188,7 @@
return abstractValue;
}
- private SingleFieldValue computeSingleFieldValue(DexEncodedField field, Value value) {
+ private SingleFieldValue computeSingleFieldValue(DexClassAndField field, Value value) {
assert !value.hasAliasedValue();
SingleFieldValue result = computeSingleEnumFieldValue(value);
if (result != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java
index 8e5b4ca..0b9e480 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexProgramClass;
@@ -32,7 +32,7 @@
public abstract static class Builder {
public abstract void recordStaticField(
- DexEncodedField staticField, AbstractValue value, DexItemFactory factory);
+ DexClassAndField staticField, AbstractValue value, DexItemFactory factory);
public abstract StaticFieldValues build();
}
@@ -69,7 +69,7 @@
@Override
public void recordStaticField(
- DexEncodedField staticField, AbstractValue value, DexItemFactory factory) {
+ DexClassAndField staticField, AbstractValue value, DexItemFactory factory) {
if (factory.enumMembers.isValuesFieldCandidate(staticField, staticField.getHolderType())) {
if (value.isSingleFieldValue()
&& value.asSingleFieldValue().getObjectState().isEnumValuesObjectState()) {
@@ -131,7 +131,7 @@
@Override
public void recordStaticField(
- DexEncodedField staticField, AbstractValue value, DexItemFactory factory) {
+ DexClassAndField staticField, AbstractValue value, DexItemFactory factory) {
// Do nothing.
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
index 6131581..09e336e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/UnknownFieldSet.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.ir.analysis.fieldvalueanalysis;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.graph.proto.ArgumentInfoCollection;
@@ -22,7 +22,7 @@
}
@Override
- public boolean contains(DexEncodedField field) {
+ public boolean contains(DexClassAndField field) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java b/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java
index 9a61590..95d68a4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/modeling/LibraryMethodReadSetModeling.java
@@ -28,6 +28,14 @@
return EmptyFieldSet.getInstance();
}
+ // Model that checkNotNullParameter() does not read any instance fields of the app. This is
+ // currently needed for constructors that call checkNotNullParameter() not to be marked as
+ // reading any field.
+ if (invokedMethod == appView.dexItemFactory().kotlin.intrinsics.checkNotNullParameter
+ || invokedMethod == appView.dexItemFactory().kotlin.intrinsics.checkParameterIsNotNull) {
+ return EmptyFieldSet.getInstance();
+ }
+
// Already handled above.
assert !appView.dexItemFactory().classMethods.isReflectiveNameLookup(invokedMethod);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/EnumLiteProtoShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/EnumLiteProtoShrinker.java
index e527832..75e4631 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/EnumLiteProtoShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/EnumLiteProtoShrinker.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
@@ -84,8 +84,8 @@
if (clazz.getInterfaces().contains(references.enumLiteMapType)) {
DexProgramClass enumLite = computeCorrespondingEnumLite(clazz);
if (enumLite != null) {
- DexEncodedField field =
- enumLite.lookupField(createInternalValueMapField(enumLite.getType()));
+ DexClassAndField field =
+ enumLite.lookupClassField(createInternalValueMapField(enumLite.getType()));
if (field == null) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
index 2ff901b..c6658c4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteBuilderShrinker.java
@@ -43,11 +43,11 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.Enqueuer;
import com.android.tools.r8.shaking.EnqueuerWorklist;
+import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.ObjectUtils;
import com.android.tools.r8.utils.PredicateSet;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.IdentityHashMap;
@@ -220,11 +220,8 @@
assert builder.superType == references.generatedMessageLiteBuilderType
|| builder.superType == references.generatedMessageLiteExtendableBuilderType;
- DexField defaultInstanceField = references.getDefaultInstanceField(dynamicMethod.getHolder());
Value builderValue =
code.createValue(ClassTypeElement.create(builder.superType, definitelyNotNull(), appView));
- Value defaultInstanceValue =
- code.createValue(ClassTypeElement.create(defaultInstanceField.type, maybeNull(), appView));
// Replace `new Message.Builder()` by `new GeneratedMessageLite.Builder()`
// (or `new GeneratedMessageLite.ExtendableBuilder()`).
@@ -239,23 +236,47 @@
//
// We may also see an accessibility bridge constructor, because the Builder constructor is
// private. The accessibility bridge takes null as an argument.
+ DexField defaultInstanceField = references.getDefaultInstanceField(dynamicMethod.getHolder());
+ Box<Value> existingDefaultInstanceValue = new Box<>();
InvokeDirect constructorInvoke =
instructionIterator.nextUntil(
instruction -> {
+ // After constructor inlining we may see a load of the DEFAULT_INSTANCE field.
+ if (instruction.isStaticGet()) {
+ StaticGet staticGet = instruction.asStaticGet();
+ if (staticGet.getField() == defaultInstanceField) {
+ existingDefaultInstanceValue.set(staticGet.outValue());
+ return false;
+ }
+ }
assert instruction.isInvokeDirect() || instruction.isConstNumber();
return instruction.isInvokeDirect();
});
assert constructorInvoke != null;
- instructionIterator.replaceCurrentInstruction(
- new StaticGet(defaultInstanceValue, defaultInstanceField));
- instructionIterator.setInsertionPosition(constructorInvoke.getPosition());
- instructionIterator.add(
- new InvokeDirect(
- builder.superType == references.generatedMessageLiteBuilderType
- ? references.generatedMessageLiteBuilderMethods.constructorMethod
- : references.generatedMessageLiteExtendableBuilderMethods.constructorMethod,
- null,
- ImmutableList.of(builderValue, defaultInstanceValue)));
+
+ DexMethod constructorMethod =
+ builder.superType == references.generatedMessageLiteBuilderType
+ ? references.generatedMessageLiteBuilderMethods.constructorMethod
+ : references.generatedMessageLiteExtendableBuilderMethods.constructorMethod;
+ if (existingDefaultInstanceValue.isSet()) {
+ instructionIterator.replaceCurrentInstruction(
+ InvokeDirect.builder()
+ .setArguments(builderValue, existingDefaultInstanceValue.get())
+ .setMethod(constructorMethod)
+ .build());
+ } else {
+ Value defaultInstanceValue =
+ code.createValue(
+ ClassTypeElement.create(defaultInstanceField.type, maybeNull(), appView));
+ instructionIterator.replaceCurrentInstruction(
+ new StaticGet(defaultInstanceValue, defaultInstanceField));
+ instructionIterator.setInsertionPosition(constructorInvoke.getPosition());
+ instructionIterator.add(
+ InvokeDirect.builder()
+ .setArguments(builderValue, defaultInstanceValue)
+ .setMethod(constructorMethod)
+ .build());
+ }
converter.removeDeadCodeAndFinalizeIR(
code, OptimizationFeedbackSimple.getInstance(), Timing.empty());
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
index ca151c5..809f0ad 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -14,9 +14,11 @@
import com.android.tools.r8.graph.AccessControl;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMember;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoMessageInfo;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoObject;
@@ -110,6 +112,16 @@
}
});
}
+
+ DexProgramClass rawMessageInfoClass =
+ asProgramClassOrNull(
+ appView.appInfo().definitionForWithoutExistenceAssert(references.rawMessageInfoType));
+ if (rawMessageInfoClass != null) {
+ disallowOptimization(
+ rawMessageInfoClass, references.rawMessageInfoInfoField, dependentMinimumKeepInfo);
+ disallowOptimization(
+ rawMessageInfoClass, references.rawMessageInfoObjectsField, dependentMinimumKeepInfo);
+ }
}
private void disallowSignatureOptimizations(KeepMethodInfo.Joiner methodJoiner) {
@@ -124,6 +136,18 @@
.disallowUnusedReturnValueOptimization();
}
+ private void disallowOptimization(
+ DexProgramClass clazz,
+ DexMember<?, ?> reference,
+ DependentMinimumKeepInfoCollection dependentMinimumKeepInfo) {
+ ProgramMember<?, ?> member = clazz.lookupProgramMember(reference);
+ if (member != null) {
+ dependentMinimumKeepInfo
+ .getOrCreateUnconditionalMinimumKeepInfoFor(reference)
+ .disallowOptimization();
+ }
+ }
+
public void run(IRCode code) {
ProgramMethod method = code.context();
if (references.isDynamicMethod(method.getReference())) {
@@ -363,7 +387,7 @@
for (Instruction instruction : code.instructions()) {
if (instruction.isInvokeMethod()) {
InvokeMethod invoke = instruction.asInvokeMethod();
- if (references.isMessageInfoConstructionMethod(invoke.getInvokedMethod())) {
+ if (references.isMessageInfoConstruction(invoke)) {
return invoke;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
index 31633de..0b15b9a 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
@@ -13,6 +13,9 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.Value;
import java.util.function.Consumer;
@@ -59,6 +62,8 @@
public final DexMethod dynamicMethod;
public final DexMethod newMessageInfoMethod;
public final DexMethod rawMessageInfoConstructor;
+ public final DexField rawMessageInfoInfoField;
+ public final DexField rawMessageInfoObjectsField;
public ProtoReferences(DexItemFactory factory) {
dexItemFactory = factory;
@@ -122,6 +127,11 @@
factory.voidType, messageLiteType, factory.stringType, factory.objectArrayType),
factory.constructorMethodName);
+ // Fields.
+ rawMessageInfoInfoField = factory.createField(rawMessageInfoType, factory.stringType, "info");
+ rawMessageInfoObjectsField =
+ factory.createField(rawMessageInfoType, factory.objectArrayType, "objects");
+
generatedExtensionMethods = new GeneratedExtensionMethods(factory);
generatedMessageLiteMethods = new GeneratedMessageLiteMethods(factory);
generatedMessageLiteBuilderMethods = new GeneratedMessageLiteBuilderMethods(factory);
@@ -130,6 +140,10 @@
methodToInvokeMembers = new MethodToInvokeMembers(factory);
}
+ public DexItemFactory dexItemFactory() {
+ return dexItemFactory;
+ }
+
public void forEachMethodReference(Consumer<DexMethod> consumer) {
generatedExtensionMethods.forEachMethodReference(consumer);
generatedMessageLiteMethods.forEachMethodReference(consumer);
@@ -195,8 +209,18 @@
&& !isAbstractGeneratedMessageLiteBuilder(clazz);
}
- public boolean isMessageInfoConstructionMethod(DexMethod method) {
- return method.match(newMessageInfoMethod) || method == rawMessageInfoConstructor;
+ public boolean isMessageInfoConstruction(InvokeMethod invoke) {
+ if (invoke.getInvokedMethod().match(newMessageInfoMethod)) {
+ return true;
+ }
+ if (invoke.isInvokeConstructor(dexItemFactory)) {
+ Value receiver = invoke.asInvokeDirect().getReceiver();
+ if (receiver.isDefinedByInstructionSatisfying(Instruction::isNewInstance)) {
+ NewInstance newInstance = receiver.getDefinition().asNewInstance();
+ return newInstance.getType() == rawMessageInfoType;
+ }
+ }
+ return false;
}
public boolean isProtoLibraryClass(DexProgramClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoUtils.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoUtils.java
index 82b4571..b6c1f4d 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoUtils.java
@@ -4,9 +4,11 @@
package com.android.tools.r8.ir.analysis.proto;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.ir.code.InstancePut;
+import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.utils.BooleanUtils;
public class ProtoUtils {
@@ -14,23 +16,74 @@
public static Value getInfoValueFromMessageInfoConstructionInvoke(
InvokeMethod invoke, ProtoReferences references) {
- assert references.isMessageInfoConstructionMethod(invoke.getInvokedMethod());
- int adjustment = BooleanUtils.intValue(invoke.isInvokeDirect());
- return invoke.inValues().get(1 + adjustment).getAliasedValue();
+ assert references.isMessageInfoConstruction(invoke);
+ // First check if there is a call to the static method newMessageInfo(...).
+ if (invoke.getInvokedMethod().match(references.newMessageInfoMethod)) {
+ return invoke.getOperand(1);
+ }
+ // Otherwise, the static method has been inlined. Check if there is a call to
+ // RawMessageInfo.<init>(...).
+ assert invoke.isInvokeDirect();
+ if (invoke.getInvokedMethod() == references.rawMessageInfoConstructor) {
+ return invoke.getOperand(2);
+ }
+ // Otherwise, RawMessageInfo.<init>(...) has been inlined, and we should find a call to
+ // Object.<init>(). In this case, we should find an instance field assignment to
+ // `RawMessageInfo.info`.
+ assert invoke.getInvokedMethod() == references.dexItemFactory().objectMembers.constructor;
+ // Find the value being assigned to the `info` field.
+ for (InstancePut instancePut :
+ invoke.getFirstArgument().<InstancePut>uniqueUsers(Instruction::isInstancePut)) {
+ if (instancePut.getField() == references.rawMessageInfoInfoField) {
+ return instancePut.value();
+ }
+ }
+ throw new Unreachable();
}
static Value getObjectsValueFromMessageInfoConstructionInvoke(
InvokeMethod invoke, ProtoReferences references) {
- assert references.isMessageInfoConstructionMethod(invoke.getInvokedMethod());
- int adjustment = BooleanUtils.intValue(invoke.isInvokeDirect());
- return invoke.inValues().get(2 + adjustment).getAliasedValue();
+ assert references.isMessageInfoConstruction(invoke);
+ if (invoke.getInvokedMethod().match(references.newMessageInfoMethod)) {
+ return invoke.getOperand(2);
+ }
+ assert invoke.isInvokeDirect();
+ if (invoke.getInvokedMethod() == references.rawMessageInfoConstructor) {
+ return invoke.getOperand(3);
+ }
+ assert invoke.getInvokedMethod() == references.dexItemFactory().objectMembers.constructor;
+ // Find the value being assigned to the `info` field.
+ for (InstancePut instancePut :
+ invoke.getFirstArgument().<InstancePut>uniqueUsers(Instruction::isInstancePut)) {
+ if (instancePut.getField() == references.rawMessageInfoObjectsField) {
+ return instancePut.value();
+ }
+ }
+ throw new Unreachable();
}
static void setObjectsValueForMessageInfoConstructionInvoke(
InvokeMethod invoke, Value newObjectsValue, ProtoReferences references) {
- assert references.isMessageInfoConstructionMethod(invoke.getInvokedMethod());
- int adjustment = BooleanUtils.intValue(invoke.isInvokeDirect());
- invoke.replaceValue(2 + adjustment, newObjectsValue);
+ assert references.isMessageInfoConstruction(invoke);
+ if (invoke.getInvokedMethod().match(references.newMessageInfoMethod)) {
+ invoke.replaceValue(2, newObjectsValue);
+ return;
+ }
+ assert invoke.isInvokeDirect();
+ if (invoke.getInvokedMethod() == references.rawMessageInfoConstructor) {
+ invoke.replaceValue(3, newObjectsValue);
+ return;
+ }
+ assert invoke.getInvokedMethod() == references.dexItemFactory().objectMembers.constructor;
+ // Find the value being assigned to the `info` field.
+ for (InstancePut instancePut :
+ invoke.getFirstArgument().<InstancePut>uniqueUsers(Instruction::isInstancePut)) {
+ if (instancePut.getField() == references.rawMessageInfoObjectsField) {
+ instancePut.setValue(newObjectsValue);
+ return;
+ }
+ }
+ throw new Unreachable();
}
public static boolean isProto2(int flags) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
index 00182d3..3cf7971 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/RawMessageInfoDecoder.java
@@ -79,7 +79,7 @@
}
public ProtoMessageInfo run(ProgramMethod dynamicMethod, InvokeMethod invoke) {
- assert references.isMessageInfoConstructionMethod(invoke.getInvokedMethod());
+ assert references.isMessageInfoConstruction(invoke);
Value infoValue = getInfoValueFromMessageInfoConstructionInvoke(invoke, references);
Value objectsValue = getObjectsValueFromMessageInfoConstructionInvoke(invoke, references);
return run(dynamicMethod, infoValue, objectsValue);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
index e84fc5a..61747c0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/schema/ProtoEnqueuerExtension.java
@@ -447,13 +447,18 @@
if (newlyLiveField != null) {
// Mark hazzer and one-of proto fields as read from dynamicMethod() if they are written in
// the app. This is needed to ensure that field writes are not removed from the app.
- ProgramMethod defaultInitializer =
- dynamicMethod.getHolder().getProgramDefaultInitializer();
- assert defaultInitializer != null;
Predicate<ProgramMethod> neitherDefaultConstructorNorDynamicMethod =
- writer ->
- !writer.isStructurallyEqualTo(defaultInitializer)
- && !writer.isStructurallyEqualTo(dynamicMethod);
+ writer -> {
+ if (dynamicMethod.getHolder().hasDefaultInitializer()
+ && writer.isStructurallyEqualTo(
+ dynamicMethod.getHolder().getProgramDefaultInitializer())) {
+ return false;
+ }
+ if (writer.isStructurallyEqualTo(dynamicMethod)) {
+ return false;
+ }
+ return true;
+ };
if (enqueuer.isFieldWrittenInMethodSatisfying(
newlyLiveField, neitherDefaultConstructorNorDynamicMethod)) {
worklist.enqueueTraceReflectiveFieldReadAction(newlyLiveField, dynamicMethod);
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/ObjectState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/ObjectState.java
index 00165e5..3b02fc4 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/ObjectState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/objectstate/ObjectState.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.ProgramMethod;
@@ -80,7 +81,7 @@
private final Map<DexField, AbstractValue> state = new IdentityHashMap<>();
- public void recordFieldHasValue(DexEncodedField field, AbstractValue abstractValue) {
+ public void recordFieldHasValue(DexClassAndField field, AbstractValue abstractValue) {
if (!abstractValue.isUnknown()) {
assert !state.containsKey(field.getReference());
state.put(field.getReference(), abstractValue);
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index eff1adc..483e19d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -190,9 +190,13 @@
@Override
public DeadInstructionResult canBeDeadCode(AppView<?> appView, IRCode code) {
- return instructionInstanceCanThrow(appView, code.context())
- ? DeadInstructionResult.notDead()
- : DeadInstructionResult.deadIfInValueIsDead(array());
+ if (!instructionInstanceCanThrow(appView, code.context())) {
+ Value arrayRoot = array().getAliasedValue();
+ if (arrayRoot.isDefinedByInstructionSatisfying(Instruction::isCreatingArray)) {
+ return DeadInstructionResult.deadIfInValueIsDead(arrayRoot);
+ }
+ }
+ return DeadInstructionResult.notDead();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
index afe8e0b..bc3297f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionListIterator.java
@@ -29,6 +29,7 @@
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
@@ -168,6 +169,14 @@
metadata.record(instruction);
}
+ @Override
+ public void set(Collection<Instruction> instructions) {
+ for (Instruction instruction : instructions) {
+ set(instruction);
+ next();
+ }
+ }
+
/**
* Remove the current instruction (aka the {@link Instruction} returned by the previous call to
* {@link #next}.
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
index cae2002..00b983b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstNumber.java
@@ -54,6 +54,10 @@
return instruction.asConstNumber();
}
+ public static Builder builder() {
+ return new Builder();
+ }
+
@Override
public int opcode() {
return Opcodes.CONST_NUMBER;
@@ -349,4 +353,24 @@
public void buildLir(LirBuilder<Value, ?> builder) {
builder.addConstNumber(outType(), value);
}
+
+ public static class Builder extends BuilderBase<Builder, ConstNumber> {
+
+ private long value;
+
+ public Builder setValue(long value) {
+ this.value = value;
+ return this;
+ }
+
+ @Override
+ public ConstNumber build() {
+ return amend(new ConstNumber(outValue, value));
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/ConstString.java b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
index e2ce753..803223f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ConstString.java
@@ -34,6 +34,10 @@
this.value = value;
}
+ public static Builder builder() {
+ return new Builder();
+ }
+
@Override
public int opcode() {
return Opcodes.CONST_STRING;
@@ -185,4 +189,24 @@
public void buildLir(LirBuilder<Value, ?> builder) {
builder.addConstString(value);
}
+
+ public static class Builder extends BuilderBase<Builder, ConstString> {
+
+ private DexString value;
+
+ public Builder setValue(DexString value) {
+ this.value = value;
+ return this;
+ }
+
+ @Override
+ public ConstString build() {
+ return amend(new ConstString(outValue, value));
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
index 5a3e094..3e8e999 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LirBuilder;
public class DebugLocalRead extends Instruction {
private static final String ERROR_MESSAGE = "Unexpected attempt to emit debug-local read.";
@@ -51,6 +52,11 @@
}
@Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ builder.addDebugLocalRead();
+ }
+
+ @Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.isDebugLocalRead();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
index 3a5623d..51182ac 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DexItemBasedConstString.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LirBuilder;
import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
public class DexItemBasedConstString extends ConstInstruction {
@@ -89,6 +90,11 @@
}
@Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ builder.addDexItemBasedConstString(item, nameComputationInfo);
+ }
+
+ @Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.isDexItemBasedConstString()
&& other.asDexItemBasedConstString().item == item
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 86ef0d7..5fa9853 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
@@ -167,7 +167,7 @@
* since that could change the lifetime of the value.
*/
boolean isStoringObjectWithFinalizer(
- AppView<AppInfoWithLiveness> appView, DexEncodedField field) {
+ AppView<AppInfoWithLiveness> appView, DexClassAndField field) {
assert isFieldPut();
TypeElement type = value().getType();
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
index 8b519ed..611d8dd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionListIterator.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.utils.InternalOptions;
+import java.util.Collection;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.Set;
@@ -227,6 +228,11 @@
}
@Override
+ public void set(Collection<Instruction> instructions) {
+ instructionIterator.set(instructions);
+ }
+
+ @Override
public void replaceCurrentInstruction(Instruction newInstruction, Set<Value> affectedValues) {
instructionIterator.replaceCurrentInstruction(newInstruction, affectedValues);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InitClass.java b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
index 765d4a1..7d1b853 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InitClass.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InitClass.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LirBuilder;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
public class InitClass extends Instruction {
@@ -82,6 +83,11 @@
}
@Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ builder.addInitClass(clazz);
+ }
+
+ @Override
public boolean definitelyTriggersClassInitialization(
DexType clazz,
ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index ad8a742..5a3984c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -155,8 +155,8 @@
return false;
}
- return appInfoWithLiveness.isFieldRead(field.getDefinition())
- || isStoringObjectWithFinalizer(appViewWithLiveness, field.getDefinition());
+ return appInfoWithLiveness.isFieldRead(field)
+ || isStoringObjectWithFinalizer(appViewWithLiveness, field);
}
// In D8, we always have to assume that the field can be read, and thus have side effects.
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index c33be9f..511c157 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -225,6 +225,11 @@
public abstract void buildCf(CfBuilder builder);
+ // TODO(b/225838009): Make this abstract.
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ throw new Unimplemented("Missing impl for " + getClass().getSimpleName());
+ }
+
public void replaceValue(Value oldValue, Value newValue) {
for (int i = 0; i < inValues.size(); i++) {
if (oldValue == inValues.get(i)) {
@@ -1535,10 +1540,6 @@
return false;
}
- public void buildLir(LirBuilder<Value, ?> builder) {
- throw new Unimplemented("Missing impl for " + getClass().getSimpleName());
- }
-
public void registerUse(UseRegistry registry, ProgramMethod context) {
internalRegisterUse(registry, context);
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index 8a69912..96db1b9 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
+import java.util.Collection;
import java.util.ListIterator;
import java.util.Set;
import java.util.function.Consumer;
@@ -83,6 +84,8 @@
*/
void removeOrReplaceByDebugLocalRead();
+ void set(Collection<Instruction> instructions);
+
default boolean hasInsertionPosition() {
return false;
}
@@ -126,6 +129,10 @@
return next();
}
+ default Instruction positionBeforeNextInstruction(Instruction instruction) {
+ return positionBeforeNextInstructionThatMatches(i -> i == instruction);
+ }
+
default Instruction positionBeforeNextInstructionThatMatches(Predicate<Instruction> predicate) {
nextUntil(predicate);
return previous();
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
index 92ecb73..4539f17 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeCustom.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.ir.desugar.LambdaDescriptor;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LirBuilder;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.List;
@@ -157,6 +158,11 @@
}
@Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ builder.addInvokeCustom(getCallSite(), arguments());
+ }
+
+ @Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.isInvokeCustom() && callSite == other.asInvokeCustom().callSite;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index b7a8fae..1871e96 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -37,6 +37,7 @@
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.List;
@@ -282,6 +283,10 @@
protected DexMethod method;
protected List<Value> arguments = Collections.emptyList();
+ public B setArguments(Value... arguments) {
+ return setArguments(Arrays.asList(arguments));
+ }
+
public B setArguments(List<Value> arguments) {
assert arguments != null;
this.arguments = arguments;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
index 47fc4f9..e492bca 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMultiNewArray.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.android.tools.r8.lightir.LirBuilder;
import com.android.tools.r8.utils.LongInterval;
import java.util.List;
@@ -115,6 +116,11 @@
}
@Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ builder.addInvokeMultiNewArray(type, arguments());
+ }
+
+ @Override
public boolean instructionInstanceCanThrow(AppView<?> appView, ProgramMethod context) {
DexType baseType = type.isArrayType() ? type.toBaseType(appView.dexItemFactory()) : type;
if (baseType.isPrimitiveType()) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
index 08b3ab8..5e80e07ef 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokePolymorphic.java
@@ -24,6 +24,7 @@
import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter;
+import com.android.tools.r8.lightir.LirBuilder;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.util.List;
@@ -112,6 +113,11 @@
}
@Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ builder.addInvokePolymorphic(getInvokedMethod(), getProto(), arguments());
+ }
+
+ @Override
public boolean identicalNonValueNonPositionParts(Instruction other) {
return other.isInvokePolymorphic()
&& proto.equals(other.asInvokePolymorphic().proto)
diff --git a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
index 819ba43..d4951ac 100644
--- a/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/LinearFlowInstructionListIterator.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
+import java.util.Collection;
import java.util.ListIterator;
import java.util.Set;
import java.util.function.Consumer;
@@ -292,4 +293,9 @@
public void set(Instruction instruction) {
currentBlockIterator.set(instruction);
}
+
+ @Override
+ public void set(Collection<Instruction> instructions) {
+ currentBlockIterator.set(instructions);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Neg.java b/src/main/java/com/android/tools/r8/ir/code/Neg.java
index dde52df..7b7c683 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Neg.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Neg.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.lightir.LirBuilder;
import java.util.function.Function;
public class Neg extends Unop {
@@ -111,4 +112,9 @@
public void buildCf(CfBuilder builder) {
builder.add(new CfNeg(type), this);
}
+
+ @Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ builder.addNeg(type, source());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
index 123b988..d47c812 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewUnboxedEnumInstance.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.ir.optimize.enums.EnumUnboxer;
import com.android.tools.r8.ir.optimize.enums.EnumUnboxerImpl;
+import com.android.tools.r8.lightir.LirBuilder;
/**
* Special instruction used by {@link EnumUnboxerImpl}.
@@ -166,4 +167,9 @@
void internalRegisterUse(UseRegistry<?> registry, DexClassAndMethod context) {
registry.registerNewUnboxedEnumInstance(clazz);
}
+
+ @Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ builder.addNewUnboxedEnumInstance(clazz, ordinal);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Not.java b/src/main/java/com/android/tools/r8/ir/code/Not.java
index 097ae22..3c6f4c7 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Not.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Not.java
@@ -15,6 +15,7 @@
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.conversion.CfBuilder;
import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.lightir.LirBuilder;
import java.util.function.Function;
public class Not extends Unop {
@@ -105,4 +106,9 @@
// JVM has no Not instruction, they should be replaced by "Load -1, Xor" before building CF.
throw new Unreachable();
}
+
+ @Override
+ public void buildLir(LirBuilder<Value, ?> builder) {
+ builder.addNot(type, source());
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index ed5958c..32b1cb2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -140,8 +140,8 @@
return false;
}
- return appInfoWithLiveness.isFieldRead(field.getDefinition())
- || isStoringObjectWithFinalizer(appViewWithLiveness, field.getDefinition());
+ return appInfoWithLiveness.isFieldRead(field)
+ || isStoringObjectWithFinalizer(appViewWithLiveness, field);
}
// In D8, we always have to assume that the field can be read, and thus have side effects.
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index d7b86fe..fcec4f3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -34,6 +34,7 @@
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.LongInterval;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.SetUtils;
@@ -357,6 +358,10 @@
return uniqueUsers = ImmutableSet.copyOf(users);
}
+ public <T extends Instruction> Iterable<T> uniqueUsers(Predicate<? super Instruction> predicate) {
+ return IterableUtils.filter(uniqueUsers(), predicate);
+ }
+
public boolean hasSingleUniqueUser() {
return uniqueUsers().size() == 1;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/FieldOptimizationFeedback.java b/src/main/java/com/android/tools/r8/ir/conversion/FieldOptimizationFeedback.java
index b5e3b1e..4eb2020 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/FieldOptimizationFeedback.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/FieldOptimizationFeedback.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.conversion;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
@@ -18,10 +19,19 @@
void markFieldAsPropagated(DexEncodedField field);
+ default void markFieldHasDynamicType(DexClassAndField field, DynamicType dynamicType) {
+ markFieldHasDynamicType(field.getDefinition(), dynamicType);
+ }
+
void markFieldHasDynamicType(DexEncodedField field, DynamicType dynamicType);
void markFieldBitsRead(DexEncodedField field, int bitsRead);
+ default void recordFieldHasAbstractValue(
+ DexClassAndField field, AppView<AppInfoWithLiveness> appView, AbstractValue abstractValue) {
+ recordFieldHasAbstractValue(field.getDefinition(), appView, abstractValue);
+ }
+
void recordFieldHasAbstractValue(
DexEncodedField field, AppView<AppInfoWithLiveness> appView, AbstractValue abstractValue);
}
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 a5417c6..431a610 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
@@ -8,6 +8,8 @@
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Code;
+import com.android.tools.r8.graph.DefaultInstanceInitializerCode;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
@@ -24,6 +26,7 @@
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.passes.ParentConstructorHoistingCodeRewriter;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
import com.android.tools.r8.ir.optimize.AssertionErrorTwoArgsConstructorRewriter;
@@ -349,14 +352,13 @@
return onWaveDoneActions != null;
}
- protected void processSimpleSynthesizeMethods(
- List<ProgramMethod> serviceLoadMethods, ExecutorService executorService)
- throws ExecutionException {
+ public void processSimpleSynthesizeMethods(
+ List<ProgramMethod> methods, ExecutorService executorService) throws ExecutionException {
ThreadUtils.processItems(
- serviceLoadMethods, this::processAndFinalizeSimpleSynthesiedMethod, executorService);
+ methods, this::processAndFinalizeSimpleSynthesizedMethod, executorService);
}
- private void processAndFinalizeSimpleSynthesiedMethod(ProgramMethod method) {
+ private void processAndFinalizeSimpleSynthesizedMethod(ProgramMethod method) {
IRCode code = method.buildIR(appView);
assert code != null;
codeRewriter.rewriteMoveResult(code);
@@ -894,6 +896,8 @@
deadCodeRemover.run(code, timing);
+ new ParentConstructorHoistingCodeRewriter(appView).run(context, code, timing);
+
BytecodeMetadataProvider.Builder bytecodeMetadataProviderBuilder =
BytecodeMetadataProvider.builder();
if (appView.enableWholeProgramOptimizations()) {
@@ -949,7 +953,19 @@
}
Code code = method.getDefinition().getCode();
assert !code.isThrowNullCode();
- return code.isDefaultInstanceInitializerCode();
+ if (code.isDefaultInstanceInitializerCode()) {
+ // Passthrough unless the parent constructor may be inlineable.
+ if (options.canInitNewInstanceUsingSuperclassConstructor()) {
+ DexMethod parentConstructorReference =
+ DefaultInstanceInitializerCode.getParentConstructor(method, appView.dexItemFactory());
+ DexClassAndMethod parentConstructor = appView.definitionFor(parentConstructorReference);
+ if (parentConstructor != null && parentConstructor.isProgramMethod()) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
}
// Compute optimization info summary for the current method unless it is pinned
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
index 7db097a..cda8f4e 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
@@ -245,7 +245,7 @@
onWaveDoneActions = Collections.synchronizedList(new ArrayList<>());
}
- private void waveDone(ProgramMethodSet wave, ExecutorService executorService)
+ public void waveDone(ProgramMethodSet wave, ExecutorService executorService)
throws ExecutionException {
delayedOptimizationFeedback.refineAppInfoWithLiveness(appView.appInfo().withLiveness());
delayedOptimizationFeedback.updateVisibleOptimizationInfo();
@@ -256,8 +256,10 @@
}
enumUnboxer.updateEnumUnboxingCandidatesInfo();
assert delayedOptimizationFeedback.noUpdatesLeft();
- onWaveDoneActions.forEach(com.android.tools.r8.utils.Action::execute);
- onWaveDoneActions = null;
+ if (onWaveDoneActions != null) {
+ onWaveDoneActions.forEach(com.android.tools.r8.utils.Action::execute);
+ onWaveDoneActions = null;
+ }
if (!prunedMethodsInWave.isEmpty()) {
appView.pruneItems(
PrunedItems.builder()
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
new file mode 100644
index 0000000..aea343b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/CodeRewriterPass.java
@@ -0,0 +1,47 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion.passes;
+
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+
+public abstract class CodeRewriterPass<T extends AppInfo> {
+
+ final AppView<?> appView;
+ final DexItemFactory dexItemFactory;
+ final InternalOptions options;
+
+ CodeRewriterPass(AppView<?> appView) {
+ this.appView = appView;
+ this.dexItemFactory = appView.dexItemFactory();
+ this.options = appView.options();
+ }
+
+ @SuppressWarnings("unchecked")
+ AppView<? extends T> appView() {
+ return (AppView<? extends T>) appView;
+ }
+
+ public final void run(ProgramMethod method, IRCode code, Timing timing) {
+ timing.time(getTimingId(), () -> run(method, code));
+ }
+
+ public final void run(ProgramMethod method, IRCode code) {
+ if (shouldRewriteCode(method, code)) {
+ rewriteCode(method, code);
+ }
+ }
+
+ abstract String getTimingId();
+
+ abstract void rewriteCode(ProgramMethod method, IRCode code);
+
+ abstract boolean shouldRewriteCode(ProgramMethod method, IRCode code);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/ParentConstructorHoistingCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/ParentConstructorHoistingCodeRewriter.java
new file mode 100644
index 0000000..06e410b
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/ParentConstructorHoistingCodeRewriter.java
@@ -0,0 +1,216 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.conversion.passes;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndMethod;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeDirect;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.KeepMethodInfo;
+import com.android.tools.r8.utils.CollectionUtils;
+import com.android.tools.r8.utils.IterableUtils;
+import com.android.tools.r8.utils.IteratorUtils;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.List;
+
+/**
+ * Pass that attempts to hoist the parent constructor call inside constructors to avoid
+ * instance-puts to the unininitialized this, as this enables more aggressive inlining of
+ * constructors.
+ */
+// TODO(b/278975138): Do not remove verification errors from hoisting.
+public class ParentConstructorHoistingCodeRewriter
+ extends CodeRewriterPass<AppInfoWithClassHierarchy> {
+
+ private List<InvokeDirect> sideEffectFreeConstructorCalls;
+
+ public ParentConstructorHoistingCodeRewriter(AppView<?> appView) {
+ super(appView);
+ }
+
+ @Override
+ String getTimingId() {
+ return "Parent constructor hoisting pass";
+ }
+
+ @Override
+ void rewriteCode(ProgramMethod method, IRCode code) {
+ for (InvokeDirect invoke : getOrComputeSideEffectFreeConstructorCalls(code)) {
+ hoistSideEffectFreeConstructorCall(code, invoke);
+ }
+ }
+
+ private void hoistSideEffectFreeConstructorCall(IRCode code, InvokeDirect invoke) {
+ Deque<Instruction> constants = new ArrayDeque<>();
+ // TODO(b/281975599): This loop would not be needed if we did not have any trivial gotos.
+ while (true) {
+ hoistSideEffectFreeConstructorCallInCurrentBlock(code, invoke, constants);
+ Instruction firstHoistedInstruction = CollectionUtils.getFirstOrDefault(constants, invoke);
+ if (invoke.getBlock().entry() != firstHoistedInstruction
+ || !hoistSideEffectFreeConstructorCallIntoPredecessorBlock(code, invoke, constants)) {
+ break;
+ }
+ }
+ }
+
+ // TODO(b/278975138): Instead of hoisting constructor call one instruction up at a time as a
+ // peephole optimization, consider finding the insertion position and then modifying the IR once.
+ private void hoistSideEffectFreeConstructorCallInCurrentBlock(
+ IRCode code, InvokeDirect invoke, Deque<Instruction> constants) {
+ InstructionListIterator instructionIterator = invoke.getBlock().listIterator(code);
+ instructionIterator.positionBeforeNextInstruction(
+ CollectionUtils.getFirstOrDefault(constants, invoke));
+ while (instructionIterator.hasPrevious()) {
+ Instruction previousInstruction = instructionIterator.previous();
+ if (previousInstruction.isArgument()) {
+ // Cannot hoist the constructor call above the Argument instruction.
+ return;
+ }
+ if (previousInstruction.hasOutValue()
+ && invoke.inValues().contains(previousInstruction.outValue())) {
+ if (previousInstruction.isConstNumber() || previousInstruction.isConstString()) {
+ // Record that the constant instruction should be hoisted along with the constructor call.
+ constants.addFirst(previousInstruction);
+ continue;
+ }
+ // Cannot hoist the constructor call above the definition of a value that is used as an
+ // argument to the constructor call.
+ return;
+ }
+ // Change the instruction order and continue the hoisting.
+ List<Instruction> newInstructionOrder =
+ ImmutableList.<Instruction>builderWithExpectedSize(constants.size() + 2)
+ .addAll(constants)
+ .add(invoke)
+ .add(previousInstruction)
+ .build();
+ instructionIterator.next();
+ instructionIterator.set(newInstructionOrder);
+ IteratorUtils.skip(instructionIterator, -newInstructionOrder.size() - 1);
+ }
+ }
+
+ private boolean hoistSideEffectFreeConstructorCallIntoPredecessorBlock(
+ IRCode code, InvokeDirect invoke, Deque<Instruction> constants) {
+ BasicBlock block = invoke.getBlock();
+ if (!block.hasUniquePredecessor()) {
+ return false;
+ }
+
+ BasicBlock predecessorBlock = block.getUniquePredecessor();
+ if (!predecessorBlock.hasUniqueSuccessor() || predecessorBlock.hasCatchHandlers()) {
+ return false;
+ }
+
+ // Remove the constants and the invoke from the block.
+ constants.forEach(constant -> block.getInstructions().removeFirst());
+ block.getInstructions().removeFirst();
+
+ // Add the constants and the invoke before the exit instruction in the predecessor block.
+ InstructionListIterator predecessorInstructionIterator =
+ predecessorBlock.listIterator(code, predecessorBlock.getInstructions().size() - 1);
+ constants.forEach(predecessorInstructionIterator::add);
+ predecessorInstructionIterator.add(invoke);
+
+ // Position the predecessor instruction iterator right before the first instruction that is
+ // subject to hoisting.
+ IteratorUtils.skip(predecessorInstructionIterator, -constants.size() - 1);
+ assert predecessorInstructionIterator.peekNext()
+ == CollectionUtils.getFirstOrDefault(constants, invoke);
+ return true;
+ }
+
+ /** Only run this when the rewriting may actually enable more constructor inlining. */
+ @Override
+ boolean shouldRewriteCode(ProgramMethod method, IRCode code) {
+ if (!appView.hasClassHierarchy()) {
+ assert !appView.enableWholeProgramOptimizations();
+ return false;
+ }
+ if (!method.getDefinition().isInstanceInitializer()
+ || !options.canInitNewInstanceUsingSuperclassConstructor()) {
+ return false;
+ }
+ KeepMethodInfo keepInfo = appView.getKeepInfo(method);
+ return keepInfo.isOptimizationAllowed(options)
+ && keepInfo.isShrinkingAllowed(options)
+ && hoistingMayRemoveInstancePutToUninitializedThis(code);
+ }
+
+ private boolean hoistingMayRemoveInstancePutToUninitializedThis(IRCode code) {
+ if (!code.metadata().mayHaveInstancePut()) {
+ return false;
+ }
+ Value thisValue = code.getThis();
+ WorkList<BasicBlock> worklist = WorkList.newIdentityWorkList();
+ for (InvokeDirect invoke : getOrComputeSideEffectFreeConstructorCalls(code)) {
+ // Check if any of the previous instructions in the current block has an instance-put that
+ // assigns a field on `this`.
+ if (IterableUtils.anyBefore(
+ invoke.getBlock().getInstructions(),
+ instruction -> isInstancePutToUninitializedThis(instruction, thisValue),
+ instruction -> instruction == invoke)) {
+ return true;
+ }
+ // Otherwise check if any of the (transitive) predecessor blocks has an instance-put that
+ // assigns a field on `this`.
+ worklist.addIfNotSeen(invoke.getBlock().getPredecessors());
+ }
+ return worklist
+ .run(
+ block -> {
+ if (Iterables.any(
+ block.getInstructions(),
+ instruction -> isInstancePutToUninitializedThis(instruction, thisValue))) {
+ return TraversalContinuation.doBreak();
+ }
+ worklist.addIfNotSeen(block.getPredecessors());
+ return TraversalContinuation.doContinue();
+ })
+ .shouldBreak();
+ }
+
+ private List<InvokeDirect> getOrComputeSideEffectFreeConstructorCalls(IRCode code) {
+ if (sideEffectFreeConstructorCalls == null) {
+ sideEffectFreeConstructorCalls = computeSideEffectFreeConstructorCalls(code);
+ }
+ return sideEffectFreeConstructorCalls;
+ }
+
+ private List<InvokeDirect> computeSideEffectFreeConstructorCalls(IRCode code) {
+ Value thisValue = code.getThis();
+ return ListUtils.filter(
+ thisValue.uniqueUsers(),
+ instruction -> {
+ if (!instruction.isInvokeConstructor(dexItemFactory)) {
+ return false;
+ }
+ InvokeDirect invoke = instruction.asInvokeDirect();
+ if (invoke.getReceiver() != thisValue) {
+ return false;
+ }
+ DexClassAndMethod target = invoke.lookupSingleTarget(appView, code.context());
+ return target != null
+ && !target.getOptimizationInfo().mayHaveSideEffects(invoke, options);
+ });
+ }
+
+ private static boolean isInstancePutToUninitializedThis(
+ Instruction instruction, Value thisValue) {
+ return instruction.isInstancePut() && instruction.asInstancePut().object() == thisValue;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
index 98b707f..be8dd14 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterPostProcessor.java
@@ -128,7 +128,8 @@
continue;
}
clazz.addExtraInterfaces(
- Collections.singletonList(new ClassTypeSignature(newInterface.type)));
+ Collections.singletonList(new ClassTypeSignature(newInterface.type)),
+ appView.dexItemFactory());
eventConsumer.acceptInterfaceInjection(clazz, newInterface);
DexMethod itfMethod =
syntheticHelper.emulatedInterfaceDispatchMethod(newInterface, descriptor);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 922fd4d..f69890a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -529,7 +529,7 @@
eventConsumer.acceptEmulatedInterfaceMarkerInterface(
clazz, helper.ensureEmulatedInterfaceMarkerInterface(signature.type()));
}
- clazz.addExtraInterfaces(extraInterfaceSignatures);
+ clazz.addExtraInterfaces(extraInterfaceSignatures, appView.dexItemFactory());
}
});
}
@@ -698,6 +698,7 @@
// TODO(b/182329331): Only handle type arguments for Cf to Cf desugar.
if (appView.options().isCfDesugaring() && clazz.validInterfaceSignatures()) {
clazz.forEachImmediateSupertypeWithSignature(
+ appView.dexItemFactory(),
(type, signature) -> {
if (emulatesInterfaces.contains(type)) {
extraInterfaceSignatures.put(
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 47a6d30..d33fcec 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
@@ -90,7 +90,7 @@
false,
emulatedInterface.getChecksumSupplier());
newEmulatedInterface.addExtraInterfaces(
- getRewrittenInterfacesOfEmulatedInterface(emulatedInterface));
+ getRewrittenInterfacesOfEmulatedInterface(emulatedInterface), appView.dexItemFactory());
return newEmulatedInterface;
}
@@ -106,7 +106,7 @@
typeArguments = Collections.emptyList();
} else {
GenericSignature.ClassTypeSignature classTypeSignature =
- classSignature.superInterfaceSignatures().get(i);
+ classSignature.getSuperInterfaceSignatures().get(i);
assert itf == classTypeSignature.type();
typeArguments = classTypeSignature.typeArguments();
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
index 4e982d8..5cd5230 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
@@ -442,16 +442,23 @@
Collection<? extends ProgramDefinition> contexts) {
DexItemFactory factory = appView.dexItemFactory();
checkRecordTagNotPresent(factory);
+ return ensureRecordClassHelper(appView, contexts, eventConsumer);
+ }
+
+ public static DexProgramClass ensureRecordClassHelper(
+ AppView<?> appView,
+ Collection<? extends ProgramDefinition> contexts,
+ RecordDesugaringEventConsumer eventConsumer) {
return appView
.getSyntheticItems()
.ensureGlobalClass(
() -> new MissingGlobalSyntheticsConsumerDiagnostic("Record desugaring"),
kinds -> kinds.RECORD_TAG,
- factory.recordType,
+ appView.dexItemFactory().recordType,
contexts,
appView,
builder -> {
- DexEncodedMethod init = synthesizeRecordInitMethod();
+ DexEncodedMethod init = synthesizeRecordInitMethod(appView);
builder.setAbstract().setDirectMethods(ImmutableList.of(init));
},
eventConsumer::acceptRecordClass);
@@ -532,14 +539,16 @@
return factory.objectMembers.toString;
}
- private DexEncodedMethod synthesizeRecordInitMethod() {
+ private static DexEncodedMethod synthesizeRecordInitMethod(AppView<?> appView) {
MethodAccessFlags methodAccessFlags =
MethodAccessFlags.fromSharedAccessFlags(
Constants.ACC_SYNTHETIC | Constants.ACC_PROTECTED, true);
return DexEncodedMethod.syntheticBuilder()
- .setMethod(factory.recordMembers.constructor)
+ .setMethod(appView.dexItemFactory().recordMembers.constructor)
.setAccessFlags(methodAccessFlags)
- .setCode(new CallObjectInitCfCodeProvider(appView, factory.recordTagType).generateCfCode())
+ .setCode(
+ new CallObjectInitCfCodeProvider(appView, appView.dexItemFactory().recordTagType)
+ .generateCfCode())
// Will be traced by the enqueuer.
.disableAndroidApiLevelCheck()
.build();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
index 063286c..c267943 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/varhandle/VarHandleDesugaring.java
@@ -183,17 +183,21 @@
return refersToMethodHandlesLookup(field.type, factory);
}
- private void ensureMethodHandlesLookupClass(
+ @SuppressWarnings("InconsistentOverloads")
+ public static void ensureMethodHandlesLookupClass(
+ AppView<?> appView,
VarHandleDesugaringEventConsumer eventConsumer,
Collection<? extends ProgramDefinition> contexts) {
- assert contexts.stream().allMatch(context -> context.getContextType() != factory.lookupType);
+ assert contexts.stream()
+ .allMatch(context -> context.getContextType() != appView.dexItemFactory().lookupType);
DexProgramClass clazz =
appView
.getSyntheticItems()
.ensureGlobalClass(
- () -> new MissingGlobalSyntheticsConsumerDiagnostic("VarHandle desugaring"),
+ () ->
+ new MissingGlobalSyntheticsConsumerDiagnostic("MethodHandlesLookup desugaring"),
kinds -> kinds.METHOD_HANDLES_LOOKUP,
- factory.lookupType,
+ appView.dexItemFactory().lookupType,
contexts,
appView,
builder ->
@@ -207,20 +211,23 @@
private void ensureMethodHandlesLookupClass(
VarHandleDesugaringEventConsumer eventConsumer, ProgramDefinition context) {
- ensureMethodHandlesLookupClass(eventConsumer, ImmutableList.of(context));
+ ensureMethodHandlesLookupClass(appView, eventConsumer, ImmutableList.of(context));
}
- private void ensureVarHandleClass(
+ @SuppressWarnings("InconsistentOverloads")
+ public static void ensureVarHandleClass(
+ AppView<?> appView,
VarHandleDesugaringEventConsumer eventConsumer,
Collection<? extends ProgramDefinition> contexts) {
- assert contexts.stream().allMatch(context -> context.getContextType() != factory.varHandleType);
+ assert contexts.stream()
+ .allMatch(context -> context.getContextType() != appView.dexItemFactory().varHandleType);
DexProgramClass clazz =
appView
.getSyntheticItems()
.ensureGlobalClass(
() -> new MissingGlobalSyntheticsConsumerDiagnostic("VarHandle desugaring"),
kinds -> kinds.VAR_HANDLE,
- factory.varHandleType,
+ appView.dexItemFactory().varHandleType,
contexts,
appView,
builder ->
@@ -235,7 +242,7 @@
private void ensureVarHandleClass(
VarHandleDesugaringEventConsumer eventConsumer, ProgramDefinition context) {
if (context.getContextType() != factory.varHandleType) {
- ensureVarHandleClass(eventConsumer, ImmutableList.of(context));
+ ensureVarHandleClass(appView, eventConsumer, ImmutableList.of(context));
}
}
@@ -590,12 +597,12 @@
flags,
DexApplicationReadFlags::hasReadMethodHandlesLookupReferenceFromProgramClass,
DexApplicationReadFlags::getMethodHandlesLookupWitnesses,
- classes -> ensureMethodHandlesLookupClass(eventConsumer, classes));
+ classes -> ensureMethodHandlesLookupClass(appView, eventConsumer, classes));
synthesizeClassIfReferenced(
flags,
DexApplicationReadFlags::hasReadVarHandleReferenceFromProgramClass,
DexApplicationReadFlags::getVarHandleWitnesses,
- classes -> ensureVarHandleClass(eventConsumer, classes));
+ classes -> ensureVarHandleClass(appView, eventConsumer, classes));
}
private void synthesizeClassIfReferenced(
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 66ef64e..8d2b4bb 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
@@ -11,6 +11,7 @@
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItemFactory;
@@ -82,21 +83,28 @@
}
}
- public boolean hasStaticValue(DexEncodedField field) {
- if (field.isStatic()) {
- return (fieldsWithStaticValues != null && fieldsWithStaticValues.containsKey(field))
- || field.getStaticValue() != null;
+ public void forEachOptimizedField(
+ BiConsumer<DexClassAndField, DexValue> consumer, AppView<?> appView) {
+ forEachOptimizedField((field, value) -> consumer.accept(field.asClassField(appView), value));
+ }
+
+ public boolean hasStaticValue(DexClassAndField field) {
+ if (field.getAccessFlags().isStatic()) {
+ return (fieldsWithStaticValues != null
+ && fieldsWithStaticValues.containsKey(field.getDefinition()))
+ || field.getDefinition().getStaticValue() != null;
}
return false;
}
- public DexValue getStaticValue(DexEncodedField field) {
+ public DexValue getStaticValue(DexClassAndField field) {
assert hasStaticValue(field);
- assert field.isStatic();
- if (fieldsWithStaticValues != null && fieldsWithStaticValues.containsKey(field)) {
- return fieldsWithStaticValues.get(field);
+ assert field.getAccessFlags().isStatic();
+ if (fieldsWithStaticValues != null
+ && fieldsWithStaticValues.containsKey(field.getDefinition())) {
+ return fieldsWithStaticValues.get(field.getDefinition());
}
- return field.getStaticValue();
+ return field.getDefinition().getStaticValue();
}
}
@@ -251,7 +259,7 @@
.filter(unnecessaryStaticPuts::contains)
.map(FieldInstruction::getField)
.map(appInfoWithLiveness::resolveField)
- .map(FieldResolutionResult::getResolvedField)
+ .map(FieldResolutionResult::getResolutionPair)
.filter(appInfoWithLiveness::isStaticFieldWrittenOnlyInEnclosingStaticInitializer)
.map(field -> field.getReference())
.collect(Collectors.toSet());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
index eb7671a..677da9c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/ConstantCanonicalizer.java
@@ -152,7 +152,7 @@
public ConstantCanonicalizer canonicalize() {
Object2ObjectLinkedOpenCustomHashMap<Instruction, List<Instruction>> valuesDefinedByConstant =
new Object2ObjectLinkedOpenCustomHashMap<>(
- new Strategy<Instruction>() {
+ new Strategy<>() {
@Override
public int hashCode(Instruction candidate) {
@@ -428,7 +428,7 @@
return false;
}
}
- if (!isReadOfFinalFieldOutsideInitializer(instanceGet)) {
+ if (!isReadOfEffectivelyFinalFieldOutsideInitializer(instanceGet)) {
return false;
}
if (getOrComputeIneligibleInstanceGetInstructions().contains(instanceGet)) {
@@ -444,7 +444,7 @@
if (staticGet.instructionMayHaveSideEffects(appView, context)) {
return false;
}
- if (!isReadOfFinalFieldOutsideInitializer(staticGet)
+ if (!isReadOfEffectivelyFinalFieldOutsideInitializer(staticGet)
&& !isEffectivelyFinalField(staticGet)) {
return false;
}
@@ -475,7 +475,7 @@
return true;
}
- private boolean isReadOfFinalFieldOutsideInitializer(FieldGet fieldGet) {
+ private boolean isReadOfEffectivelyFinalFieldOutsideInitializer(FieldGet fieldGet) {
if (getOrComputeIsAccessingVolatileField()) {
// A final field may be initialized concurrently. A requirement for this is that the field is
// volatile. However, the reading or writing of another volatile field also allows for
@@ -500,9 +500,7 @@
ProgramField resolvedField = resolutionResult.getSingleProgramField();
FieldAccessFlags accessFlags = resolvedField.getAccessFlags();
assert !accessFlags.isVolatile();
- // TODO(b/236661949): Add support for effectively final fields so that this also works well
- // without -allowaccessmodification.
- if (!accessFlags.isFinal()) {
+ if (!resolvedField.isFinalOrEffectivelyFinal(appViewWithClassHierarchy)) {
return false;
}
if (appView.getKeepInfo(resolvedField).isPinned(appView.options())) {
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 05c83da..79ba9d1 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
@@ -478,6 +478,10 @@
ProgramMethod singleTarget,
InliningIRProvider inliningIRProvider,
WhyAreYouNotInliningReporter whyAreYouNotInliningReporter) {
+ if (!inlinerOptions.isConstructorInliningEnabled()) {
+ return false;
+ }
+
IRCode inlinee = inliningIRProvider.getInliningIR(invoke, singleTarget);
// In the Java VM Specification section "4.10.2.4. Instance Initialization Methods and
@@ -517,32 +521,30 @@
// ...
// }
// }
- // TODO(b/278679664): Relax requirement (3) when targeting DEX.
Value thisValue = inlinee.entryBlock().entry().asArgument().outValue();
List<InvokeDirect> initCallsOnThis = new ArrayList<>();
for (Instruction instruction : inlinee.instructions()) {
- if (instruction.isInvokeDirect()) {
+ if (instruction.isInvokeConstructor(appView.dexItemFactory())) {
InvokeDirect initCall = instruction.asInvokeDirect();
- DexMethod invokedMethod = initCall.getInvokedMethod();
- if (appView.dexItemFactory().isConstructor(invokedMethod)) {
- Value receiver = initCall.getReceiver().getAliasedValue();
- if (receiver == thisValue) {
- // The <init>() call of the constructor must be on the same class.
- if (calleeMethodHolder != invokedMethod.holder) {
- whyAreYouNotInliningReporter
- .reportUnsafeConstructorInliningDueToIndirectConstructorCall(initCall);
- return false;
- }
- initCallsOnThis.add(initCall);
+ Value receiver = initCall.getReceiver().getAliasedValue();
+ if (receiver == thisValue) {
+ // The <init>() call of the constructor must be on the same class when targeting the JVM
+ // and Dalvik.
+ if (!options.canInitNewInstanceUsingSuperclassConstructor()
+ && calleeMethodHolder != initCall.getInvokedMethod().getHolderType()) {
+ whyAreYouNotInliningReporter
+ .reportUnsafeConstructorInliningDueToIndirectConstructorCall(initCall);
+ return false;
}
+ initCallsOnThis.add(initCall);
}
} else if (instruction.isInstancePut()) {
// Final fields may not be initialized outside of a constructor in the enclosing class.
InstancePut instancePut = instruction.asInstancePut();
DexField field = instancePut.getField();
DexEncodedField target = appView.appInfo().lookupInstanceTarget(field);
- if (target == null || target.accessFlags.isFinal()) {
+ if (target == null || target.isFinal()) {
whyAreYouNotInliningReporter.reportUnsafeConstructorInliningDueToFinalFieldAssignment(
instancePut);
return false;
@@ -550,29 +552,22 @@
}
}
- // Check that there are no uses of the uninitialized object before it gets initialized.
- int markingColor = inlinee.reserveMarkingColor();
- for (InvokeDirect initCallOnThis : initCallsOnThis) {
- BasicBlock block = initCallOnThis.getBlock();
- for (Instruction instruction : block.instructionsBefore(initCallOnThis)) {
- for (Value inValue : instruction.inValues()) {
- Value root = inValue.getAliasedValue();
- if (root == thisValue) {
- inlinee.returnMarkingColor(markingColor);
- whyAreYouNotInliningReporter.reportUnsafeConstructorInliningDueToUninitializedObjectUse(
- instruction);
- return false;
- }
+ if (initCallsOnThis.isEmpty()) {
+ // In the unusual case where there is no parent/forwarding constructor call, there must be no
+ // instance-put instructions that assign fields on the receiver.
+ for (Instruction user : thisValue.uniqueUsers()) {
+ if (user.isInstancePut() && user.asInstancePut().object().getAliasedValue() == thisValue) {
+ whyAreYouNotInliningReporter.reportUnsafeConstructorInliningDueToUninitializedObjectUse(
+ user);
+ return false;
}
}
- for (BasicBlock predecessor : block.getPredecessors()) {
- inlinee.markTransitivePredecessors(predecessor, markingColor);
- }
- }
-
- for (BasicBlock block : inlinee.blocks) {
- if (block.isMarked(markingColor)) {
- for (Instruction instruction : block.getInstructions()) {
+ } else {
+ // Check that there are no uses of the uninitialized object before it gets initialized.
+ int markingColor = inlinee.reserveMarkingColor();
+ for (InvokeDirect initCallOnThis : initCallsOnThis) {
+ BasicBlock block = initCallOnThis.getBlock();
+ for (Instruction instruction : block.instructionsBefore(initCallOnThis)) {
for (Value inValue : instruction.inValues()) {
Value root = inValue.getAliasedValue();
if (root == thisValue) {
@@ -583,10 +578,29 @@
}
}
}
+ for (BasicBlock predecessor : block.getPredecessors()) {
+ inlinee.markTransitivePredecessors(predecessor, markingColor);
+ }
}
+
+ for (BasicBlock block : inlinee.getBlocks()) {
+ if (block.isMarked(markingColor)) {
+ for (Instruction instruction : block.getInstructions()) {
+ for (Value inValue : instruction.inValues()) {
+ Value root = inValue.getAliasedValue();
+ if (root == thisValue) {
+ inlinee.returnMarkingColor(markingColor);
+ whyAreYouNotInliningReporter
+ .reportUnsafeConstructorInliningDueToUninitializedObjectUse(instruction);
+ return false;
+ }
+ }
+ }
+ }
+ }
+ inlinee.returnMarkingColor(markingColor);
}
- inlinee.returnMarkingColor(markingColor);
inliningIRProvider.cacheInliningIR(invoke, inlinee);
return true;
}
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 178c021..06a8cc2 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
@@ -514,8 +514,9 @@
invoke.getArgument(info.asArgumentInitializationInfo().getArgumentIndex());
Value object = invoke.getReceiver().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field.getReference(), object);
- if (field.isFinal()) {
- activeState.putFinalInstanceField(fieldAndObject, new ExistingValue(value));
+ if (field.isFinalOrEffectivelyFinal(appViewWithLiveness)) {
+ activeState.putFinalOrEffectivelyFinalInstanceField(
+ fieldAndObject, new ExistingValue(value));
} else {
activeState.putNonFinalInstanceField(fieldAndObject, new ExistingValue(value));
}
@@ -524,8 +525,9 @@
if (value.isMaterializableInContext(appViewWithLiveness, method)) {
Value object = invoke.getReceiver().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field.getReference(), object);
- if (field.isFinal()) {
- activeState.putFinalInstanceField(fieldAndObject, new MaterializableValue(value));
+ if (field.isFinalOrEffectivelyFinal(appViewWithLiveness)) {
+ activeState.putFinalOrEffectivelyFinalInstanceField(
+ fieldAndObject, new MaterializableValue(value));
} else {
activeState.putNonFinalInstanceField(
fieldAndObject, new MaterializableValue(value));
@@ -648,8 +650,10 @@
FieldAndObject fieldAndObject = new FieldAndObject(field.getReference(), object);
FieldValue replacement = activeState.getInstanceFieldValue(fieldAndObject);
if (replacement != null) {
- markAssumeDynamicTypeUsersForRemoval(instanceGet, replacement, assumeRemover);
- replacement.eliminateRedundantRead(it, instanceGet);
+ if (isRedundantFieldLoadEliminationAllowed(field)) {
+ markAssumeDynamicTypeUsersForRemoval(instanceGet, replacement, assumeRemover);
+ replacement.eliminateRedundantRead(it, instanceGet);
+ }
return;
}
@@ -658,6 +662,15 @@
clearMostRecentInstanceFieldWrite(instanceGet, field);
}
+ private boolean isRedundantFieldLoadEliminationAllowed(DexClassAndField field) {
+ // Always allowed in D8 since D8 does not support @NoRedundantFieldLoadElimination.
+ return !appView.enableWholeProgramOptimizations()
+ || !field.isProgramField()
+ || appView
+ .getKeepInfo(field.asProgramField())
+ .isRedundantFieldLoadEliminationAllowed(appView.options());
+ }
+
private void handleNewInstance(NewInstance newInstance) {
markClassAsInitialized(newInstance.getType());
markMostRecentInitClassForRemoval(newInstance.getType());
@@ -706,24 +719,24 @@
Value object = instancePut.object().getAliasedValue();
FieldAndObject fieldAndObject = new FieldAndObject(field.getReference(), object);
ExistingValue value = new ExistingValue(instancePut.value());
- if (isFinal(field)) {
+ if (field.isFinalOrEffectivelyFinal(appView)) {
assert !field.getDefinition().isFinal()
|| method.getDefinition().isInstanceInitializer()
|| verifyWasInstanceInitializer();
- activeState.putFinalInstanceField(fieldAndObject, value);
+ activeState.putFinalOrEffectivelyFinalInstanceField(fieldAndObject, value);
} else {
activeState.putNonFinalInstanceField(fieldAndObject, value);
+ }
- // Record that this field is now most recently written by the current instruction.
- if (release) {
- InstancePut mostRecentInstanceFieldWrite =
- activeState.putMostRecentInstanceFieldWrite(fieldAndObject, instancePut);
- if (mostRecentInstanceFieldWrite != null) {
- instructionsToRemove
- .computeIfAbsent(
- mostRecentInstanceFieldWrite.getBlock(), ignoreKey(Sets::newIdentityHashSet))
- .add(mostRecentInstanceFieldWrite);
- }
+ // Record that this field is now most recently written by the current instruction.
+ if (release) {
+ InstancePut mostRecentInstanceFieldWrite =
+ activeState.putMostRecentInstanceFieldWrite(fieldAndObject, instancePut);
+ if (mostRecentInstanceFieldWrite != null) {
+ instructionsToRemove
+ .computeIfAbsent(
+ mostRecentInstanceFieldWrite.getBlock(), ignoreKey(Sets::newIdentityHashSet))
+ .add(mostRecentInstanceFieldWrite);
}
}
@@ -755,7 +768,7 @@
clearMostRecentStaticFieldWrite(staticGet, field);
FieldValue value = new ExistingValue(staticGet.value());
- if (isFinal(field)) {
+ if (field.isFinalOrEffectivelyFinal(appView)) {
activeState.putFinalStaticField(field.getReference(), value);
} else {
activeState.putNonFinalStaticField(field.getReference(), value);
@@ -796,7 +809,7 @@
}
ExistingValue value = new ExistingValue(staticPut.value());
- if (isFinal(field)) {
+ if (field.isFinalOrEffectivelyFinal(appView)) {
assert appView.checkForTesting(
() -> !field.getDefinition().isFinal() || method.getDefinition().isClassInitializer());
activeState.putFinalStaticField(field.getReference(), value);
@@ -827,7 +840,7 @@
&& fieldValue.isSingleValue()) {
SingleValue singleFieldValue = fieldValue.asSingleValue();
if (singleFieldValue.isMaterializableInContext(appViewWithLiveness, method)) {
- activeState.putFinalInstanceField(
+ activeState.putFinalOrEffectivelyFinalInstanceField(
new FieldAndObject(field, value), new MaterializableValue(singleFieldValue));
}
}
@@ -1325,7 +1338,7 @@
arraySlotValues.put(arraySlot, value);
}
- public void putFinalInstanceField(FieldAndObject field, FieldValue value) {
+ public void putFinalOrEffectivelyFinalInstanceField(FieldAndObject field, FieldValue value) {
ensureCapacityForNewElement();
if (finalInstanceFieldValues == null) {
finalInstanceFieldValues = new LinkedHashMap<>();
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 72a2169..69d5822 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
@@ -883,12 +883,17 @@
}
}
- // Must be a constructor of the exact same class.
+ // Must be a constructor of the exact same class except when targeting ART, where constructors
+ // in the superclass hierarchy are also allowed.
DexMethod init = invoke.getInvokedMethod();
- if (init.holder != eligibleClass.type) {
- // Calling a constructor on a class that is different from the type of the instance.
- // Gracefully abort class inlining (see the test B116282409).
- return null;
+ if (appView.options().canInitNewInstanceUsingSuperclassConstructor()) {
+ if (!appView.appInfo().isSubtype(eligibleClass.getType(), init.getHolderType())) {
+ return null;
+ }
+ } else {
+ if (eligibleClass.getType() != init.getHolderType()) {
+ return null;
+ }
}
// Check that the `eligibleInstance` does not escape via the constructor.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 4754658..4838202 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -107,6 +107,7 @@
import com.android.tools.r8.shaking.KeepInfoCollection;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.TraversalContinuation;
import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap;
import com.android.tools.r8.utils.collections.LongLivedClassSetBuilder;
import com.android.tools.r8.utils.collections.LongLivedProgramMethodMapBuilder;
@@ -860,89 +861,102 @@
// This maps the ordinal to the object state, note that some fields may have been removed,
// hence the entry is in this map but not the enumToOrdinalMap.
Int2ReferenceMap<ObjectState> ordinalToObjectState = new Int2ReferenceArrayMap<>();
- // Any fields matching the expected $VALUES content can be recorded here, they have however
- // all the same content.
- ImmutableSet.Builder<DexField> valuesField = ImmutableSet.builder();
- EnumValuesObjectState valuesContents = null;
- EnumStaticFieldValues enumStaticFieldValues = staticFieldValuesMap.get(enumClass.type);
- if (enumStaticFieldValues == null) {
+ if (!staticFieldValuesMap.containsKey(enumClass.getType())) {
reportFailure(enumClass, new MissingEnumStaticFieldValuesReason());
return null;
}
- enumStaticFieldValues =
- enumStaticFieldValues.rewrittenWithLens(appView, appView.graphLens(), appView.codeLens());
+ EnumStaticFieldValues enumStaticFieldValues =
+ staticFieldValuesMap
+ .get(enumClass.getType())
+ .rewrittenWithLens(appView, appView.graphLens(), appView.codeLens());
Set<DexType> enumSubtypes = enumUnboxingCandidatesInfo.getSubtypes(enumClass.getType());
// Step 1: We iterate over the field to find direct enum instance information and the values
// fields.
- for (DexEncodedField staticField : enumClass.staticFields()) {
- // The field might be specialized while the data was recorded without the specialization.
- if (factory.enumMembers.isEnumField(staticField, enumClass.type, enumSubtypes)) {
- ObjectState enumState =
- enumStaticFieldValues.getObjectStateForPossiblyPinnedField(staticField.getReference());
- if (enumState == null) {
- assert enumStaticFieldValues.getObjectStateForPossiblyPinnedField(
- staticField.getReference().withType(enumClass.type, factory))
- == null;
- if (staticField.getOptimizationInfo().isDead()) {
- // We don't care about unused field data.
- continue;
- }
- // We could not track the content of that field. We bail out.
- reportFailure(
- enumClass, new MissingObjectStateForEnumInstanceReason(staticField.getReference()));
- return null;
- }
- OptionalInt optionalOrdinal = getOrdinal(enumState);
- if (!optionalOrdinal.isPresent()) {
- reportFailure(
- enumClass,
- new MissingInstanceFieldValueForEnumInstanceReason(
- factory.enumMembers.ordinalField, staticField.getReference()));
- return null;
- }
- int ordinal = optionalOrdinal.getAsInt();
- unboxedValues.put(staticField.getReference(), ordinalToUnboxedInt(ordinal));
- ordinalToObjectState.put(ordinal, enumState);
- if (isEnumWithSubtypes) {
- DynamicType dynamicType = staticField.getOptimizationInfo().getDynamicType();
- if (dynamicType.isExactClassType()) {
- valueTypes.put(ordinal, dynamicType.getExactClassType().getClassType());
- } else {
- reportFailure(
- enumClass,
- new MissingExactDynamicEnumTypeForEnumWithSubtypesReason(
- staticField.getReference()));
- return null;
- }
- }
- } else if (factory.enumMembers.isValuesFieldCandidate(staticField, enumClass.type)) {
- ObjectState valuesState =
- enumStaticFieldValues.getObjectStateForPossiblyPinnedField(staticField.getReference());
- if (valuesState == null) {
- if (staticField.getOptimizationInfo().isDead()) {
- // We don't care about unused field data.
- continue;
- }
- // We could not track the content of that field. We bail out.
- // We could not track the content of that field, and the field could be a values field.
- // We conservatively bail out.
- reportFailure(
- enumClass, new MissingContentsForEnumValuesArrayReason(staticField.getReference()));
- return null;
- }
- assert valuesState.isEnumValuesObjectState();
- assert valuesContents == null
- || valuesContents.equals(valuesState.asEnumValuesObjectState());
- valuesContents = valuesState.asEnumValuesObjectState();
- valuesField.add(staticField.getReference());
- }
+ ImmutableSet.Builder<DexField> valuesField = ImmutableSet.builder();
+ TraversalContinuation<?, EnumValuesObjectState> traversalContinuation =
+ enumClass.traverseProgramFields(
+ (field, valuesContents) -> {
+ if (!field.getAccessFlags().isStatic()) {
+ return TraversalContinuation.doContinue(valuesContents);
+ }
+ // The field might be specialized while the data was recorded without the
+ // specialization.
+ if (factory.enumMembers.isEnumField(field, enumClass.type, enumSubtypes)) {
+ ObjectState enumState =
+ enumStaticFieldValues.getObjectStateForPossiblyPinnedField(
+ field.getReference());
+ if (enumState == null) {
+ assert enumStaticFieldValues.getObjectStateForPossiblyPinnedField(
+ field.getReference().withType(enumClass.type, factory))
+ == null;
+ if (field.getOptimizationInfo().isDead()) {
+ // We don't care about unused field data.
+ return TraversalContinuation.doContinue(valuesContents);
+ }
+ // We could not track the content of that field. We bail out.
+ reportFailure(
+ enumClass, new MissingObjectStateForEnumInstanceReason(field.getReference()));
+ return TraversalContinuation.doBreak();
+ }
+ OptionalInt optionalOrdinal = getOrdinal(enumState);
+ if (!optionalOrdinal.isPresent()) {
+ reportFailure(
+ enumClass,
+ new MissingInstanceFieldValueForEnumInstanceReason(
+ factory.enumMembers.ordinalField, field.getReference()));
+ return TraversalContinuation.doBreak();
+ }
+ int ordinal = optionalOrdinal.getAsInt();
+ unboxedValues.put(field.getReference(), ordinalToUnboxedInt(ordinal));
+ ordinalToObjectState.put(ordinal, enumState);
+ if (isEnumWithSubtypes) {
+ DynamicType dynamicType = field.getOptimizationInfo().getDynamicType();
+ if (dynamicType.isExactClassType()) {
+ valueTypes.put(ordinal, dynamicType.getExactClassType().getClassType());
+ } else {
+ reportFailure(
+ enumClass,
+ new MissingExactDynamicEnumTypeForEnumWithSubtypesReason(
+ field.getReference()));
+ return TraversalContinuation.doBreak();
+ }
+ }
+ } else if (factory.enumMembers.isValuesFieldCandidate(field, enumClass.type)) {
+ ObjectState valuesState =
+ enumStaticFieldValues.getObjectStateForPossiblyPinnedField(
+ field.getReference());
+ if (valuesState == null) {
+ if (field.getOptimizationInfo().isDead()) {
+ // We don't care about unused field data.
+ return TraversalContinuation.doContinue(valuesContents);
+ }
+ // We could not track the content of that field. We bail out.
+ // We could not track the content of that field, and the field could be a values
+ // field.
+ // We conservatively bail out.
+ reportFailure(
+ enumClass, new MissingContentsForEnumValuesArrayReason(field.getReference()));
+ return TraversalContinuation.doBreak();
+ }
+ assert valuesState.isEnumValuesObjectState();
+ assert valuesContents == null
+ || valuesContents.equals(valuesState.asEnumValuesObjectState());
+ valuesContents = valuesState.asEnumValuesObjectState();
+ valuesField.add(field.getReference());
+ }
+ return TraversalContinuation.doContinue(valuesContents);
+ },
+ null);
+ if (traversalContinuation.shouldBreak()) {
+ return null;
}
// Step 2: We complete the information based on the values content, since some enum instances
// may be reachable only though the $VALUES field.
+ EnumValuesObjectState valuesContents = traversalContinuation.asContinue().getValue();
if (valuesContents != null) {
for (int ordinal = 0; ordinal < valuesContents.getEnumValuesSize(); ordinal++) {
if (!ordinalToObjectState.containsKey(ordinal)) {
@@ -1026,7 +1040,7 @@
boolean canBeOrdinal = instanceField.type.isIntType();
ImmutableInt2ReferenceSortedMap.Builder<AbstractValue> data =
ImmutableInt2ReferenceSortedMap.builder();
- for (Integer ordinal : ordinalToObjectState.keySet()) {
+ for (int ordinal : ordinalToObjectState.keySet()) {
ObjectState state = ordinalToObjectState.get(ordinal);
AbstractValue fieldValue = state.getAbstractFieldValue(encodedInstanceField);
if (!fieldValue.isSingleValue()) {
@@ -1486,9 +1500,27 @@
} else if (singleTargetReference == factory.enumMembers.hashCode) {
return Reason.ELIGIBLE;
} else if (singleTargetReference == factory.enumMembers.constructor) {
- // Enum constructor call is allowed only if called from an enum initializer.
- if (code.method().isInstanceInitializer() && code.context().getHolder() == enumClass) {
- return Reason.ELIGIBLE;
+ assert invoke.getFirstArgument() == enumValue;
+ if (appView.options().canInitNewInstanceUsingSuperclassConstructor()) {
+ // Enum constructor call is allowed if called from any enum initializer.
+ DexProgramClass representativeContext =
+ enumUnboxingCandidatesInfo.getCandidateClassOrNull(context.getHolderType());
+ if (context.getDefinition().isInstanceInitializer()
+ && representativeContext == enumClass) {
+ return Reason.ELIGIBLE;
+ }
+ // Otherwise must be called from the class initializer of a root enum initializer.
+ if (context.isStructurallyEqualTo(enumClass.getProgramClassInitializer())) {
+ assert enumUnboxingCandidatesInfo.verifyIsSuperEnumUnboxingCandidate(enumClass);
+ assert context.getHolder() == representativeContext;
+ return Reason.ELIGIBLE;
+ }
+ } else {
+ // Enum constructor call is allowed only if called from a root enum initializer.
+ if (context.getDefinition().isInstanceInitializer() && context.getHolder() == enumClass) {
+ assert enumUnboxingCandidatesInfo.verifyIsSuperEnumUnboxingCandidate(enumClass);
+ return Reason.ELIGIBLE;
+ }
}
}
return new UnsupportedLibraryInvokeReason(singleTargetReference);
@@ -1524,6 +1556,13 @@
|| singleTargetReference == factory.objectsMethods.requireNonNullWithMessage) {
return Reason.ELIGIBLE;
}
+ if (singleTargetReference == factory.objectsMethods.toStringWithObject) {
+ addRequiredNameData(enumClass);
+ return Reason.ELIGIBLE;
+ }
+ if (singleTargetReference == factory.objectsMethods.equals) {
+ return comparableAsUnboxedValues(invoke);
+ }
return new UnsupportedLibraryInvokeReason(singleTargetReference);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
index 70e73dc..06cf453 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateInfoCollection.java
@@ -158,6 +158,11 @@
return true;
}
+ public boolean verifyIsSuperEnumUnboxingCandidate(DexProgramClass clazz) {
+ assert enumTypeToInfo.containsKey(clazz.getType());
+ return true;
+ }
+
public static class EnumUnboxingCandidateInfo {
private final DexProgramClass enumClass;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java
index be89c4e..53e7b32 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCfMethods.java
@@ -139,6 +139,38 @@
ImmutableList.of());
}
+ public static CfCode EnumUnboxingMethods_objectEquals(DexItemFactory factory, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 2,
+ 2,
+ ImmutableList.of(
+ label0,
+ new CfLoad(ValueType.INT, 0),
+ new CfLoad(ValueType.INT, 1),
+ new CfIfCmp(IfType.NE, ValueType.INT, label1),
+ new CfConstNumber(1, ValueType.INT),
+ new CfGoto(label2),
+ label1,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1}, new FrameType[] {FrameType.intType(), FrameType.intType()})),
+ new CfConstNumber(0, ValueType.INT),
+ label2,
+ new CfFrame(
+ new Int2ObjectAVLTreeMap<>(
+ new int[] {0, 1}, new FrameType[] {FrameType.intType(), FrameType.intType()}),
+ new ArrayDeque<>(Arrays.asList(FrameType.intType()))),
+ new CfReturn(ValueType.INT),
+ label3),
+ ImmutableList.of(),
+ ImmutableList.of());
+ }
+
public static CfCode EnumUnboxingMethods_ordinal(DexItemFactory factory, DexMethod method) {
CfLabel label0 = new CfLabel();
CfLabel label1 = new CfLabel();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index 789c542..05648c3 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -499,6 +499,18 @@
getSharedUtilityClass()
.ensureCheckNotZeroWithMessageMethod(appView, context, eventConsumer));
}
+ } else if (invokedMethod == factory.objectsMethods.toStringWithObject) {
+ rewriteStringValueOf(invoke, context, convertedEnums, instructionIterator, eventConsumer);
+ } else if (invokedMethod == factory.objectsMethods.equals) {
+ assert invoke.arguments().size() == 2;
+ Value argument = invoke.getFirstArgument();
+ DexType enumType = getEnumClassTypeOrNull(argument, convertedEnums);
+ if (enumType != null) {
+ replaceEnumInvoke(
+ instructionIterator,
+ invoke,
+ getSharedUtilityClass().ensureObjectsEqualsMethod(appView, context, eventConsumer));
+ }
}
return;
}
@@ -506,17 +518,7 @@
// Calls to java.lang.String.
if (invokedMethod.getHolderType() == factory.stringType) {
if (invokedMethod == factory.stringMembers.valueOf) {
- assert invoke.arguments().size() == 1;
- Value argument = invoke.getFirstArgument();
- DexType enumType = getEnumClassTypeOrNull(argument, convertedEnums);
- if (enumType != null) {
- ProgramMethod stringValueOfMethod =
- getLocalUtilityClass(enumType)
- .ensureStringValueOfMethod(appView, context, eventConsumer);
- instructionIterator.replaceCurrentInstruction(
- new InvokeStatic(
- stringValueOfMethod.getReference(), invoke.outValue(), invoke.arguments()));
- }
+ rewriteStringValueOf(invoke, context, convertedEnums, instructionIterator, eventConsumer);
}
return;
}
@@ -574,6 +576,24 @@
}
}
+ private void rewriteStringValueOf(
+ InvokeStatic invoke,
+ ProgramMethod context,
+ Map<Instruction, DexType> convertedEnums,
+ InstructionListIterator instructionIterator,
+ EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
+ assert invoke.arguments().size() == 1;
+ Value argument = invoke.getFirstArgument();
+ DexType enumType = getEnumClassTypeOrNull(argument, convertedEnums);
+ if (enumType != null) {
+ ProgramMethod stringValueOfMethod =
+ getLocalUtilityClass(enumType).ensureStringValueOfMethod(appView, context, eventConsumer);
+ instructionIterator.replaceCurrentInstruction(
+ new InvokeStatic(
+ stringValueOfMethod.getReference(), invoke.outValue(), invoke.arguments()));
+ }
+ }
+
public void rewriteNullCheck(
InstructionListIterator iterator,
InvokeMethod invoke,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 6b8b450..189f603 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.DexValue;
import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.PrunedItems;
@@ -32,6 +33,7 @@
import com.android.tools.r8.graph.proto.RewrittenPrototypeDescription;
import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.IRCode;
@@ -41,8 +43,11 @@
import com.android.tools.r8.ir.code.InvokeVirtual;
import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.NewUnboxedEnumInstance;
+import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.ExtraParameter;
import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
@@ -288,10 +293,7 @@
private void fixupSuperEnumClassInitializers(
IRConverter converter, ExecutorService executorService) throws ExecutionException {
DexEncodedField ordinalField =
- appView
- .appInfo()
- .resolveField(appView.dexItemFactory().enumMembers.ordinalField)
- .getResolvedField();
+ appView.appInfo().resolveField(factory.enumMembers.ordinalField).getResolvedField();
ThreadUtils.processItems(
unboxedEnumHierarchy.keySet(),
unboxedEnum -> fixupSuperEnumClassInitializer(converter, unboxedEnum, ordinalField),
@@ -346,8 +348,7 @@
for (Instruction user : constClass.outValue().aliasedUsers()) {
if (user.isInvokeVirtual()) {
InvokeVirtual invoke = user.asInvokeVirtual();
- if (invoke.getInvokedMethod()
- == appView.dexItemFactory().classMethods.desiredAssertionStatus) {
+ if (invoke.getInvokedMethod() == factory.classMethods.desiredAssertionStatus) {
desiredAssertionStatusUsers.add(invoke);
}
}
@@ -371,8 +372,7 @@
NewInstance newInstance = instruction.asNewInstance();
DexType rewrittenType = appView.graphLens().lookupType(newInstance.getType());
if (enumDataMap.isAssignableTo(rewrittenType, unboxedEnum.getType())) {
- InvokeDirect constructorInvoke =
- newInstance.getUniqueConstructorInvoke(appView.dexItemFactory());
+ InvokeDirect constructorInvoke = newInstance.getUniqueConstructorInvoke(factory);
assert constructorInvoke != null;
DexMethod invokedMethod = constructorInvoke.getInvokedMethod();
@@ -382,7 +382,8 @@
// find the ordinal of the current enum instance.
MethodLookupResult lookupResult =
appView.graphLens().lookupInvokeDirect(invokedMethod, classInitializer);
- if (lookupResult.getReference() != invokedMethod) {
+ if (lookupResult.getReference() != invokedMethod
+ || !lookupResult.getPrototypeChanges().isEmpty()) {
List<Value> rewrittenArguments =
new ArrayList<>(constructorInvoke.arguments().size());
for (int i = 0; i < constructorInvoke.arguments().size(); i++) {
@@ -394,6 +395,23 @@
rewrittenArguments.add(argument);
}
}
+ for (ExtraParameter extraParameter :
+ lookupResult.getPrototypeChanges().getExtraParameters()) {
+ SingleNumberValue singleNumberValue = extraParameter.getValue(appView);
+ Instruction materializingInstruction =
+ singleNumberValue.createMaterializingInstruction(
+ appView,
+ code,
+ TypeAndLocalInfoSupplier.create(
+ extraParameter.getType(appView.dexItemFactory()).toTypeElement(appView),
+ null));
+ materializingInstruction.setPosition(Position.none());
+ instructionIterator.previous();
+ instructionIterator.add(materializingInstruction);
+ rewrittenArguments.add(materializingInstruction.outValue());
+ Instruction next = instructionIterator.next();
+ assert next == newInstance;
+ }
InvokeDirect originalConstructorInvoke = constructorInvoke;
constructorInvoke =
InvokeDirect.builder()
@@ -409,7 +427,6 @@
// then remove the rewritten constructor invoke from the IR.
instructionsToRemove.put(originalConstructorInvoke, Optional.of(constructorInvoke));
} else {
- assert lookupResult.getPrototypeChanges().isEmpty();
// Record that the constructor invoke needs to be removed.
instructionsToRemove.put(constructorInvoke, Optional.empty());
}
@@ -418,7 +435,17 @@
newInstance.getType() == unboxedEnum.getType()
? unboxedEnum
: appView.programDefinitionFor(newInstance.getType(), classInitializer);
- ProgramMethod constructor = holder.lookupProgramMethod(lookupResult.getReference());
+ DexClassAndMethod constructor;
+ if (appView.options().canInitNewInstanceUsingSuperclassConstructor()) {
+ MethodResolutionResult resolutionResult =
+ appView
+ .appInfo()
+ .resolveMethod(
+ lookupResult.getReference(), constructorInvoke.getInterfaceBit());
+ constructor = resolutionResult.getResolutionPair();
+ } else {
+ constructor = holder.lookupProgramMethod(lookupResult.getReference());
+ }
assert constructor != null;
InstanceFieldInitializationInfo ordinalInitializationInfo =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
index 3792bd4..37c814d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
@@ -72,6 +72,7 @@
ensureCheckNotZeroWithMessageMethod(appView);
ensureCompareToMethod(appView);
ensureEqualsMethod(appView);
+ ensureObjectsEqualsMethod(appView);
ensureOrdinalMethod(appView);
}
@@ -151,6 +152,25 @@
method -> EnumUnboxingCfMethods.EnumUnboxingMethods_equals(dexItemFactory, method));
}
+ public ProgramMethod ensureObjectsEqualsMethod(
+ AppView<AppInfoWithLiveness> appView,
+ ProgramMethod context,
+ EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
+ ProgramMethod method = ensureObjectsEqualsMethod(appView);
+ eventConsumer.acceptEnumUnboxerSharedUtilityClassMethodContext(method, context);
+ return method;
+ }
+
+ private ProgramMethod ensureObjectsEqualsMethod(AppView<AppInfoWithLiveness> appView) {
+ DexItemFactory dexItemFactory = appView.dexItemFactory();
+ return internalEnsureMethod(
+ appView,
+ dexItemFactory.createString("objects$equals"),
+ dexItemFactory.createProto(
+ dexItemFactory.booleanType, dexItemFactory.intType, dexItemFactory.intType),
+ method -> EnumUnboxingCfMethods.EnumUnboxingMethods_objectEquals(dexItemFactory, method));
+ }
+
public ProgramMethod ensureOrdinalMethod(
AppView<AppInfoWithLiveness> appView,
ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
index f8e784e..ba4ba64 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MethodOptimizationInfoCollector.java
@@ -46,6 +46,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexItemFactory;
@@ -339,8 +340,8 @@
case STATIC_GET:
{
FieldInstruction fieldGet = instruction.asFieldInstruction();
- DexEncodedField field =
- appView.appInfo().resolveField(fieldGet.getField()).getResolvedField();
+ DexClassAndField field =
+ appView.appInfo().resolveField(fieldGet.getField()).getResolutionPair();
if (field == null) {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/BridgeAnalyzer.java b/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/BridgeAnalyzer.java
index be806b4..88ee7f1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/BridgeAnalyzer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/bridge/BridgeAnalyzer.java
@@ -6,14 +6,18 @@
import static com.android.tools.r8.ir.code.Opcodes.ARGUMENT;
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.GOTO;
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
import static com.android.tools.r8.ir.code.Opcodes.RETURN;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.CheckCast;
+import com.android.tools.r8.ir.code.Goto;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.Return;
@@ -24,17 +28,15 @@
/** Returns a {@link BridgeInfo} object describing this method if it is recognized as a bridge. */
public static BridgeInfo analyzeMethod(DexEncodedMethod method, IRCode code) {
- if (code.blocks.size() > 1) {
- return failure();
- }
-
// Scan through the instructions one-by-one. We expect a sequence of Argument instructions,
// followed by a (possibly empty) sequence of CheckCast instructions, followed by a single
// InvokeMethod instruction, followed by an optional CheckCast instruction, followed by a Return
// instruction.
InvokeMethodWithReceiver uniqueInvoke = null;
CheckCast uniqueReturnCast = null;
- for (Instruction instruction : code.entryBlock().getInstructions()) {
+ InstructionListIterator instructionIterator = code.entryBlock().listIterator(code);
+ while (instructionIterator.hasNext()) {
+ Instruction instruction = instructionIterator.next();
switch (instruction.opcode()) {
case ARGUMENT:
break;
@@ -74,6 +76,17 @@
break;
}
+ case GOTO:
+ {
+ Goto gotoInstruction = instruction.asGoto();
+ BasicBlock targetBlock = gotoInstruction.getTarget();
+ if (targetBlock.hasCatchHandlers()) {
+ return failure();
+ }
+ instructionIterator = targetBlock.listIterator(code);
+ break;
+ }
+
case RETURN:
if (!analyzeReturn(instruction.asReturn(), uniqueInvoke, uniqueReturnCast)) {
return failure();
@@ -107,8 +120,10 @@
}
int argumentIndex = object.definition.asArgument().getIndex();
Value castValue = checkCast.outValue();
- // The out value cannot have any phi users since there is only one block.
- assert !castValue.hasPhiUsers();
+ // The out value should not have any phi users since we only allow linear control flow.
+ if (castValue.hasPhiUsers()) {
+ return false;
+ }
// It is not allowed to have any other users than the invoke instruction.
if (castValue.hasDebugUsers() || !castValue.hasSingleUniqueUser()) {
return false;
@@ -142,8 +157,10 @@
Value returnValue = invoke.outValue();
Value uncastValue = checkCast.object().getAliasedValue();
Value castValue = checkCast.outValue();
- // The out value cannot have any phi users since there is only one block.
- assert !castValue.hasPhiUsers();
+ // The out value should not have any phi users since we only allow linear control flow.
+ if (castValue.hasPhiUsers()) {
+ return false;
+ }
// It must cast the result to the return type of the enclosing method and return the cast value.
return uncastValue == returnValue
&& checkCast.getType() == method.returnType()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
index 1de19ad..c5ab574 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/EmptyInstanceFieldInitializationInfoCollection.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.info.field;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.lens.GraphLens;
@@ -31,14 +32,14 @@
@Override
public void forEach(
DexDefinitionSupplier definitions,
- BiConsumer<DexEncodedField, InstanceFieldInitializationInfo> consumer) {
+ BiConsumer<DexClassAndField, InstanceFieldInitializationInfo> consumer) {
// Intentionally empty.
}
@Override
public void forEachWithDeterministicOrder(
DexDefinitionSupplier definitions,
- BiConsumer<DexEncodedField, InstanceFieldInitializationInfo> consumer) {
+ BiConsumer<DexClassAndField, InstanceFieldInitializationInfo> consumer) {
// Intentionally empty.
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
index d6ab75a..e4b044d 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldInitializationInfoCollection.java
@@ -30,11 +30,11 @@
public abstract void forEach(
DexDefinitionSupplier definitions,
- BiConsumer<DexEncodedField, InstanceFieldInitializationInfo> consumer);
+ BiConsumer<DexClassAndField, InstanceFieldInitializationInfo> consumer);
public abstract void forEachWithDeterministicOrder(
DexDefinitionSupplier definitions,
- BiConsumer<DexEncodedField, InstanceFieldInitializationInfo> consumer);
+ BiConsumer<DexClassAndField, InstanceFieldInitializationInfo> consumer);
public abstract InstanceFieldInitializationInfo get(DexEncodedField field);
@@ -55,7 +55,7 @@
TreeMap<DexField, InstanceFieldInitializationInfo> infos = new TreeMap<>(DexField::compareTo);
public void recordInitializationInfo(
- DexEncodedField field, InstanceFieldInitializationInfo info) {
+ DexClassAndField field, InstanceFieldInitializationInfo info) {
recordInitializationInfo(field.getReference(), info);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
index 1ecc4df..88a7626 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/NonTrivialInstanceFieldInitializationInfoCollection.java
@@ -6,7 +6,7 @@
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
@@ -35,11 +35,10 @@
@Override
public void forEach(
DexDefinitionSupplier definitions,
- BiConsumer<DexEncodedField, InstanceFieldInitializationInfo> consumer) {
+ BiConsumer<DexClassAndField, InstanceFieldInitializationInfo> consumer) {
infos.forEach(
(field, info) -> {
- DexClass holder = definitions.definitionForHolder(field);
- DexEncodedField definition = field.lookupOnClass(holder);
+ DexClassAndField definition = definitions.definitionFor(field);
if (definition != null) {
consumer.accept(definition, info);
} else {
@@ -51,7 +50,7 @@
@Override
public void forEachWithDeterministicOrder(
DexDefinitionSupplier definitions,
- BiConsumer<DexEncodedField, InstanceFieldInitializationInfo> consumer) {
+ BiConsumer<DexClassAndField, InstanceFieldInitializationInfo> consumer) {
// We currently use a sorted backing and can therefore simply use forEach().
forEach(definitions, consumer);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
index 2005de2..096adfb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/initializer/NonTrivialInstanceInitializerInfo.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.ir.optimize.info.initializer;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexClassAndField;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.lens.GraphLens;
@@ -149,12 +149,12 @@
&& parent == null;
}
- public Builder markFieldAsRead(DexEncodedField field) {
+ public Builder markFieldAsRead(DexClassAndField field) {
if (readSet.isKnownFieldSet()) {
if (readSet.isBottom()) {
- readSet = new ConcreteMutableFieldSet(field);
+ readSet = new ConcreteMutableFieldSet(field.getDefinition());
} else {
- readSet.asConcreteFieldSet().add(field);
+ readSet.asConcreteFieldSet().add(field.getDefinition());
}
}
assert readSet.contains(field);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/R8MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/R8MemberValuePropagation.java
index 1810750..b8d6c66 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/R8MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/membervaluepropagation/R8MemberValuePropagation.java
@@ -271,7 +271,7 @@
AbstractValue abstractValue;
if (field.getType().isAlwaysNull(appView)) {
abstractValue = appView.abstractValueFactory().createSingleNumberValue(0);
- } else if (appView.appInfo().isFieldWrittenByFieldPutInstruction(definition)) {
+ } else if (appView.appInfo().isFieldWrittenByFieldPutInstruction(target)) {
abstractValue = definition.getOptimizationInfo().getAbstractValue();
if (abstractValue.isUnknown() && !definition.isStatic()) {
AbstractValue abstractReceiverValue =
@@ -350,8 +350,7 @@
// If the field is read, we can't remove the instance-put unless the value of the field is known
// to be null (in which case the instance-put is a no-op because it assigns the field the same
// value as its default value).
- if (field.getType().isAlwaysNull(appView)
- || !appView.appInfo().isFieldRead(field.getDefinition())) {
+ if (field.getType().isAlwaysNull(appView) || !appView.appInfo().isFieldRead(field)) {
iterator.replaceCurrentInstructionByNullCheckIfPossible(appView, code.context());
return;
}
@@ -376,8 +375,7 @@
// If the field is read, we can't remove the static-put unless the value of the field is known
// to be null (in which case the static-put is a no-op because it assigns the field the same
// value as its default value).
- if (field.getType().isAlwaysNull(appView)
- || !appView.appInfo().isFieldRead(field.getDefinition())) {
+ if (field.getType().isAlwaysNull(appView) || !appView.appInfo().isFieldRead(field)) {
iterator.removeOrReplaceCurrentInstructionByInitClassIfPossible(
appView, code, field.getHolderType());
return;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
index 5f0bea9..2798fb0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderAppendOptimizer.java
@@ -31,7 +31,6 @@
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.AppendNode;
-import com.android.tools.r8.ir.optimize.string.StringBuilderNode.EscapeNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.ImplicitToStringNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitNode;
import com.android.tools.r8.ir.optimize.string.StringBuilderNode.InitOrAppendNode;
@@ -177,15 +176,17 @@
previousState = result.asAbstractState();
for (Phi phi : block.getPhis()) {
if (previousState.isLiveStringBuilder(phi)) {
- visitAllAliasing(
- phi,
- previousState,
- value -> {},
- alias -> {
- EscapeNode escapeNode = new EscapeNode();
- currentRoots.put(alias, escapeNode);
- currentTails.put(alias, escapeNode);
- });
+ boolean seenEscaped =
+ visitAllAliasing(
+ phi,
+ previousState,
+ value -> {},
+ alias ->
+ addNodeToRootAndTail(
+ currentRoots, currentTails, alias, createEscapeNode()));
+ if (seenEscaped) {
+ addNodeToRootAndTail(currentRoots, currentTails, phi, createEscapeNode());
+ }
}
}
for (Instruction instruction : block.getInstructions()) {
@@ -198,16 +199,8 @@
createNodesForInstruction(
instruction,
previousState,
- (value, sbNode) -> {
- StringBuilderNode currentTail = currentTails.get(value);
- if (currentTail == null) {
- currentRoots.put(value, sbNode);
- currentTails.put(value, sbNode);
- } else if (shouldAddNodeToGraph(currentTail, sbNode)) {
- currentTail.addSuccessor(sbNode);
- currentTails.put(value, sbNode);
- }
- });
+ (value, sbNode) ->
+ addNodeToRootAndTail(currentRoots, currentTails, value, sbNode));
}
assert currentRoots.keySet().equals(currentTails.keySet());
assert previousState.getLiveStringBuilders().containsAll(currentRoots.keySet())
@@ -219,6 +212,21 @@
return TraversalContinuation.doContinue();
}
+ private void addNodeToRootAndTail(
+ Map<Value, StringBuilderNode> currentRoots,
+ Map<Value, StringBuilderNode> currentTails,
+ Value value,
+ StringBuilderNode node) {
+ StringBuilderNode currentTail = currentTails.get(value);
+ if (currentTail == null) {
+ currentRoots.put(value, node);
+ currentTails.put(value, node);
+ } else if (shouldAddNodeToGraph(currentTail, node)) {
+ currentTail.addSuccessor(node);
+ currentTails.put(value, node);
+ }
+ }
+
private boolean shouldAddNodeToGraph(
StringBuilderNode insertedNode, StringBuilderNode newNode) {
// No need for multiple mutating nodes or inspecting nodes.
@@ -226,6 +234,8 @@
return !newNode.isMutateNode() && !newNode.isInspectingNode();
} else if (insertedNode.isInspectingNode()) {
return !newNode.isInspectingNode();
+ } else if (insertedNode.isEscapeNode()) {
+ return !newNode.isEscapeNode();
}
return true;
}
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 90c6c2b..b66dd62 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
@@ -2122,35 +2122,36 @@
needsRegisterPair,
usePositions,
Type.CONST_NUMBER);
- if (candidate != Integer.MAX_VALUE) {
- // Look for a non-const, non-monitor candidate.
- int otherCandidate =
- getLargestValidCandidate(
- unhandledInterval, registerConstraint, needsRegisterPair, usePositions, Type.OTHER);
- if (otherCandidate == Integer.MAX_VALUE || candidate == REGISTER_CANDIDATE_NOT_FOUND) {
+ // Look for a non-const, non-monitor candidate.
+ int otherCandidate =
+ getLargestValidCandidate(
+ unhandledInterval, registerConstraint, needsRegisterPair, usePositions, Type.OTHER);
+ if (otherCandidate != REGISTER_CANDIDATE_NOT_FOUND) {
+ // There is a potential other candidate, check if that should be used instead.
+ if (candidate == REGISTER_CANDIDATE_NOT_FOUND) {
candidate = otherCandidate;
} else {
int largestConstUsePosition =
getLargestPosition(usePositions, candidate, needsRegisterPair);
- if (largestConstUsePosition - MIN_CONSTANT_FREE_FOR_POSITIONS <
- unhandledInterval.getStart()) {
+ if (largestConstUsePosition - MIN_CONSTANT_FREE_FOR_POSITIONS
+ < unhandledInterval.getStart()) {
// The candidate that can be rematerialized has a live range too short to use it.
candidate = otherCandidate;
}
}
+ }
- // If looking at constants and non-monitor registers did not find a valid spill candidate
- // we allow ourselves to look at monitor spill candidates as well. Registers holding objects
- // used as monitors should not be spilled if we can avoid it. Spilling them can lead
- // to Art lock verification issues.
- // Also, at this point we still don't allow splitting any string new-instance instructions
- // that have been explicitly blocked. Doing so could lead to a behavioral bug on some ART
- // runtimes (b/80118070). To remove this restriction, we would need to know when the call to
- // <init> has definitely happened, and would be safe to split the value after that point.
- if (candidate == REGISTER_CANDIDATE_NOT_FOUND) {
- candidate = getLargestValidCandidate(
- unhandledInterval, registerConstraint, needsRegisterPair, usePositions, Type.MONITOR);
- }
+ // If looking at constants and non-monitor registers did not find a valid spill candidate
+ // we allow ourselves to look at monitor spill candidates as well. Registers holding objects
+ // used as monitors should not be spilled if we can avoid it. Spilling them can lead
+ // to Art lock verification issues.
+ // Also, at this point we still don't allow splitting any string new-instance instructions
+ // that have been explicitly blocked. Doing so could lead to a behavioral bug on some ART
+ // runtimes (b/80118070). To remove this restriction, we would need to know when the call to
+ // <init> has definitely happened, and would be safe to split the value after that point.
+ if (candidate == REGISTER_CANDIDATE_NOT_FOUND) {
+ candidate = getLargestValidCandidate(
+ unhandledInterval, registerConstraint, needsRegisterPair, usePositions, Type.MONITOR);
}
int largestUsePosition = getLargestPosition(usePositions, candidate, needsRegisterPair);
diff --git a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
index 19febfd..c2bbb70 100644
--- a/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/jar/CfApplicationWriter.java
@@ -513,7 +513,8 @@
for (int i = 0; i < names.getValues().length; i++) {
DexValueString name = names.getValues()[i].asDexValueString();
DexValueInt access = accessFlags.getValues()[i].asDexValueInt();
- visitor.visitParameter(name.value.toString(), access.value);
+ String nameString = name != null ? name.value.toString() : null;
+ visitor.visitParameter(nameString, access.value);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/lightir/ByteUtils.java b/src/main/java/com/android/tools/r8/lightir/ByteUtils.java
index dc174ff..559685a 100644
--- a/src/main/java/com/android/tools/r8/lightir/ByteUtils.java
+++ b/src/main/java/com/android/tools/r8/lightir/ByteUtils.java
@@ -8,17 +8,20 @@
/** Simple utilities for byte encodings. */
public class ByteUtils {
+ public static final int MAX_U1 = 0xFF;
+ public static final int MAX_U2 = 0xFFFF;
+
public static boolean isU1(int value) {
- return (0 <= value) && (value <= 0xFF);
+ return (0 <= value) && (value <= MAX_U1);
}
// Lossy truncation of an integer value to its lowest byte.
private static int truncateToU1(int value) {
- return value & 0xFF;
+ return value & MAX_U1;
}
private static int truncateToU1(long value) {
- return (int) value & 0xFF;
+ return (int) value & MAX_U1;
}
public static int ensureU1(int value) {
@@ -27,7 +30,7 @@
}
public static int fromU1(byte value) {
- return value & 0xFF;
+ return value & MAX_U1;
}
public static int intEncodingSize(int value) {
@@ -81,11 +84,11 @@
}
public static boolean isU2(int value) {
- return (value >= 0) && (value <= 0xFFFF);
+ return (value >= 0) && (value <= MAX_U2);
}
private static int truncateToU2(int value) {
- return value & 0xFFFF;
+ return value & MAX_U2;
}
public static int ensureU2(int value) {
diff --git a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
index bda771a..421be9b 100644
--- a/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
+++ b/src/main/java/com/android/tools/r8/lightir/Lir2IRConverter.java
@@ -6,8 +6,11 @@
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
@@ -15,6 +18,7 @@
import com.android.tools.r8.ir.analysis.type.PrimitiveTypeElement;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Add;
+import com.android.tools.r8.ir.code.And;
import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.ArrayGet;
import com.android.tools.r8.ir.code.ArrayLength;
@@ -27,21 +31,27 @@
import com.android.tools.r8.ir.code.ConstClass;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.ConstString;
+import com.android.tools.r8.ir.code.DebugLocalRead;
import com.android.tools.r8.ir.code.DebugLocalWrite;
import com.android.tools.r8.ir.code.DebugPosition;
+import com.android.tools.r8.ir.code.DexItemBasedConstString;
import com.android.tools.r8.ir.code.Div;
import com.android.tools.r8.ir.code.Goto;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.IfType;
+import com.android.tools.r8.ir.code.InitClass;
import com.android.tools.r8.ir.code.InstanceGet;
import com.android.tools.r8.ir.code.InstanceOf;
import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.IntSwitch;
+import com.android.tools.r8.ir.code.InvokeCustom;
import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeInterface;
+import com.android.tools.r8.ir.code.InvokeMultiNewArray;
import com.android.tools.r8.ir.code.InvokeNewArray;
+import com.android.tools.r8.ir.code.InvokePolymorphic;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeSuper;
import com.android.tools.r8.ir.code.InvokeVirtual;
@@ -50,27 +60,35 @@
import com.android.tools.r8.ir.code.MonitorType;
import com.android.tools.r8.ir.code.MoveException;
import com.android.tools.r8.ir.code.Mul;
+import com.android.tools.r8.ir.code.Neg;
import com.android.tools.r8.ir.code.NewArrayEmpty;
import com.android.tools.r8.ir.code.NewArrayFilledData;
import com.android.tools.r8.ir.code.NewInstance;
+import com.android.tools.r8.ir.code.NewUnboxedEnumInstance;
+import com.android.tools.r8.ir.code.Not;
import com.android.tools.r8.ir.code.NumberConversion;
import com.android.tools.r8.ir.code.NumberGenerator;
import com.android.tools.r8.ir.code.NumericType;
+import com.android.tools.r8.ir.code.Or;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Position.SyntheticPosition;
import com.android.tools.r8.ir.code.Rem;
import com.android.tools.r8.ir.code.Return;
+import com.android.tools.r8.ir.code.Shl;
+import com.android.tools.r8.ir.code.Shr;
import com.android.tools.r8.ir.code.StaticGet;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.code.Sub;
import com.android.tools.r8.ir.code.Throw;
+import com.android.tools.r8.ir.code.Ushr;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.code.Xor;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
import com.android.tools.r8.lightir.LirCode.PositionEntry;
+import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
import com.android.tools.r8.utils.ListUtils;
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
@@ -148,7 +166,7 @@
// Control instructions must close the block, thus the current block is null iff the
// instruction denotes a new block.
if (currentBlock == null) {
- currentBlock = blocks.computeIfAbsent(nextInstructionIndex, k -> new BasicBlock());
+ currentBlock = getBasicBlock(nextInstructionIndex);
CatchHandlers<Integer> handlers =
code.getTryCatchTable().getHandlersForBlock(nextInstructionIndex);
if (handlers != null) {
@@ -418,6 +436,48 @@
}
@Override
+ public void onNeg(NumericType type, EV value) {
+ Value dest = getOutValueForNextInstruction(valueTypeElement(type));
+ addInstruction(new Neg(type, dest, getValue(value)));
+ }
+
+ @Override
+ public void onNot(NumericType type, EV value) {
+ Value dest = getOutValueForNextInstruction(valueTypeElement(type));
+ addInstruction(new Not(type, dest, getValue(value)));
+ }
+
+ @Override
+ public void onShl(NumericType type, EV left, EV right) {
+ Value dest = getOutValueForNextInstruction(valueTypeElement(type));
+ addInstruction(new Shl(type, dest, getValue(left), getValue(right)));
+ }
+
+ @Override
+ public void onShr(NumericType type, EV left, EV right) {
+ Value dest = getOutValueForNextInstruction(valueTypeElement(type));
+ addInstruction(new Shr(type, dest, getValue(left), getValue(right)));
+ }
+
+ @Override
+ public void onUshr(NumericType type, EV left, EV right) {
+ Value dest = getOutValueForNextInstruction(valueTypeElement(type));
+ addInstruction(new Ushr(type, dest, getValue(left), getValue(right)));
+ }
+
+ @Override
+ public void onAnd(NumericType type, EV left, EV right) {
+ Value dest = getOutValueForNextInstruction(valueTypeElement(type));
+ addInstruction(And.createNonNormalized(type, dest, getValue(left), getValue(right)));
+ }
+
+ @Override
+ public void onOr(NumericType type, EV left, EV right) {
+ Value dest = getOutValueForNextInstruction(valueTypeElement(type));
+ addInstruction(Or.createNonNormalized(type, dest, getValue(left), getValue(right)));
+ }
+
+ @Override
public void onXor(NumericType type, EV left, EV right) {
Value dest = getOutValueForNextInstruction(valueTypeElement(type));
addInstruction(Xor.createNonNormalized(type, dest, getValue(left), getValue(right)));
@@ -425,11 +485,22 @@
@Override
public void onConstString(DexString string) {
- Value dest = getOutValueForNextInstruction(TypeElement.stringClassType(appView));
+ Value dest =
+ getOutValueForNextInstruction(
+ TypeElement.stringClassType(appView, Nullability.definitelyNotNull()));
addInstruction(new ConstString(dest, string));
}
@Override
+ public void onDexItemBasedConstString(
+ DexReference item, NameComputationInfo<?> nameComputationInfo) {
+ Value dest =
+ getOutValueForNextInstruction(
+ TypeElement.stringClassType(appView, Nullability.definitelyNotNull()));
+ addInstruction(new DexItemBasedConstString(dest, item, nameComputationInfo));
+ }
+
+ @Override
public void onConstClass(DexType type) {
Value dest =
getOutValueForNextInstruction(
@@ -553,10 +624,31 @@
addInstruction(instruction);
}
+ @Override
+ public void onInvokeCustom(DexCallSite callSite, List<EV> arguments) {
+ Value dest = getInvokeInstructionOutputValue(callSite.methodProto);
+ List<Value> ssaArgumentValues = getValues(arguments);
+ InvokeCustom instruction = new InvokeCustom(callSite, dest, ssaArgumentValues);
+ addInstruction(instruction);
+ }
+
+ @Override
+ public void onInvokePolymorphic(DexMethod target, DexProto proto, List<EV> arguments) {
+ Value dest = getInvokeInstructionOutputValue(target);
+ List<Value> ssaArgumentValues = getValues(arguments);
+ InvokePolymorphic instruction = new InvokePolymorphic(target, proto, dest, ssaArgumentValues);
+ addInstruction(instruction);
+ }
+
private Value getInvokeInstructionOutputValue(DexMethod target) {
- return target.getReturnType().isVoidType()
+ return getInvokeInstructionOutputValue(target.getProto());
+ }
+
+ private Value getInvokeInstructionOutputValue(DexProto target) {
+ DexType returnType = target.getReturnType();
+ return returnType.isVoidType()
? null
- : getOutValueForNextInstruction(target.getReturnType().toTypeElement(appView));
+ : getOutValueForNextInstruction(returnType.toTypeElement(appView));
}
@Override
@@ -585,7 +677,8 @@
@Override
public void onInstancePut(DexField field, EV object, EV value) {
- addInstruction(new InstancePut(field, getValue(object), getValue(value)));
+ addInstruction(
+ InstancePut.createPotentiallyInvalid(field, getValue(object), getValue(value)));
}
@Override
@@ -663,6 +756,19 @@
}
@Override
+ public void onDebugLocalRead() {
+ addInstruction(new DebugLocalRead());
+ }
+
+ @Override
+ public void onInvokeMultiNewArray(DexType type, List<EV> arguments) {
+ Value dest =
+ getOutValueForNextInstruction(
+ type.toTypeElement(appView, Nullability.definitelyNotNull()));
+ addInstruction(new InvokeMultiNewArray(type, dest, getValues(arguments)));
+ }
+
+ @Override
public void onInvokeNewArray(DexType type, List<EV> arguments) {
Value dest =
getOutValueForNextInstruction(
@@ -740,5 +846,18 @@
ArrayPut.createWithoutVerification(
type, getValue(array), getValue(index), getValue(value)));
}
+
+ @Override
+ public void onNewUnboxedEnumInstance(DexType clazz, int ordinal) {
+ TypeElement type = TypeElement.fromDexType(clazz, Nullability.definitelyNotNull(), appView);
+ Value dest = getOutValueForNextInstruction(type);
+ addInstruction(new NewUnboxedEnumInstance(clazz, ordinal, dest));
+ }
+
+ @Override
+ public void onInitClass(DexType clazz) {
+ Value dest = getOutValueForNextInstruction(TypeElement.getInt());
+ addInstruction(new InitClass(dest, clazz));
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
index 7a88ae2..60df1eb 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirBuilder.java
@@ -11,10 +11,13 @@
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DebugLocalInfo;
+import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.TypeElement;
@@ -33,6 +36,7 @@
import com.android.tools.r8.lightir.LirCode.DebugLocalInfoTable;
import com.android.tools.r8.lightir.LirCode.PositionEntry;
import com.android.tools.r8.lightir.LirCode.TryCatchTable;
+import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
import com.android.tools.r8.utils.ListUtils;
import com.google.common.collect.ImmutableList;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -125,6 +129,14 @@
}
}
+ public static class NameComputationPayload extends InstructionPayload {
+ public final NameComputationInfo<?> nameComputationInfo;
+
+ public NameComputationPayload(NameComputationInfo<?> nameComputationInfo) {
+ this.nameComputationInfo = nameComputationInfo;
+ }
+ }
+
public LirBuilder(DexMethod method, LirEncodingStrategy<V, EV> strategy, DexItemFactory factory) {
this.factory = factory;
constants = new Reference2IntOpenHashMap<>();
@@ -379,6 +391,48 @@
return addOneItemInstruction(LirOpcodes.LDC, type);
}
+ public LirBuilder<V, EV> addNeg(NumericType type, V value) {
+ int opcode;
+ switch (type) {
+ case BYTE:
+ case CHAR:
+ case SHORT:
+ case INT:
+ opcode = LirOpcodes.INEG;
+ break;
+ case LONG:
+ opcode = LirOpcodes.LNEG;
+ break;
+ case FLOAT:
+ opcode = LirOpcodes.FNEG;
+ break;
+ case DOUBLE:
+ opcode = LirOpcodes.DNEG;
+ break;
+ default:
+ throw new Unreachable("Unexpected type: " + type);
+ }
+ return addOneValueInstruction(opcode, value);
+ }
+
+ public LirBuilder<V, EV> addNot(NumericType type, V value) {
+ int opcode;
+ switch (type) {
+ case BYTE:
+ case CHAR:
+ case SHORT:
+ case INT:
+ opcode = LirOpcodes.INOT;
+ break;
+ case LONG:
+ opcode = LirOpcodes.LNOT;
+ break;
+ default:
+ throw new Unreachable("Unexpected type: " + type);
+ }
+ return addOneValueInstruction(opcode, value);
+ }
+
public LirBuilder<V, EV> addDiv(NumericType type, V leftValue, V rightValue) {
int opcode;
switch (type) {
@@ -445,7 +499,7 @@
LirOpcodes.PUTFIELD, Collections.singletonList(field), ImmutableList.of(object, value));
}
- public LirBuilder<V, EV> addInvokeInstruction(int opcode, DexMethod method, List<V> arguments) {
+ public LirBuilder<V, EV> addInvokeInstruction(int opcode, DexItem method, List<V> arguments) {
return addInstructionTemplate(opcode, Collections.singletonList(method), arguments);
}
@@ -475,6 +529,16 @@
return addInvokeInstruction(LirOpcodes.INVOKEINTERFACE, method, arguments);
}
+ public LirBuilder<V, EV> addInvokeCustom(DexCallSite callSite, List<V> arguments) {
+ return addInvokeInstruction(LirOpcodes.INVOKEDYNAMIC, callSite, arguments);
+ }
+
+ public LirBuilder<V, EV> addInvokePolymorphic(
+ DexMethod invokedMethod, DexProto proto, List<V> arguments) {
+ return addInstructionTemplate(
+ LirOpcodes.INVOKEPOLYMORPHIC, ImmutableList.of(invokedMethod, proto), arguments);
+ }
+
public LirBuilder<V, EV> addNewInstance(DexType clazz) {
return addOneItemInstruction(LirOpcodes.NEW, clazz);
}
@@ -613,6 +677,10 @@
return addOneValueInstruction(LirOpcodes.DEBUGLOCALWRITE, src);
}
+ public LirBuilder<V, EV> addDebugLocalRead() {
+ return addNoOperandInstruction(LirOpcodes.DEBUGLOCALREAD);
+ }
+
public LirCode<EV> build() {
assert metadata != null;
int constantsCount = constants.size();
@@ -679,6 +747,11 @@
LirOpcodes.NEWARRAY, Collections.singletonList(type), Collections.singletonList(size));
}
+ public LirBuilder<V, EV> addInvokeMultiNewArray(DexType type, List<V> arguments) {
+ return addInstructionTemplate(
+ LirOpcodes.MULTIANEWARRAY, Collections.singletonList(type), arguments);
+ }
+
public LirBuilder<V, EV> addInvokeNewArray(DexType type, List<V> arguments) {
return addInstructionTemplate(
LirOpcodes.INVOKENEWARRAY, Collections.singletonList(type), arguments);
@@ -767,4 +840,24 @@
return addInstructionTemplate(
opcode, Collections.emptyList(), ImmutableList.of(array, index, value));
}
+
+ public LirBuilder<V, EV> addDexItemBasedConstString(
+ DexReference item, NameComputationInfo<?> nameComputationInfo) {
+ NameComputationPayload payload = new NameComputationPayload(nameComputationInfo);
+ return addInstructionTemplate(
+ LirOpcodes.ITEMBASEDCONSTSTRING, ImmutableList.of(item, payload), Collections.emptyList());
+ }
+
+ public LirBuilder<V, EV> addNewUnboxedEnumInstance(DexType clazz, int ordinal) {
+ advanceInstructionState();
+ int operandSize = constantIndexSize(clazz) + ByteUtils.intEncodingSize(ordinal);
+ writer.writeInstruction(LirOpcodes.NEWUNBOXEDENUMINSTANCE, operandSize);
+ writeConstantIndex(clazz);
+ ByteUtils.writeEncodedInt(ordinal, writer::writeOperand);
+ return this;
+ }
+
+ public LirBuilder<V, EV> addInitClass(DexType clazz) {
+ return addOneItemInstruction(LirOpcodes.INITCLASS, clazz);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirIterator.java b/src/main/java/com/android/tools/r8/lightir/LirIterator.java
index 84eaee6..0d9ccbd 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirIterator.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirIterator.java
@@ -51,6 +51,10 @@
// Any instruction that is not a single byte has a two-byte header. The second byte is the
// size of the variable width operand payload.
int operandSize = u1();
+ if (operandSize == 0) {
+ // Zero is used to indicate the operand size is larger than a u1 encoded value.
+ operandSize = u4();
+ }
endOfCurrentInstruction = currentByteIndex + operandSize;
}
return this;
diff --git a/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java b/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
index c4f2076..0f0811d 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirOpcodes.java
@@ -14,7 +14,11 @@
static boolean isOneByteInstruction(int opcode) {
assert opcode >= ACONST_NULL;
- return opcode <= DCONST_1 || opcode == RETURN || opcode == DEBUGPOS || opcode == FALLTHROUGH;
+ return opcode <= DCONST_1
+ || opcode == RETURN
+ || opcode == DEBUGPOS
+ || opcode == FALLTHROUGH
+ || opcode == DEBUGLOCALREAD;
}
// Instructions maintaining the same opcode as defined in CF.
@@ -196,6 +200,13 @@
int DEBUGLOCALWRITE = 213;
int INVOKENEWARRAY = 214;
int NEWARRAYFILLEDDATA = 215;
+ int ITEMBASEDCONSTSTRING = 216;
+ int NEWUNBOXEDENUMINSTANCE = 217;
+ int INOT = 218;
+ int LNOT = 219;
+ int DEBUGLOCALREAD = 220;
+ int INITCLASS = 221;
+ int INVOKEPOLYMORPHIC = 222;
static String toString(int opcode) {
switch (opcode) {
@@ -512,6 +523,20 @@
return "INVOKENEWARRAY";
case NEWARRAYFILLEDDATA:
return "NEWARRAYFILLEDDATA";
+ case ITEMBASEDCONSTSTRING:
+ return "ITEMBASEDCONSTSTRING";
+ case NEWUNBOXEDENUMINSTANCE:
+ return "NEWUNBOXEDENUMINSTANCE";
+ case INOT:
+ return "INOT";
+ case LNOT:
+ return "LNOT";
+ case DEBUGLOCALREAD:
+ return "DEBUGLOCALREAD";
+ case INITCLASS:
+ return "INITCLASS";
+ case INVOKEPOLYMORPHIC:
+ return "INVOKEPOLYMORPHIC";
default:
throw new Unreachable("Unexpected LIR opcode: " + opcode);
diff --git a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
index d418d96..781db23 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirParsedInstructionCallback.java
@@ -6,9 +6,12 @@
import com.android.tools.r8.cf.code.CfNumberConversion;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexItem;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.IfType;
@@ -16,6 +19,8 @@
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.lightir.LirBuilder.FillArrayPayload;
import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
+import com.android.tools.r8.lightir.LirBuilder.NameComputationPayload;
+import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
import java.util.ArrayList;
import java.util.List;
@@ -87,6 +92,11 @@
onInstruction();
}
+ public void onDexItemBasedConstString(
+ DexReference item, NameComputationInfo<?> nameComputationInfo) {
+ onInstruction();
+ }
+
public void onConstClass(DexType type) {
onInstruction();
}
@@ -123,10 +133,18 @@
onInstruction();
}
- public void onAdd(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+ public void onBinop(NumericType type, EV left, EV right) {
onInstruction();
}
+ public void onArithmeticBinop(NumericType type, EV left, EV right) {
+ onBinop(type, left, right);
+ }
+
+ public void onAdd(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+ onArithmeticBinop(type, leftValueIndex, rightValueIndex);
+ }
+
public void onAddInt(EV leftValueIndex, EV rightValueIndex) {
onAdd(NumericType.INT, leftValueIndex, rightValueIndex);
}
@@ -144,7 +162,7 @@
}
public void onSub(NumericType type, EV leftValueIndex, EV rightValueIndex) {
- onInstruction();
+ onArithmeticBinop(type, leftValueIndex, rightValueIndex);
}
public void onSubInt(EV leftValueIndex, EV rightValueIndex) {
@@ -164,7 +182,7 @@
}
public void onMul(NumericType type, EV leftValueIndex, EV rightValueIndex) {
- onInstruction();
+ onArithmeticBinop(type, leftValueIndex, rightValueIndex);
}
public void onMulInt(EV leftValueIndex, EV rightValueIndex) {
@@ -184,7 +202,7 @@
}
public void onDiv(NumericType type, EV leftValueIndex, EV rightValueIndex) {
- onInstruction();
+ onArithmeticBinop(type, leftValueIndex, rightValueIndex);
}
public void onDivInt(EV leftValueIndex, EV rightValueIndex) {
@@ -204,7 +222,7 @@
}
public void onRem(NumericType type, EV leftValueIndex, EV rightValueIndex) {
- onInstruction();
+ onArithmeticBinop(type, leftValueIndex, rightValueIndex);
}
public void onRemInt(EV leftValueIndex, EV rightValueIndex) {
@@ -223,34 +241,85 @@
onRem(NumericType.DOUBLE, leftValueIndex, rightValueIndex);
}
+ public void onNeg(NumericType type, EV value) {
+ onInstruction();
+ }
+
+ public void onNot(NumericType type, EV value) {
+ onInstruction();
+ }
+
private void onLogicalBinopInternal(int opcode, LirInstructionView view) {
EV left = getNextValueOperand(view);
EV right = getNextValueOperand(view);
switch (opcode) {
case LirOpcodes.ISHL:
+ onShl(NumericType.INT, left, right);
+ return;
case LirOpcodes.LSHL:
+ onShl(NumericType.LONG, left, right);
+ return;
case LirOpcodes.ISHR:
+ onShr(NumericType.INT, left, right);
+ return;
case LirOpcodes.LSHR:
+ onShr(NumericType.LONG, left, right);
+ return;
case LirOpcodes.IUSHR:
+ onUshr(NumericType.INT, left, right);
+ return;
case LirOpcodes.LUSHR:
+ onUshr(NumericType.LONG, left, right);
+ return;
case LirOpcodes.IAND:
+ onAnd(NumericType.INT, left, right);
+ return;
case LirOpcodes.LAND:
+ onAnd(NumericType.LONG, left, right);
+ return;
case LirOpcodes.IOR:
+ onOr(NumericType.INT, left, right);
+ return;
case LirOpcodes.LOR:
- throw new Unimplemented();
+ onOr(NumericType.LONG, left, right);
+ return;
case LirOpcodes.IXOR:
onXor(NumericType.INT, left, right);
return;
case LirOpcodes.LXOR:
- onXor(NumericType.INT, left, right);
+ onXor(NumericType.LONG, left, right);
return;
default:
throw new Unreachable("Unexpected logical binop: " + opcode);
}
}
+ public void onLogicalBinop(NumericType type, EV left, EV right) {
+ onBinop(type, left, right);
+ }
+
+ public void onShl(NumericType type, EV left, EV right) {
+ onLogicalBinop(type, left, right);
+ }
+
+ public void onShr(NumericType type, EV left, EV right) {
+ onLogicalBinop(type, left, right);
+ }
+
+ public void onUshr(NumericType type, EV left, EV right) {
+ onLogicalBinop(type, left, right);
+ }
+
+ public void onAnd(NumericType type, EV left, EV right) {
+ onLogicalBinop(type, left, right);
+ }
+
+ public void onOr(NumericType type, EV left, EV right) {
+ onLogicalBinop(type, left, right);
+ }
+
public void onXor(NumericType type, EV left, EV right) {
- onInstruction();
+ onLogicalBinop(type, left, right);
}
public void onNumberConversion(int opcode, EV value) {
@@ -305,6 +374,14 @@
onInstruction();
}
+ public void onDebugLocalRead() {
+ onInstruction();
+ }
+
+ public void onInvokeMultiNewArray(DexType type, List<EV> arguments) {
+ onInstruction();
+ }
+
public void onInvokeNewArray(DexType type, List<EV> arguments) {
onInstruction();
}
@@ -337,6 +414,14 @@
onInvokeMethodInstruction(method, arguments);
}
+ public void onInvokeCustom(DexCallSite callSite, List<EV> arguments) {
+ onInstruction();
+ }
+
+ public void onInvokePolymorphic(DexMethod target, DexProto proto, List<EV> arguments) {
+ onInstruction();
+ }
+
public void onNewInstance(DexType clazz) {
onInstruction();
}
@@ -398,6 +483,14 @@
public abstract void onMonitorExit(EV value);
+ public void onNewUnboxedEnumInstance(DexType type, int ordinal) {
+ onInstruction();
+ }
+
+ public void onInitClass(DexType clazz) {
+ onInstruction();
+ }
+
private DexItem getConstantItem(int index) {
return code.getConstantItem(index);
}
@@ -714,6 +807,30 @@
onRemDouble(leftValueIndex, rightValueIndex);
return;
}
+ case LirOpcodes.INEG:
+ {
+ EV value = getNextValueOperand(view);
+ onNeg(NumericType.INT, value);
+ return;
+ }
+ case LirOpcodes.LNEG:
+ {
+ EV value = getNextValueOperand(view);
+ onNeg(NumericType.LONG, value);
+ return;
+ }
+ case LirOpcodes.FNEG:
+ {
+ EV value = getNextValueOperand(view);
+ onNeg(NumericType.FLOAT, value);
+ return;
+ }
+ case LirOpcodes.DNEG:
+ {
+ EV value = getNextValueOperand(view);
+ onNeg(NumericType.DOUBLE, value);
+ return;
+ }
case LirOpcodes.ISHL:
case LirOpcodes.LSHL:
case LirOpcodes.ISHR:
@@ -882,6 +999,21 @@
onInvokeInterface(target, arguments);
return;
}
+ case LirOpcodes.INVOKEDYNAMIC:
+ {
+ DexCallSite callSite = (DexCallSite) getConstantItem(view.getNextConstantOperand());
+ List<EV> arguments = getInvokeInstructionArguments(view);
+ onInvokeCustom(callSite, arguments);
+ return;
+ }
+ case LirOpcodes.INVOKEPOLYMORPHIC:
+ {
+ DexMethod target = getInvokeInstructionTarget(view);
+ DexProto proto = (DexProto) getConstantItem(view.getNextConstantOperand());
+ List<EV> arguments = getInvokeInstructionArguments(view);
+ onInvokePolymorphic(target, proto, arguments);
+ return;
+ }
case LirOpcodes.NEW:
{
DexItem item = getConstantItem(view.getNextConstantOperand());
@@ -1006,6 +1138,18 @@
onDebugLocalWrite(srcIndex);
return;
}
+ case LirOpcodes.DEBUGLOCALREAD:
+ {
+ onDebugLocalRead();
+ return;
+ }
+ case LirOpcodes.MULTIANEWARRAY:
+ {
+ DexType type = getNextDexTypeOperand(view);
+ List<EV> arguments = getInvokeInstructionArguments(view);
+ onInvokeMultiNewArray(type, arguments);
+ return;
+ }
case LirOpcodes.INVOKENEWARRAY:
{
DexType type = getNextDexTypeOperand(view);
@@ -1032,6 +1176,34 @@
onCmpInstruction(opcode, leftValue, rightValue);
return;
}
+ case LirOpcodes.ITEMBASEDCONSTSTRING:
+ {
+ DexReference item = (DexReference) getConstantItem(view.getNextConstantOperand());
+ NameComputationPayload payload =
+ (NameComputationPayload) getConstantItem(view.getNextConstantOperand());
+ onDexItemBasedConstString(item, payload.nameComputationInfo);
+ return;
+ }
+ case LirOpcodes.NEWUNBOXEDENUMINSTANCE:
+ {
+ DexType type = getNextDexTypeOperand(view);
+ int ordinal = view.getNextIntegerOperand();
+ onNewUnboxedEnumInstance(type, ordinal);
+ return;
+ }
+ case LirOpcodes.INOT:
+ case LirOpcodes.LNOT:
+ {
+ EV value = getNextValueOperand(view);
+ onNot(opcode == LirOpcodes.INOT ? NumericType.INT : NumericType.LONG, value);
+ return;
+ }
+ case LirOpcodes.INITCLASS:
+ {
+ DexType clazz = getNextDexTypeOperand(view);
+ onInitClass(clazz);
+ return;
+ }
default:
throw new Unimplemented("No dispatch for opcode " + LirOpcodes.toString(opcode));
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
index e19661e..b20e303 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirPrinter.java
@@ -4,14 +4,18 @@
package com.android.tools.r8.lightir;
import com.android.tools.r8.errors.Unimplemented;
+import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.code.IfType;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NumericType;
import com.android.tools.r8.lightir.LirBuilder.IntSwitchPayload;
+import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
import com.android.tools.r8.utils.StringUtils;
import java.util.Arrays;
import java.util.List;
@@ -151,32 +155,32 @@
}
@Override
+ public void onDexItemBasedConstString(
+ DexReference item, NameComputationInfo<?> nameComputationInfo) {
+ appendOutValue().append("item(").append(item).append(")");
+ }
+
+ @Override
public void onConstClass(DexType type) {
appendOutValue().append("class(").append(type).append(")");
}
@Override
- public void onAdd(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+ public void onBinop(NumericType type, EV left, EV right) {
appendOutValue();
- appendValueArguments(leftValueIndex, rightValueIndex);
+ appendValueArguments(left, right);
}
@Override
- public void onSub(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+ public void onNeg(NumericType type, EV value) {
appendOutValue();
- appendValueArguments(leftValueIndex, rightValueIndex);
+ appendValueArguments(value);
}
@Override
- public void onDiv(NumericType type, EV leftValueIndex, EV rightValueIndex) {
+ public void onNot(NumericType type, EV value) {
appendOutValue();
- appendValueArguments(leftValueIndex, rightValueIndex);
- }
-
- @Override
- public void onXor(NumericType type, EV leftValueIndex, EV rightValueIndex) {
- appendOutValue();
- appendValueArguments(leftValueIndex, rightValueIndex);
+ appendValueArguments(value);
}
@Override
@@ -224,6 +228,18 @@
}
@Override
+ public void onDebugLocalRead() {
+ // Nothing to add.
+ }
+
+ @Override
+ public void onInvokeMultiNewArray(DexType type, List<EV> arguments) {
+ appendOutValue();
+ appendValueArguments(arguments);
+ builder.append(type);
+ }
+
+ @Override
public void onInvokeNewArray(DexType type, List<EV> arguments) {
appendOutValue();
appendValueArguments(arguments);
@@ -252,6 +268,18 @@
}
@Override
+ public void onInvokeCustom(DexCallSite callSite, List<EV> arguments) {
+ appendValueArguments(arguments);
+ builder.append(callSite);
+ }
+
+ @Override
+ public void onInvokePolymorphic(DexMethod target, DexProto proto, List<EV> arguments) {
+ appendValueArguments(arguments);
+ builder.append(target).append(' ').append(proto);
+ }
+
+ @Override
public void onStaticGet(DexField field) {
appendOutValue();
builder.append(field).append(' ');
@@ -364,4 +392,14 @@
public void onMonitorExit(EV value) {
appendValueArguments(value);
}
+
+ @Override
+ public void onNewUnboxedEnumInstance(DexType type, int ordinal) {
+ appendOutValue().append("type(").append(type).append(") ordinal(").append(ordinal).append(")");
+ }
+
+ @Override
+ public void onInitClass(DexType clazz) {
+ builder.append(clazz);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/lightir/LirWriter.java b/src/main/java/com/android/tools/r8/lightir/LirWriter.java
index 3f9e6b4..b7e4565 100644
--- a/src/main/java/com/android/tools/r8/lightir/LirWriter.java
+++ b/src/main/java/com/android/tools/r8/lightir/LirWriter.java
@@ -25,9 +25,15 @@
}
public void writeInstruction(int opcode, int operandsSizeInBytes) {
+ assert operandsSizeInBytes > 0;
assert pendingOperandBytes == 0;
writer.put(ByteUtils.ensureU1(opcode));
- writer.put(ByteUtils.ensureU1(operandsSizeInBytes));
+ if (operandsSizeInBytes <= ByteUtils.MAX_U1) {
+ writer.put(ByteUtils.ensureU1(operandsSizeInBytes));
+ } else {
+ writer.put(0);
+ ByteUtils.writeEncodedInt(operandsSizeInBytes, writer);
+ }
pendingOperandBytes = operandsSizeInBytes;
}
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
index cfd7b32..ad33448 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNameMapper.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.BiMapContainer;
import com.android.tools.r8.utils.ChainableStringConsumer;
+import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
@@ -32,6 +33,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
@@ -247,6 +249,14 @@
return preamble;
}
+ public Set<String> getObfuscatedPackages() {
+ Set<String> packages = new HashSet<>();
+ classNameMappings.forEach(
+ (s, classNamingForNameMapper) ->
+ packages.add(DescriptorUtils.getPackageNameFromTypeName(s)));
+ return packages;
+ }
+
public void setPreamble(List<String> preamble) {
this.preamble = preamble;
}
diff --git a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
index 0da73cf..43c2c1c 100644
--- a/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/IdentifierMinifier.java
@@ -163,6 +163,7 @@
assert code != null;
if (code.isDexCode()) {
DexInstruction[] instructions = code.asDexCode().instructions;
+ boolean updated = false;
for (int i = 0; i < instructions.length; ++i) {
DexInstruction instruction = instructions[i];
if (instruction.isDexItemBasedConstString()) {
@@ -173,8 +174,12 @@
DexConstString constString = new DexConstString(cnst.AA, replacement);
constString.setOffset(instruction.getOffset());
instructions[i] = constString;
+ updated = true;
}
}
+ if (updated) {
+ code.asDexCode().flushCachedValues();
+ }
} else if (code.isCfCode()) {
List<CfInstruction> instructions = code.asCfCode().getInstructions();
List<CfInstruction> newInstructions =
diff --git a/src/main/java/com/android/tools/r8/optimize/AccessModifier.java b/src/main/java/com/android/tools/r8/optimize/AccessModifier.java
index 85533d5..43d46ea 100644
--- a/src/main/java/com/android/tools/r8/optimize/AccessModifier.java
+++ b/src/main/java/com/android/tools/r8/optimize/AccessModifier.java
@@ -15,7 +15,6 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.FieldAccessFlags;
-import com.android.tools.r8.graph.FieldAccessInfo;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramDefinition;
@@ -134,29 +133,10 @@
private void processField(ProgramField field) {
if (appView.appInfo().isAccessModificationAllowed(field)) {
- finalizeField(field);
publicizeField(field);
}
}
- private void finalizeField(ProgramField field) {
- FieldAccessFlags flags = field.getAccessFlags();
- FieldAccessInfo accessInfo =
- appView.appInfo().getFieldAccessInfoCollection().get(field.getReference());
- if (!appView.getKeepInfo(field).isPinned(options)
- && !accessInfo.hasReflectiveWrite()
- && !accessInfo.isWrittenFromMethodHandle()
- && accessInfo.isWrittenOnlyInMethodSatisfying(
- method ->
- method.getDefinition().isInitializer()
- && method.getAccessFlags().isStatic() == flags.isStatic()
- && method.getHolder() == field.getHolder())
- && !flags.isFinal()
- && !flags.isVolatile()) {
- flags.promoteToFinal();
- }
- }
-
private void publicizeField(ProgramField field) {
FieldAccessFlags flags = field.getAccessFlags();
if (!flags.isPublic()) {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
index 00be161..2d47dcc 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.conversion.PostMethodProcessor;
+import com.android.tools.r8.ir.conversion.PrimaryR8IRConverter;
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.VirtualRootMethodsAnalysis;
@@ -139,7 +140,7 @@
}
public void tearDownCodeScanner(
- IRConverter converter,
+ PrimaryR8IRConverter converter,
PostMethodProcessor.Builder postMethodProcessorBuilder,
ExecutorService executorService,
Timing timing)
@@ -205,7 +206,7 @@
* optimization info.
*/
private void populateParameterOptimizationInfo(
- IRConverter converter,
+ PrimaryR8IRConverter converter,
ImmediateProgramSubtypingInfo immediateSubtypingInfo,
List<Set<DexProgramClass>> stronglyConnectedProgramComponents,
BiConsumer<Set<DexProgramClass>, DexMethodSignature> interfaceDispatchOutsideProgram,
@@ -250,15 +251,17 @@
* <p>Therefore, we assert that we only find a method state for direct methods.
*/
public void onMethodPruned(ProgramMethod method) {
- assert codeScanner != null;
- MethodState methodState = codeScanner.getMethodStates().removeOrElse(method, null);
- assert methodState == null || method.getDefinition().belongsToDirectPool();
+ if (codeScanner != null) {
+ MethodState methodState = codeScanner.getMethodStates().removeOrElse(method, null);
+ assert methodState == null || method.getDefinition().belongsToDirectPool();
+ }
assert effectivelyUnusedArgumentsAnalysis != null;
effectivelyUnusedArgumentsAnalysis.onMethodPruned(method);
}
public void onMethodCodePruned(ProgramMethod method) {
- // Intentionally empty.
+ assert effectivelyUnusedArgumentsAnalysis != null;
+ effectivelyUnusedArgumentsAnalysis.onMethodCodePruned(method);
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
index 0b47a5e..f4a7af2 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -15,8 +15,8 @@
import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.PostMethodProcessor;
+import com.android.tools.r8.ir.conversion.PrimaryR8IRConverter;
import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
@@ -38,6 +38,7 @@
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -52,7 +53,7 @@
public class ArgumentPropagatorOptimizationInfoPopulator {
private final AppView<AppInfoWithLiveness> appView;
- private final IRConverter converter;
+ private final PrimaryR8IRConverter converter;
private final MethodStateCollectionByReference methodStates;
private final InternalOptions options;
private final PostMethodProcessor.Builder postMethodProcessorBuilder;
@@ -65,7 +66,7 @@
ArgumentPropagatorOptimizationInfoPopulator(
AppView<AppInfoWithLiveness> appView,
- IRConverter converter,
+ PrimaryR8IRConverter converter,
ImmediateProgramSubtypingInfo immediateSubtypingInfo,
MethodStateCollectionByReference methodStates,
PostMethodProcessor.Builder postMethodProcessorBuilder,
@@ -148,18 +149,42 @@
}
private void setOptimizationInfo(ExecutorService executorService) throws ExecutionException {
+ ProgramMethodSet prunedMethods = ProgramMethodSet.createConcurrent();
ThreadUtils.processItems(
- appView.appInfo().classes(), this::setOptimizationInfo, executorService);
+ appView.appInfo().classes(),
+ clazz -> prunedMethods.addAll(setOptimizationInfo(clazz)),
+ executorService);
+ for (ProgramMethod prunedMethod : prunedMethods) {
+ converter.onMethodPruned(prunedMethod);
+ postMethodProcessorBuilder.remove(prunedMethod, appView.graphLens());
+ }
+ converter.waveDone(ProgramMethodSet.empty(), executorService);
}
- private void setOptimizationInfo(DexProgramClass clazz) {
- clazz.forEachProgramMethod(this::setOptimizationInfo);
+ private ProgramMethodSet setOptimizationInfo(DexProgramClass clazz) {
+ ProgramMethodSet prunedMethods = ProgramMethodSet.create();
+ clazz.forEachProgramMethod(method -> setOptimizationInfo(method, prunedMethods));
+ clazz.getMethodCollection().removeMethods(prunedMethods.toDefinitionSet());
+ return prunedMethods;
}
- private void setOptimizationInfo(ProgramMethod method) {
+ private void setOptimizationInfo(ProgramMethod method, ProgramMethodSet prunedMethods) {
MethodState methodState = methodStates.remove(method);
if (methodState.isBottom()) {
- if (method.getDefinition().hasCode() && !method.getDefinition().isClassInitializer()) {
+ if (method.getDefinition().isClassInitializer()) {
+ return;
+ }
+ // If all uses of a direct method have been removed, we can remove the method. However, if its
+ // return value has been propagated, then we retain it for correct evaluation of -if rules in
+ // the final round of tree shaking.
+ // TODO(b/203188583): Enable pruning of methods with generic signatures. For this to
+ // work we need to pass in a seed to GenericSignatureContextBuilder.create in R8.
+ if (method.getDefinition().belongsToDirectPool()
+ && !method.getOptimizationInfo().returnValueHasBeenPropagated()
+ && !method.getDefinition().getGenericSignature().hasSignature()
+ && !appView.appInfo().isFailedResolutionTarget(method.getReference())) {
+ prunedMethods.add(method);
+ } else if (method.getDefinition().hasCode()) {
method.convertToAbstractOrThrowNullMethod(appView);
converter.onMethodCodePruned(method);
postMethodProcessorBuilder.remove(method, appView.graphLens());
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
index 468b735..6fa0cbc 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/lenscoderewriter/NullCheckInserter.java
@@ -199,7 +199,7 @@
DexField rewrittenField = appView.graphLens().lookupField(field, graphLens);
FieldResolutionResult resolutionResult = appView.appInfo().resolveField(rewrittenField);
return resolutionResult.isSingleFieldResolutionResult()
- && !appView.appInfo().isFieldRead(resolutionResult.getResolvedField());
+ && !appView.appInfo().isFieldRead(resolutionResult.getResolutionPair());
}
return false;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java
index 2bc10c5..c7472d7 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/unusedarguments/EffectivelyUnusedArgumentsAnalysis.java
@@ -217,6 +217,10 @@
}
public void onMethodPruned(ProgramMethod method) {
+ onMethodCodePruned(method);
+ }
+
+ public void onMethodCodePruned(ProgramMethod method) {
for (int argumentIndex = 0;
argumentIndex < method.getDefinition().getNumberOfArguments();
argumentIndex++) {
diff --git a/src/main/java/com/android/tools/r8/optimize/fields/FieldFinalizer.java b/src/main/java/com/android/tools/r8/optimize/fields/FieldFinalizer.java
new file mode 100644
index 0000000..9b4bec4
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/fields/FieldFinalizer.java
@@ -0,0 +1,58 @@
+// Copyright (c) 2023, 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.fields;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+/**
+ * Sets the final flag of fields that are only assigned inside the instance initializers of its
+ * holder class.
+ */
+public class FieldFinalizer {
+
+ private final AppView<AppInfoWithLiveness> appView;
+
+ private FieldFinalizer(AppView<AppInfoWithLiveness> appView) {
+ this.appView = appView;
+ }
+
+ public static void run(
+ AppView<AppInfoWithLiveness> appView, ExecutorService executorService, Timing timing)
+ throws ExecutionException {
+ timing.time("Finalize fields pass", () -> run(appView, executorService));
+ }
+
+ private static void run(AppView<AppInfoWithLiveness> appView, ExecutorService executorService)
+ throws ExecutionException {
+ if (appView.options().isAccessModificationEnabled()
+ && appView.options().isOptimizing()
+ && appView.options().isShrinking()) {
+ new FieldFinalizer(appView).processClasses(executorService);
+ }
+ }
+
+ private void processClasses(ExecutorService executorService) throws ExecutionException {
+ ThreadUtils.processItems(appView.appInfo().classes(), this::processClass, executorService);
+ }
+
+ private void processClass(DexProgramClass clazz) {
+ clazz.forEachProgramField(this::processField);
+ }
+
+ private void processField(ProgramField field) {
+ FieldAccessFlags accessFlags = field.getAccessFlags();
+ if (!accessFlags.isFinal() && !accessFlags.isVolatile() && field.isEffectivelyFinal(appView)) {
+ accessFlags.promoteToFinal();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
index df58e45..0ca1265 100644
--- a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemovalOptions.java
@@ -4,42 +4,9 @@
package com.android.tools.r8.optimize.redundantbridgeremoval;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexLibraryClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.SetUtils;
-import java.util.Collections;
-import java.util.Set;
-
public class RedundantBridgeRemovalOptions {
- private final InternalOptions options;
-
- private boolean enableRetargetingOfConstructorBridgeCalls = false;
- private Set<DexType> noConstructorShrinkingHierarchies;
-
- public RedundantBridgeRemovalOptions(InternalOptions options) {
- this.options = options;
- }
-
- public void clearNoConstructorShrinkingHierarchiesForTesting() {
- noConstructorShrinkingHierarchies = Collections.emptySet();
- }
-
- public RedundantBridgeRemovalOptions ensureInitialized() {
- if (noConstructorShrinkingHierarchies == null) {
- DexItemFactory dexItemFactory = options.dexItemFactory();
- noConstructorShrinkingHierarchies =
- SetUtils.newIdentityHashSet(
- dexItemFactory.androidAppFragment, dexItemFactory.androidAppZygotePreload);
- }
- return this;
- }
-
- public boolean isPlatformReflectingOnDefaultConstructorInSubclasses(DexLibraryClass clazz) {
- return noConstructorShrinkingHierarchies.contains(clazz.getType());
- }
+ private boolean enableRetargetingOfConstructorBridgeCalls = true;
public boolean isRetargetingOfConstructorBridgeCallsEnabled() {
return enableRetargetingOfConstructorBridgeCalls;
diff --git a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
index 49b5d17..2ed2ab5 100644
--- a/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
+++ b/src/main/java/com/android/tools/r8/optimize/redundantbridgeremoval/RedundantBridgeRemover.java
@@ -10,6 +10,8 @@
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ImmediateProgramSubtypingInfo;
+import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodResolutionResult;
import com.android.tools.r8.graph.MethodResolutionResult.FailedResolutionResult;
import com.android.tools.r8.graph.MethodResolutionResult.SingleResolutionResult;
@@ -19,32 +21,31 @@
import com.android.tools.r8.optimize.InvokeSingleTargetExtractor;
import com.android.tools.r8.optimize.InvokeSingleTargetExtractor.InvokeKind;
import com.android.tools.r8.optimize.MemberRebindingIdentityLens;
+import com.android.tools.r8.optimize.argumentpropagation.utils.DepthFirstTopDownClassHierarchyTraversal;
+import com.android.tools.r8.optimize.argumentpropagation.utils.ProgramClassesBidirectedGraph;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepMethodInfo;
-import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.ThreadUtils;
-import com.android.tools.r8.utils.WorkList;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
-import com.google.common.collect.Iterables;
-import java.util.Collections;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
-import java.util.function.Consumer;
public class RedundantBridgeRemover {
private final AppView<AppInfoWithLiveness> appView;
+ private final ImmediateProgramSubtypingInfo immediateSubtypingInfo;
private final RedundantBridgeRemovalOptions redundantBridgeRemovalOptions;
- private final InvokedReflectivelyFromPlatformAnalysis invokedReflectivelyFromPlatformAnalysis =
- new InvokedReflectivelyFromPlatformAnalysis();
+ private final RedundantBridgeRemovalLens.Builder lensBuilder =
+ new RedundantBridgeRemovalLens.Builder();
public RedundantBridgeRemover(AppView<AppInfoWithLiveness> appView) {
this.appView = appView;
- this.redundantBridgeRemovalOptions =
- appView.options().getRedundantBridgeRemovalOptions().ensureInitialized();
+ this.immediateSubtypingInfo = ImmediateProgramSubtypingInfo.create(appView);
+ this.redundantBridgeRemovalOptions = appView.options().getRedundantBridgeRemovalOptions();
}
private DexClassAndMethod getTargetForRedundantBridge(ProgramMethod method) {
@@ -65,9 +66,6 @@
if (!isTargetingSuperMethod(method, targetExtractor.getKind(), target)) {
return null;
}
- if (invokedReflectivelyFromPlatformAnalysis.isMaybeInvokedReflectivelyFromPlatform(method)) {
- return null;
- }
// This is a visibility forward, so check for the direct target.
DexClassAndMethod targetMethod =
appView.appInfo().unsafeResolveMethodDueToDexFormatLegacy(target).getResolutionPair();
@@ -106,6 +104,9 @@
if (kind == InvokeKind.STATIC) {
return appView.appInfo().isStrictSubtypeOf(method.getHolderType(), target.holder);
}
+ if (kind == InvokeKind.VIRTUAL) {
+ return false;
+ }
assert false : "Unexpected invoke-kind for visibility bridge: " + kind;
return false;
}
@@ -117,9 +118,7 @@
|| memberRebindingIdentityLens == appView.graphLens();
// Collect all redundant bridges to remove.
- RedundantBridgeRemovalLens.Builder lensBuilder = new RedundantBridgeRemovalLens.Builder();
- Map<DexProgramClass, ProgramMethodSet> bridgesToRemove =
- computeBridgesToRemove(lensBuilder, executorService);
+ ProgramMethodSet bridgesToRemove = removeRedundantBridgesConcurrently(executorService);
if (bridgesToRemove.isEmpty()) {
return;
}
@@ -131,65 +130,50 @@
}
if (memberRebindingIdentityLens != null) {
- for (ProgramMethodSet bridgesToRemoveFromClass : bridgesToRemove.values()) {
- for (ProgramMethod bridgeToRemove : bridgesToRemoveFromClass) {
- DexClassAndMethod resolvedMethod =
- appView
- .appInfo()
- .resolveMethodOn(bridgeToRemove.getHolder(), bridgeToRemove.getReference())
- .getResolutionPair();
- memberRebindingIdentityLens.addNonReboundMethodReference(
- bridgeToRemove.getReference(), resolvedMethod.getReference());
- }
+ for (ProgramMethod bridgeToRemove : bridgesToRemove) {
+ DexClassAndMethod resolvedMethod =
+ appView
+ .appInfo()
+ .resolveMethodOn(bridgeToRemove.getHolder(), bridgeToRemove.getReference())
+ .getResolutionPair();
+ memberRebindingIdentityLens.addNonReboundMethodReference(
+ bridgeToRemove.getReference(), resolvedMethod.getReference());
}
}
}
- private Map<DexProgramClass, ProgramMethodSet> computeBridgesToRemove(
- RedundantBridgeRemovalLens.Builder lensBuilder, ExecutorService executorService)
+ private ProgramMethodSet removeRedundantBridgesConcurrently(ExecutorService executorService)
throws ExecutionException {
- Map<DexProgramClass, ProgramMethodSet> bridgesToRemove = new ConcurrentHashMap<>();
- ThreadUtils.processItems(
- appView.appInfo().classes(),
- clazz -> {
- ProgramMethodSet bridgesToRemoveForClass = ProgramMethodSet.create();
- clazz.forEachProgramMethod(
- method -> {
- KeepMethodInfo keepInfo = appView.getKeepInfo(method);
- if (!keepInfo.isShrinkingAllowed(appView.options())
- || !keepInfo.isOptimizationAllowed(appView.options())) {
- return;
- }
- if (isRedundantAbstractBridge(method)) {
- // Record that the redundant bridge should be removed.
- bridgesToRemoveForClass.add(method);
- return;
- }
- DexClassAndMethod target = getTargetForRedundantBridge(method);
- if (target != null) {
- // Record that the redundant bridge should be removed.
- bridgesToRemoveForClass.add(method);
+ // Compute the strongly connected program components for parallelization.
+ List<Set<DexProgramClass>> stronglyConnectedProgramComponents =
+ new ProgramClassesBidirectedGraph(appView, immediateSubtypingInfo)
+ .computeStronglyConnectedComponents();
- // Rewrite invokes to the bridge to the target if it is accessible.
- // TODO(b/173751869): Consider enabling this for constructors as well.
- // TODO(b/245882297): Refine these visibility checks so that we also rewrite when
- // the target is not public, but still accessible to call sites.
- boolean isEligibleForRetargeting =
- redundantBridgeRemovalOptions.isRetargetingOfConstructorBridgeCallsEnabled()
- || !method.getDefinition().isInstanceInitializer();
- if (isEligibleForRetargeting
- && target.getAccessFlags().isPublic()
- && target.getHolder().isPublic()) {
- lensBuilder.map(method, target);
- }
- }
- });
- if (!bridgesToRemoveForClass.isEmpty()) {
- bridgesToRemove.put(clazz, bridgesToRemoveForClass);
- }
- },
- executorService);
- return bridgesToRemove;
+ // Process the components concurrently.
+ Collection<ProgramMethodSet> results =
+ ThreadUtils.processItemsWithResultsThatMatches(
+ stronglyConnectedProgramComponents,
+ this::removeRedundantBridgesInComponent,
+ removedBridges -> !removedBridges.isEmpty(),
+ executorService);
+ ProgramMethodSet removedBridges = ProgramMethodSet.create();
+ results.forEach(
+ result -> {
+ removedBridges.addAll(result);
+ result.clear();
+ });
+ return removedBridges;
+ }
+
+ private ProgramMethodSet removeRedundantBridgesInComponent(
+ Set<DexProgramClass> stronglyConnectedProgramComponent) {
+ // Remove bridges in a top-down traversal of the class hierarchy. This ensures that we don't map
+ // an invoke to a removed bridge method to a method in the superclass hierarchy, which is then
+ // also removed by bridge removal.
+ RedundantBridgeRemoverClassHierarchyTraversal traversal =
+ new RedundantBridgeRemoverClassHierarchyTraversal();
+ traversal.run(stronglyConnectedProgramComponent);
+ return traversal.getRemovedBridges();
}
private boolean isRedundantAbstractBridge(ProgramMethod method) {
@@ -241,99 +225,95 @@
return true;
}
- private void pruneApp(
- Map<DexProgramClass, ProgramMethodSet> bridgesToRemove, ExecutorService executorService)
+ private void pruneApp(ProgramMethodSet bridgesToRemove, ExecutorService executorService)
throws ExecutionException {
PrunedItems.Builder prunedItemsBuilder = PrunedItems.builder().setPrunedApp(appView.app());
- bridgesToRemove.forEach(
- (clazz, methods) -> {
- clazz.getMethodCollection().removeMethods(methods.toDefinitionSet());
- methods.forEach(method -> prunedItemsBuilder.addRemovedMethod(method.getReference()));
- });
+ bridgesToRemove.forEach(method -> prunedItemsBuilder.addRemovedMethod(method.getReference()));
appView.pruneItems(prunedItemsBuilder.build(), executorService);
}
- class InvokedReflectivelyFromPlatformAnalysis {
+ class RedundantBridgeRemoverClassHierarchyTraversal
+ extends DepthFirstTopDownClassHierarchyTraversal {
- // Maps each class to a boolean indicating if the class inherits from android.app.Fragment or
- // android.app.ZygotePreload.
- private final Map<DexClass, Boolean> cache = new ConcurrentHashMap<>();
+ private final ProgramMethodSet removedBridges = ProgramMethodSet.create();
- boolean isMaybeInvokedReflectivelyFromPlatform(ProgramMethod method) {
- return method.getDefinition().isDefaultInstanceInitializer()
- && !method.getHolder().isAbstract()
- && computeIsPlatformReflectingOnDefaultConstructor(method.getHolder());
+ RedundantBridgeRemoverClassHierarchyTraversal() {
+ super(
+ RedundantBridgeRemover.this.appView, RedundantBridgeRemover.this.immediateSubtypingInfo);
}
- private boolean computeIsPlatformReflectingOnDefaultConstructor(DexProgramClass clazz) {
- Boolean cacheResult = cache.get(clazz);
- if (cacheResult != null) {
- return cacheResult;
- }
- WorkList.<WorklistItem>newIdentityWorkList(new NotProcessedWorklistItem(clazz))
- .process(WorklistItem::accept);
- assert cache.containsKey(clazz);
- return cache.get(clazz);
+ public ProgramMethodSet getRemovedBridges() {
+ return removedBridges;
}
- abstract class WorklistItem implements Consumer<WorkList<WorklistItem>> {
+ @Override
+ public void visit(DexProgramClass clazz) {
+ ProgramMethodSet bridgesToRemoveForClass = ProgramMethodSet.create();
+ clazz.forEachProgramMethod(
+ method -> {
+ KeepMethodInfo keepInfo = appView.getKeepInfo(method);
+ if (!keepInfo.isShrinkingAllowed(appView.options())
+ || !keepInfo.isOptimizationAllowed(appView.options())) {
+ return;
+ }
+ if (isRedundantAbstractBridge(method)) {
+ // Record that the redundant bridge should be removed.
+ bridgesToRemoveForClass.add(method);
+ return;
+ }
+ DexClassAndMethod target = getTargetForRedundantBridge(method);
+ if (target != null) {
+ // Record that the redundant bridge should be removed.
+ bridgesToRemoveForClass.add(method);
- protected final DexClass clazz;
-
- WorklistItem(DexClass clazz) {
- this.clazz = clazz;
- }
-
- Iterable<DexClass> getImmediateSupertypes() {
- return IterableUtils.flatMap(
- clazz.allImmediateSupertypes(),
- supertype -> {
- DexClass definition = appView.definitionFor(supertype);
- return definition != null
- ? Collections.singletonList(definition)
- : Collections.emptyList();
- });
+ // Rewrite invokes to the bridge to the target if it is accessible.
+ if (canRetargetInvokesToTargetMethod(method, target)) {
+ lensBuilder.map(method, target);
+ }
+ }
+ });
+ if (!bridgesToRemoveForClass.isEmpty()) {
+ clazz.getMethodCollection().removeMethods(bridgesToRemoveForClass.toDefinitionSet());
+ removedBridges.addAll(bridgesToRemoveForClass);
}
}
- class NotProcessedWorklistItem extends WorklistItem {
-
- NotProcessedWorklistItem(DexClass clazz) {
- super(clazz);
+ private boolean canRetargetInvokesToTargetMethod(
+ ProgramMethod method, DexClassAndMethod target) {
+ // Check if constructor retargeting is enabled.
+ if (method.getDefinition().isInstanceInitializer()
+ && !redundantBridgeRemovalOptions.isRetargetingOfConstructorBridgeCallsEnabled()) {
+ return false;
}
-
- @Override
- public void accept(WorkList<WorklistItem> worklist) {
- // Enqueue a worklist item to process the current class after processing its super classes.
- worklist.addFirstIgnoringSeenSet(new ProcessedWorklistItem(clazz));
- // Enqueue all superclasses for processing.
- for (DexClass supertype : getImmediateSupertypes()) {
- if (!cache.containsKey(supertype)) {
- worklist.addFirstIgnoringSeenSet(new NotProcessedWorklistItem(supertype));
- }
+ // Check if all possible contexts that have access to the holder of the redundant bridge
+ // method also have access to the holder of the target method.
+ DexProgramClass methodHolder = method.getHolder();
+ DexClass targetHolder = target.getHolder();
+ if (!targetHolder.getAccessFlags().isPublic()) {
+ if (methodHolder.getAccessFlags().isPublic() || !method.isSamePackage(target)) {
+ return false;
}
}
+ // Check if all possible contexts that have access to the redundant bridge method also have
+ // access to the target method.
+ if (target.getAccessFlags().isPublic()) {
+ return true;
+ }
+ MethodAccessFlags methodAccessFlags = method.getAccessFlags();
+ MethodAccessFlags targetAccessFlags = target.getAccessFlags();
+ if (methodAccessFlags.isPackagePrivate()
+ && !targetAccessFlags.isPrivate()
+ && method.isSamePackage(target)) {
+ return true;
+ }
+ return methodAccessFlags.isProtected()
+ && targetAccessFlags.isProtected()
+ && method.isSamePackage(target);
}
- class ProcessedWorklistItem extends WorklistItem {
-
- ProcessedWorklistItem(DexClass clazz) {
- super(clazz);
- }
-
- @Override
- public void accept(WorkList<WorklistItem> worklist) {
- cache.put(
- clazz,
- Iterables.any(
- getImmediateSupertypes(),
- supertype ->
- cache.get(supertype)
- || (supertype.isLibraryClass()
- && redundantBridgeRemovalOptions
- .isPlatformReflectingOnDefaultConstructorInSubclasses(
- supertype.asLibraryClass()))));
- }
+ @Override
+ public void prune(DexProgramClass clazz) {
+ // Empty.
}
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceElementProxy.java b/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceElementProxy.java
index 7f4747a..714e2b9 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceElementProxy.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetraceStackTraceElementProxy.java
@@ -43,6 +43,8 @@
String getSourceFile();
+ RetracedSourceFile getRetracedSourceFile();
+
int getLineNumber();
RetraceStackTraceContext getContext();
diff --git a/src/main/java/com/android/tools/r8/retrace/RetracedClassReference.java b/src/main/java/com/android/tools/r8/retrace/RetracedClassReference.java
index 5e6b29b..2c030f7 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetracedClassReference.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetracedClassReference.java
@@ -10,6 +10,10 @@
@Keep
public interface RetracedClassReference {
+ boolean isUnknown();
+
+ boolean isKnown();
+
String getTypeName();
String getDescriptor();
diff --git a/src/main/java/com/android/tools/r8/retrace/RetracedSourceFile.java b/src/main/java/com/android/tools/r8/retrace/RetracedSourceFile.java
index d1d3542..d677cb8 100644
--- a/src/main/java/com/android/tools/r8/retrace/RetracedSourceFile.java
+++ b/src/main/java/com/android/tools/r8/retrace/RetracedSourceFile.java
@@ -14,4 +14,6 @@
String getSourceFile();
String getOrInferSourceFile();
+
+ String getOrInferSourceFile(String original);
}
diff --git a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
index df888dc..3f37a84 100644
--- a/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
+++ b/src/main/java/com/android/tools/r8/retrace/StringRetrace.java
@@ -133,26 +133,33 @@
private List<String> joinAmbiguousLines(
List<RetraceStackFrameAmbiguousResult<String>> retracedResult) {
List<String> result = new ArrayList<>();
- retracedResult.forEach(
- potentialResults -> {
- Set<String> reportedFrames = new HashSet<>();
- potentialResults.forEachWithIndex(
- (inlineFrames, index) -> {
- // Check if we already reported position.
- String topFrame = inlineFrames.get(0);
- if (reportedFrames.add(topFrame)) {
- inlineFrames.forEach(
- inlineFrame -> {
- boolean isAmbiguous = index > 0 && topFrame.equals(inlineFrame);
- if (isAmbiguous) {
- result.add(insertOrIntoStackTraceLine(inlineFrame));
- } else {
- result.add(inlineFrame);
- }
- });
- }
- });
- });
+ for (RetraceStackFrameAmbiguousResult<String> ambiguousResult : retracedResult) {
+ boolean addedLines = true;
+ int lineIndex = 0;
+ while (addedLines) {
+ addedLines = false;
+ Set<String> reportedFrames = new HashSet<>();
+ RetraceStackFrameResult<String> firstResult = null;
+ for (RetraceStackFrameResult<String> inlineFrames : ambiguousResult.getAmbiguousResult()) {
+ if (firstResult == null) {
+ firstResult = inlineFrames;
+ }
+ if (lineIndex < inlineFrames.size()) {
+ addedLines = true;
+ String frameToAdd = inlineFrames.get(lineIndex);
+ if (reportedFrames.add(frameToAdd)) {
+ boolean isAmbiguous = inlineFrames != firstResult;
+ if (isAmbiguous) {
+ result.add(insertOrIntoStackTraceLine(frameToAdd));
+ } else {
+ result.add(frameToAdd);
+ }
+ }
+ }
+ }
+ lineIndex += 1;
+ }
+ }
return result;
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionMetadataInternal.java b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionMetadataInternal.java
index 9896856..b5438c6 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionMetadataInternal.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MappingPartitionMetadataInternal.java
@@ -43,7 +43,7 @@
}
default MetadataAdditionalInfo getAdditionalInfo() {
- return MetadataAdditionalInfo.create(null);
+ return MetadataAdditionalInfo.create(null, null);
}
// Magic byte put into the metadata
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/MetadataAdditionalInfo.java b/src/main/java/com/android/tools/r8/retrace/internal/MetadataAdditionalInfo.java
index 706584b..7e0e22d 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/MetadataAdditionalInfo.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/MetadataAdditionalInfo.java
@@ -5,20 +5,30 @@
package com.android.tools.r8.retrace.internal;
import com.android.tools.r8.dex.CompatByteBuffer;
+import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.retrace.RetracePartitionException;
import com.android.tools.r8.utils.SerializationUtils;
import com.android.tools.r8.utils.StringUtils;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Predicate;
public class MetadataAdditionalInfo {
+ private static final int NUMBER_OF_ELEMENTS = 2;
+
public enum AdditionalInfoTypes {
UNKNOWN(-1),
- PREAMBLE(0);
+ PREAMBLE(0),
+ OBFUSCATED_PACKAGES(1);
private final int serializedKey;
@@ -29,15 +39,19 @@
static AdditionalInfoTypes getByKey(int serializedKey) {
if (serializedKey == 0) {
return PREAMBLE;
+ } else if (serializedKey == 1) {
+ return OBFUSCATED_PACKAGES;
}
return UNKNOWN;
}
}
protected final List<String> preamble;
+ protected final Set<String> obfuscatedPackages;
- private MetadataAdditionalInfo(List<String> preamble) {
+ private MetadataAdditionalInfo(List<String> preamble, Set<String> obfuscatedPackages) {
this.preamble = preamble;
+ this.obfuscatedPackages = obfuscatedPackages;
}
public boolean hasPreamble() {
@@ -48,70 +62,116 @@
return preamble;
}
+ public boolean hasObfuscatedPackages() {
+ return obfuscatedPackages != null;
+ }
+
+ public Set<String> getObfuscatedPackages() {
+ return obfuscatedPackages;
+ }
+
// The serialized format is an extensible list where we first record the offsets for each data
// section and then emit the data.
// <total-size:int><number-of-elements:short>[<type-i:short><length-i:int><data-i>]
public void serialize(DataOutputStream dataOutputStream) throws IOException {
ByteArrayOutputStream temp = new ByteArrayOutputStream();
DataOutputStream additionalInfoStream = new DataOutputStream(temp);
- additionalInfoStream.writeShort(1);
+ additionalInfoStream.writeShort(NUMBER_OF_ELEMENTS);
additionalInfoStream.writeShort(AdditionalInfoTypes.PREAMBLE.serializedKey);
SerializationUtils.writeUTFOfIntSize(additionalInfoStream, StringUtils.unixLines(preamble));
+ additionalInfoStream.writeShort(AdditionalInfoTypes.OBFUSCATED_PACKAGES.serializedKey);
+ List<String> sortedPackages = new ArrayList<>(obfuscatedPackages);
+ Collections.sort(sortedPackages);
+ SerializationUtils.writeUTFOfIntSize(
+ additionalInfoStream, StringUtils.unixLines(sortedPackages));
byte[] payload = temp.toByteArray();
dataOutputStream.writeInt(payload.length);
dataOutputStream.write(payload);
}
- private static MetadataAdditionalInfo deserialize(byte[] bytes) {
+ private static MetadataAdditionalInfo deserialize(
+ byte[] bytes, Predicate<AdditionalInfoTypes> serializeSection) {
CompatByteBuffer compatByteBuffer = CompatByteBuffer.wrap(bytes);
int numberOfElements = compatByteBuffer.getShort();
List<String> preamble = null;
+ Set<String> packages = null;
for (int i = 0; i < numberOfElements; i++) {
// We are parsing <type:short><length:int><bytes>
int additionInfoTypeKey = compatByteBuffer.getShort();
AdditionalInfoTypes additionalInfoType = AdditionalInfoTypes.getByKey(additionInfoTypeKey);
- if (additionalInfoType == AdditionalInfoTypes.PREAMBLE) {
- preamble = StringUtils.splitLines(compatByteBuffer.getUTFOfIntSize());
- } else {
+ if (additionalInfoType == AdditionalInfoTypes.UNKNOWN) {
throw new RetracePartitionException(
"Could not additional info from key: " + additionInfoTypeKey);
}
+ if (serializeSection.test(additionalInfoType)) {
+ switch (additionalInfoType) {
+ case PREAMBLE:
+ preamble = StringUtils.splitLines(compatByteBuffer.getUTFOfIntSize());
+ break;
+ case OBFUSCATED_PACKAGES:
+ packages = StringUtils.splitLinesIntoSet(compatByteBuffer.getUTFOfIntSize());
+ break;
+ default:
+ throw new Unreachable("Unreachable since we already checked for UNKNOWN");
+ }
+ } else {
+ int length = compatByteBuffer.getInt();
+ compatByteBuffer.position(compatByteBuffer.position() + length);
+ }
}
- return new MetadataAdditionalInfo(preamble);
+ return new MetadataAdditionalInfo(preamble, packages);
}
- public static MetadataAdditionalInfo create(List<String> preamble) {
- return new MetadataAdditionalInfo(preamble);
+ public static MetadataAdditionalInfo create(
+ List<String> preamble, Set<String> obfuscatedPackages) {
+ return new MetadataAdditionalInfo(preamble, obfuscatedPackages);
}
public static class LazyMetadataAdditionalInfo extends MetadataAdditionalInfo {
- private byte[] bytes;
- private MetadataAdditionalInfo metadataAdditionalInfo = null;
+ private final byte[] bytes;
+ private final Map<Integer, MetadataAdditionalInfo> metadataAdditionalInfo =
+ new ConcurrentHashMap<>();
public LazyMetadataAdditionalInfo(byte[] bytes) {
- super(null);
+ super(null, null);
this.bytes = bytes;
}
@Override
public boolean hasPreamble() {
- MetadataAdditionalInfo metadataAdditionalInfo = getMetadataAdditionalInfo();
+ MetadataAdditionalInfo metadataAdditionalInfo =
+ getMetadataAdditionalInfo(AdditionalInfoTypes.PREAMBLE);
return metadataAdditionalInfo != null && metadataAdditionalInfo.hasPreamble();
}
@Override
public Collection<String> getPreamble() {
- MetadataAdditionalInfo metadataAdditionalInfo = getMetadataAdditionalInfo();
+ MetadataAdditionalInfo metadataAdditionalInfo =
+ getMetadataAdditionalInfo(AdditionalInfoTypes.PREAMBLE);
return metadataAdditionalInfo == null ? null : metadataAdditionalInfo.getPreamble();
}
- private MetadataAdditionalInfo getMetadataAdditionalInfo() {
- if (metadataAdditionalInfo == null) {
- metadataAdditionalInfo = MetadataAdditionalInfo.deserialize(bytes);
- bytes = null;
- }
- return metadataAdditionalInfo;
+ @Override
+ public boolean hasObfuscatedPackages() {
+ MetadataAdditionalInfo metadataAdditionalInfo =
+ getMetadataAdditionalInfo(AdditionalInfoTypes.OBFUSCATED_PACKAGES);
+ return metadataAdditionalInfo != null && metadataAdditionalInfo.hasObfuscatedPackages();
+ }
+
+ @Override
+ public Set<String> getObfuscatedPackages() {
+ MetadataAdditionalInfo metadataAdditionalInfo =
+ getMetadataAdditionalInfo(AdditionalInfoTypes.OBFUSCATED_PACKAGES);
+ return metadataAdditionalInfo == null ? null : metadataAdditionalInfo.getObfuscatedPackages();
+ }
+
+ private MetadataAdditionalInfo getMetadataAdditionalInfo(AdditionalInfoTypes infoType) {
+ return metadataAdditionalInfo.computeIfAbsent(
+ infoType.serializedKey,
+ ignored ->
+ MetadataAdditionalInfo.deserialize(
+ bytes, deserializeType -> deserializeType == infoType));
}
public static LazyMetadataAdditionalInfo create(CompatByteBuffer buffer) {
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBase.java b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBase.java
index 012fb94..a15f89f 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBase.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/PartitionMappingSupplierBase.java
@@ -23,6 +23,8 @@
import com.android.tools.r8.retrace.PrepareMappingPartitionsCallback;
import com.android.tools.r8.retrace.RegisterMappingPartitionCallback;
import com.android.tools.r8.retrace.internal.ProguardMapReaderWithFiltering.ProguardMapReaderWithFilteringInputBuffer;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@@ -30,6 +32,7 @@
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
+import java.util.function.Predicate;
public abstract class PartitionMappingSupplierBase<T extends PartitionMappingSupplierBase<T>>
implements Finishable {
@@ -45,7 +48,8 @@
private final Set<String> pendingKeys = new LinkedHashSet<>();
private final Set<String> builtKeys = new HashSet<>();
- private MappingPartitionMetadataInternal mappingPartitionMetadataCache;
+ private final Box<MappingPartitionMetadataInternal> mappingPartitionMetadataCache = new Box<>();
+ private final Box<Predicate<String>> typeNameCouldHavePartitionCache = new Box<>();
protected PartitionMappingSupplierBase(
RegisterMappingPartitionCallback registerCallback,
@@ -63,16 +67,49 @@
}
public MappingPartitionMetadataInternal getMetadata(DiagnosticsHandler diagnosticsHandler) {
- if (mappingPartitionMetadataCache != null) {
- return mappingPartitionMetadataCache;
+ if (mappingPartitionMetadataCache.isSet()) {
+ return mappingPartitionMetadataCache.get();
}
- return mappingPartitionMetadataCache =
- MappingPartitionMetadataInternal.deserialize(
- CompatByteBuffer.wrapOrNull(metadata), fallbackMapVersion, diagnosticsHandler);
+ synchronized (mappingPartitionMetadataCache) {
+ if (mappingPartitionMetadataCache.isSet()) {
+ return mappingPartitionMetadataCache.get();
+ }
+ MappingPartitionMetadataInternal data =
+ MappingPartitionMetadataInternal.deserialize(
+ CompatByteBuffer.wrapOrNull(metadata), fallbackMapVersion, diagnosticsHandler);
+ mappingPartitionMetadataCache.set(data);
+ return data;
+ }
}
public T registerClassUse(DiagnosticsHandler diagnosticsHandler, ClassReference classReference) {
- return registerKeyUse(classReference.getTypeName());
+ // Check if the package name is registered before requesting the bytes for a partition.
+ String typeName = classReference.getTypeName();
+ if (isPotentialRetraceClass(diagnosticsHandler, typeName)) {
+ return registerKeyUse(typeName);
+ }
+ return self();
+ }
+
+ private boolean isPotentialRetraceClass(DiagnosticsHandler diagnosticsHandler, String typeName) {
+ if (typeNameCouldHavePartitionCache.isSet()) {
+ return typeNameCouldHavePartitionCache.get().test(typeName);
+ }
+ synchronized (typeNameCouldHavePartitionCache) {
+ if (typeNameCouldHavePartitionCache.isSet()) {
+ return typeNameCouldHavePartitionCache.get().test(typeName);
+ }
+ Predicate<String> typeNameCouldHavePartitionPredicate =
+ getPartitionPredicate(getPackagesWithClasses(diagnosticsHandler));
+ typeNameCouldHavePartitionCache.set(typeNameCouldHavePartitionPredicate);
+ return typeNameCouldHavePartitionPredicate.test(typeName);
+ }
+ }
+
+ private Predicate<String> getPartitionPredicate(Set<String> packagesWithClasses) {
+ return name ->
+ packagesWithClasses == null
+ || packagesWithClasses.contains(DescriptorUtils.getPackageNameFromTypeName(name));
}
public T registerMethodUse(
@@ -85,13 +122,24 @@
}
public T registerKeyUse(String key) {
- // TODO(b/274735214): only call the register partition if we have a partition for it.
if (!builtKeys.contains(key) && pendingKeys.add(key)) {
registerCallback.register(key);
}
return self();
}
+ private Set<String> getPackagesWithClasses(DiagnosticsHandler diagnosticsHandler) {
+ MappingPartitionMetadataInternal metadata = getMetadata(diagnosticsHandler);
+ if (metadata == null || !metadata.canGetAdditionalInfo()) {
+ return null;
+ }
+ MetadataAdditionalInfo additionalInfo = metadata.getAdditionalInfo();
+ if (!additionalInfo.hasObfuscatedPackages()) {
+ return null;
+ }
+ return additionalInfo.getObfuscatedPackages();
+ }
+
public void verifyMappingFileHash(DiagnosticsHandler diagnosticsHandler) {
String errorMessage = "Cannot verify map file hash for partitions";
diagnosticsHandler.error(new StringDiagnostic(errorMessage));
@@ -115,7 +163,6 @@
for (String pendingKey : pendingKeys) {
try {
byte[] suppliedPartition = partitionSupplier.get(pendingKey);
- // TODO(b/274735214): only expect a partition if have generated one for the key.
if (suppliedPartition == null) {
continue;
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapPartitionerOnClassNameToText.java b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapPartitionerOnClassNameToText.java
index 3edde9a..3af7dff 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapPartitionerOnClassNameToText.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/ProguardMapPartitionerOnClassNameToText.java
@@ -168,7 +168,8 @@
return ObfuscatedTypeNameAsKeyMetadataWithPartitionNames.create(
mapVersion,
MetadataPartitionCollection.create(keys),
- MetadataAdditionalInfo.create(classMapper.getPreamble()));
+ MetadataAdditionalInfo.create(
+ classMapper.getPreamble(), classMapper.getObfuscatedPackages()));
} else {
RetracePartitionException retraceError =
new RetracePartitionException("Unknown mapping partitioning strategy");
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
index dc90e36..98ed799 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceClassResultImpl.java
@@ -212,9 +212,8 @@
return new RetraceClassElementImpl(
this,
RetracedClassReferenceImpl.create(
- mapper == null
- ? obfuscatedReference
- : Reference.classFromTypeName(mapper.originalName)),
+ mapper == null ? obfuscatedReference : Reference.classFromTypeName(mapper.originalName),
+ mapper != null),
mapper);
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
index afade35..ac45d92 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceFieldResultImpl.java
@@ -10,7 +10,6 @@
import com.android.tools.r8.retrace.RetraceFieldElement;
import com.android.tools.r8.retrace.RetraceFieldResult;
import com.android.tools.r8.retrace.RetracedSourceFile;
-import com.android.tools.r8.retrace.Retracer;
import com.android.tools.r8.retrace.internal.RetraceClassResultImpl.RetraceClassElementImpl;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.Pair;
@@ -22,13 +21,13 @@
private final RetraceClassResultImpl classResult;
private final List<Pair<RetraceClassElementImpl, List<MemberNaming>>> memberNamings;
private final FieldDefinition fieldDefinition;
- private final Retracer retracer;
+ private final RetracerImpl retracer;
RetraceFieldResultImpl(
RetraceClassResultImpl classResult,
List<Pair<RetraceClassElementImpl, List<MemberNaming>>> memberNamings,
FieldDefinition fieldDefinition,
- Retracer retracer) {
+ RetracerImpl retracer) {
this.classResult = classResult;
this.memberNamings = memberNamings;
this.fieldDefinition = fieldDefinition;
@@ -64,7 +63,8 @@
? RetracedClassReferenceImpl.create(
Reference.classFromDescriptor(
DescriptorUtils.javaTypeToDescriptor(
- fieldSignature.toHolderFromQualified())))
+ fieldSignature.toHolderFromQualified())),
+ true)
: classElement.getRetracedClass();
return new ElementImpl(
this,
@@ -144,7 +144,8 @@
@Override
public RetracedSourceFile getSourceFile() {
- return classElement.getSourceFile();
+ return RetraceUtils.getSourceFile(
+ fieldReference.getHolderClass(), retraceFieldResult.retracer);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceThrownExceptionResultImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceThrownExceptionResultImpl.java
index e5d0df2..3488823 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceThrownExceptionResultImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceThrownExceptionResultImpl.java
@@ -43,9 +43,8 @@
return new RetraceThrownExceptionElementImpl(
this,
RetracedClassReferenceImpl.create(
- mapper == null
- ? obfuscatedReference
- : Reference.classFromTypeName(mapper.originalName)),
+ mapper == null ? obfuscatedReference : Reference.classFromTypeName(mapper.originalName),
+ mapper != null),
mapper,
obfuscatedReference);
}
@@ -89,7 +88,7 @@
}
}
}
- return new RetracedSourceFileImpl(getRetracedClass().getClassReference(), sourceFile);
+ return new RetracedSourceFileImpl(getRetracedClass(), sourceFile);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java b/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java
index 0180960..b339440 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetraceUtils.java
@@ -68,8 +68,7 @@
public static RetracedSourceFile getSourceFile(
RetracedClassReference holder, RetracerImpl retracer) {
- ClassReference holderReference = holder.getClassReference();
- return new RetracedSourceFileImpl(holderReference, retracer.getSourceFile(holderReference));
+ return new RetracedSourceFileImpl(holder, retracer.getSourceFile(holder.getClassReference()));
}
public static String inferSourceFile(
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracedClassReferenceImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracedClassReferenceImpl.java
index d036009..326b157 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracedClassReferenceImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracedClassReferenceImpl.java
@@ -10,14 +10,27 @@
public final class RetracedClassReferenceImpl implements RetracedClassReference {
private final ClassReference classReference;
+ private final boolean hasResult;
- private RetracedClassReferenceImpl(ClassReference classReference) {
+ private RetracedClassReferenceImpl(ClassReference classReference, boolean hasResult) {
assert classReference != null;
this.classReference = classReference;
+ this.hasResult = hasResult;
}
- public static RetracedClassReferenceImpl create(ClassReference classReference) {
- return new RetracedClassReferenceImpl(classReference);
+ public static RetracedClassReferenceImpl create(
+ ClassReference classReference, boolean hasResult) {
+ return new RetracedClassReferenceImpl(classReference, hasResult);
+ }
+
+ @Override
+ public boolean isUnknown() {
+ return !isKnown();
+ }
+
+ @Override
+ public boolean isKnown() {
+ return hasResult;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracedFieldReferenceImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracedFieldReferenceImpl.java
index 8316a3b..304462e 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracedFieldReferenceImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracedFieldReferenceImpl.java
@@ -49,7 +49,7 @@
@Override
public RetracedClassReferenceImpl getHolderClass() {
- return RetracedClassReferenceImpl.create(fieldReference.getHolderClass());
+ return RetracedClassReferenceImpl.create(fieldReference.getHolderClass(), true);
}
@Override
@@ -95,7 +95,7 @@
@Override
public RetracedClassReferenceImpl getHolderClass() {
- return RetracedClassReferenceImpl.create(fieldDefinition.getHolderClass());
+ return RetracedClassReferenceImpl.create(fieldDefinition.getHolderClass(), false);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracedMethodReferenceImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracedMethodReferenceImpl.java
index f896692..8d7d20f 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracedMethodReferenceImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracedMethodReferenceImpl.java
@@ -99,7 +99,7 @@
@Override
public RetracedClassReferenceImpl getHolderClass() {
- return RetracedClassReferenceImpl.create(methodReference.getHolderClass());
+ return RetracedClassReferenceImpl.create(methodReference.getHolderClass(), true);
}
@Override
@@ -153,7 +153,7 @@
@Override
public RetracedClassReferenceImpl getHolderClass() {
- return RetracedClassReferenceImpl.create(methodDefinition.getHolderClass());
+ return RetracedClassReferenceImpl.create(methodDefinition.getHolderClass(), false);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/RetracedSourceFileImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/RetracedSourceFileImpl.java
index c7f2fae..e570354 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/RetracedSourceFileImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/RetracedSourceFileImpl.java
@@ -4,15 +4,15 @@
package com.android.tools.r8.retrace.internal;
-import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.retrace.RetracedClassReference;
import com.android.tools.r8.retrace.RetracedSourceFile;
public class RetracedSourceFileImpl implements RetracedSourceFile {
- private final ClassReference classReference;
+ private final RetracedClassReference classReference;
private final String filename;
- RetracedSourceFileImpl(ClassReference classReference, String filename) {
+ RetracedSourceFileImpl(RetracedClassReference classReference, String filename) {
assert classReference != null;
this.classReference = classReference;
this.filename = filename;
@@ -30,9 +30,17 @@
@Override
public String getOrInferSourceFile() {
- String sourceFile = getSourceFile();
+ return getOrInferSourceFile(null);
+ }
+
+ @Override
+ public String getOrInferSourceFile(String original) {
+ String sourceFile = filename;
return sourceFile != null
? sourceFile
- : RetraceUtils.inferSourceFile(classReference.getTypeName(), "", true);
+ : RetraceUtils.inferSourceFile(
+ classReference.getTypeName(),
+ original == null ? "" : original,
+ classReference.isKnown());
}
}
diff --git a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
index c907105..18623af 100644
--- a/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
+++ b/src/main/java/com/android/tools/r8/retrace/internal/StackTraceElementProxyRetracerImpl.java
@@ -103,11 +103,7 @@
.joinAmbiguous(classResult.isAmbiguous())
.setTopFrame(true)
.setContext(thrownExceptionElement.getContext())
- .apply(
- setSourceFileOnProxy(
- thrownExceptionElement::getSourceFile,
- thrownExceptionElement.getRetracedClass(),
- classResult))
+ .apply(setSourceFileOnProxy(thrownExceptionElement::getSourceFile))
.build();
}
@@ -143,11 +139,7 @@
.applyIf(
element.hasLineNumber(),
b -> b.setLineNumber(element.getLineNumber()))
- .apply(
- setSourceFileOnProxy(
- classElement::getSourceFile,
- classElement.getRetracedClass(),
- classResult))
+ .apply(setSourceFileOnProxy(classElement::getSourceFile))
.build());
}
return frameResult.stream()
@@ -160,7 +152,6 @@
singleFrame ->
buildProxyForRewrittenFrameElement(
element,
- classResult,
proxy,
frameResult,
frameElement,
@@ -172,7 +163,6 @@
private RetraceStackTraceElementProxyImpl<T, ST> buildProxyForRewrittenFrameElement(
ST element,
- RetraceClassResult classResult,
RetraceStackTraceElementProxyImpl<T, ST> proxy,
RetraceFrameResult frameResult,
RetraceFrameElement frameElement,
@@ -188,12 +178,9 @@
.setContext(frameElement.getRetraceStackTraceContext())
.applyIf(
element.hasLineNumber(),
- builder -> {
- builder.setLineNumber(method.getOriginalPositionOrDefault(element.getLineNumber()));
- })
- .apply(
- setSourceFileOnProxy(
- () -> frameElement.getSourceFile(method), method.getHolderClass(), classResult))
+ builder ->
+ builder.setLineNumber(method.getOriginalPositionOrDefault(element.getLineNumber())))
+ .apply(setSourceFileOnProxy(() -> frameElement.getSourceFile(method)))
.build();
}
@@ -213,13 +200,12 @@
.map(
fieldElement ->
buildProxyForRewrittenFieldElement(
- classResult, proxy, retraceFieldResult, fieldElement));
+ proxy, retraceFieldResult, fieldElement));
}))
.build();
}
private RetraceStackTraceElementProxyImpl<T, ST> buildProxyForRewrittenFieldElement(
- RetraceClassResult classResult,
RetraceStackTraceElementProxyImpl<T, ST> proxy,
RetraceFieldResult retraceFieldResult,
RetraceFieldElement fieldElement) {
@@ -229,27 +215,18 @@
.setRetracedField(fieldElement.getField())
.joinAmbiguous(retraceFieldResult.isAmbiguous())
.setTopFrame(true)
- .apply(
- setSourceFileOnProxy(
- fieldElement::getSourceFile, fieldElement.getField().getHolderClass(), classResult))
+ .apply(setSourceFileOnProxy(fieldElement::getSourceFile))
.build();
}
private Consumer<RetraceStackTraceElementProxyImpl.Builder<T, ST>> setSourceFileOnProxy(
- Supplier<RetracedSourceFile> sourceFile,
- RetracedClassReference classReference,
- RetraceClassResult classResult) {
+ Supplier<RetracedSourceFile> sourceFile) {
return proxy -> {
ST original = proxy.originalElement;
if (!original.hasSourceFile()) {
return;
}
- RetracedSourceFile retracedSourceFile = sourceFile.get();
- proxy.setSourceFile(
- retracedSourceFile.hasRetraceResult()
- ? retracedSourceFile.getSourceFile()
- : RetraceUtils.inferSourceFile(
- classReference.getTypeName(), original.getSourceFile(), classResult.isEmpty()));
+ proxy.setSourceFile(sourceFile.get());
};
}
@@ -351,7 +328,7 @@
private final RetracedFieldReference retracedField;
private final RetracedTypeReference fieldOrReturnType;
private final List<RetracedTypeReference> methodArguments;
- private final String sourceFile;
+ private final RetracedSourceFile sourceFile;
private final int lineNumber;
private final boolean isAmbiguous;
private final boolean isTopFrame;
@@ -364,7 +341,7 @@
RetracedFieldReference retracedField,
RetracedTypeReference fieldOrReturnType,
List<RetracedTypeReference> methodArguments,
- String sourceFile,
+ RetracedSourceFile sourceFile,
int lineNumber,
boolean isAmbiguous,
boolean isTopFrame,
@@ -460,6 +437,16 @@
@Override
public String getSourceFile() {
+ if (sourceFile == null) {
+ assert originalItem.getSourceFile() == null;
+ return null;
+ }
+ return sourceFile.getOrInferSourceFile(
+ originalItem.getSourceFile() == null ? "" : originalItem.getSourceFile());
+ }
+
+ @Override
+ public RetracedSourceFile getRetracedSourceFile() {
return sourceFile;
}
@@ -549,7 +536,7 @@
private RetracedFieldReference retracedField;
private RetracedTypeReference fieldOrReturnType;
private List<RetracedTypeReference> methodArguments;
- private String sourceFile;
+ private RetracedSourceFile sourceFile;
private int lineNumber = -1;
private boolean isAmbiguous;
private boolean isTopFrame;
@@ -584,7 +571,8 @@
return this;
}
- private Builder<T, ST> setSourceFile(String sourceFile) {
+ private Builder<T, ST> setSourceFile(RetracedSourceFile sourceFile) {
+ assert sourceFile != null;
this.sourceFile = sourceFile;
return this;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 3bab5ca..472680e 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.features.ClassToFeatureSplitMap;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.Definition;
import com.android.tools.r8.graph.DexCallSite;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndField;
@@ -873,38 +874,36 @@
return false;
}
- public boolean isFieldRead(DexEncodedField encodedField) {
+ public boolean isFieldRead(DexClassAndField field) {
assert checkIfObsolete();
- DexField field = encodedField.getReference();
- FieldAccessInfo info = getFieldAccessInfoCollection().get(field);
+ FieldAccessInfo info = getFieldAccessInfoCollection().get(field.getReference());
if (info != null && info.isRead()) {
return true;
}
- if (isPinned(encodedField)) {
+ if (isPinned(field)) {
return true;
}
- // For library classes we don't know whether a field is read.
- return isLibraryOrClasspathField(encodedField);
+ // For non-program classes we don't know whether a field is read.
+ return !field.isProgramField();
}
- public boolean isFieldWritten(DexEncodedField encodedField) {
+ public boolean isFieldWritten(DexClassAndField field) {
assert checkIfObsolete();
- return isFieldWrittenByFieldPutInstruction(encodedField) || isPinned(encodedField);
+ return isFieldWrittenByFieldPutInstruction(field) || isPinned(field);
}
- public boolean isFieldWrittenByFieldPutInstruction(DexEncodedField encodedField) {
+ public boolean isFieldWrittenByFieldPutInstruction(DexClassAndField field) {
assert checkIfObsolete();
- DexField field = encodedField.getReference();
- FieldAccessInfo info = getFieldAccessInfoCollection().get(field);
+ FieldAccessInfo info = getFieldAccessInfoCollection().get(field.getReference());
if (info != null && info.isWritten()) {
// The field is written directly by the program itself.
return true;
}
- // For library classes we don't know whether a field is rewritten.
- return isLibraryOrClasspathField(encodedField);
+ // For non-program classes we don't know whether a field is rewritten.
+ return !field.isProgramField();
}
- public boolean isFieldOnlyWrittenInMethod(DexEncodedField field, DexEncodedMethod method) {
+ public boolean isFieldOnlyWrittenInMethod(DexClassAndField field, DexEncodedMethod method) {
assert checkIfObsolete();
assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
if (isPinned(field)) {
@@ -914,7 +913,7 @@
}
public boolean isFieldOnlyWrittenInMethodIgnoringPinning(
- DexEncodedField field, DexEncodedMethod method) {
+ DexClassAndField field, DexEncodedMethod method) {
assert checkIfObsolete();
assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
FieldAccessInfo fieldAccessInfo = getFieldAccessInfoCollection().get(field.getReference());
@@ -924,10 +923,6 @@
}
public boolean isInstanceFieldWrittenOnlyInInstanceInitializers(DexClassAndField field) {
- return isInstanceFieldWrittenOnlyInInstanceInitializers(field.getDefinition());
- }
-
- public boolean isInstanceFieldWrittenOnlyInInstanceInitializers(DexEncodedField field) {
assert checkIfObsolete();
assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
if (isPinned(field)) {
@@ -946,7 +941,7 @@
.isOrWillBeInlinedIntoInstanceInitializer(dexItemFactory()));
}
- public boolean isStaticFieldWrittenOnlyInEnclosingStaticInitializer(DexEncodedField field) {
+ public boolean isStaticFieldWrittenOnlyInEnclosingStaticInitializer(DexClassAndField field) {
assert checkIfObsolete();
assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written";
DexEncodedMethod staticInitializer =
@@ -954,14 +949,6 @@
return staticInitializer != null && isFieldOnlyWrittenInMethod(field, staticInitializer);
}
- public boolean mayPropagateArgumentsTo(ProgramMethod method) {
- DexMethod reference = method.getReference();
- return method.getDefinition().hasCode()
- && !method.getDefinition().isLibraryMethodOverride().isPossiblyTrue()
- && !neverReprocess.contains(reference)
- && !keepInfo.getMethodInfo(method).isPinned(options());
- }
-
public boolean mayPropagateValueFor(
AppView<AppInfoWithLiveness> appView, DexClassAndMember<?, ?> member) {
assert checkIfObsolete();
@@ -997,11 +984,6 @@
return true;
}
- private boolean isLibraryOrClasspathField(DexEncodedField field) {
- DexClass holder = definitionFor(field.getHolderType());
- return holder == null || holder.isLibraryClass() || holder.isClasspathClass();
- }
-
public boolean isInstantiatedInterface(DexProgramClass clazz) {
assert checkIfObsolete();
return objectAllocationInfoCollection.isInterfaceWithUnknownSubtypeHierarchy(clazz);
@@ -1075,9 +1057,10 @@
return keepInfo.isPinned(clazz, options());
}
- public boolean isPinned(ProgramDefinition definition) {
+ public boolean isPinned(Definition definition) {
assert definition != null;
- return keepInfo.isPinned(definition, options());
+ return definition.isProgramDefinition()
+ && keepInfo.isPinned(definition.asProgramDefinition(), options());
}
public boolean hasPinnedInstanceInitializer(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
index 312b61e..db53845 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
@@ -25,10 +25,12 @@
}
private final boolean allowFieldTypeStrengthening;
+ private final boolean allowRedundantFieldLoadElimination;
private KeepFieldInfo(Builder builder) {
super(builder);
this.allowFieldTypeStrengthening = builder.isFieldTypeStrengtheningAllowed();
+ this.allowRedundantFieldLoadElimination = builder.isRedundantFieldLoadEliminationAllowed();
}
// This builder is not private as there are known instances where it is safe to modify keep info
@@ -46,6 +48,14 @@
return allowFieldTypeStrengthening;
}
+ public boolean isRedundantFieldLoadEliminationAllowed(GlobalKeepInfoConfiguration configuration) {
+ return internalIsRedundantFieldLoadEliminationAllowed();
+ }
+
+ boolean internalIsRedundantFieldLoadEliminationAllowed() {
+ return allowRedundantFieldLoadElimination;
+ }
+
public Joiner joiner() {
assert !isTop();
return new Joiner(this);
@@ -64,6 +74,7 @@
public static class Builder extends KeepInfo.Builder<Builder, KeepFieldInfo> {
private boolean allowFieldTypeStrengthening;
+ private boolean allowRedundantFieldLoadElimination;
private Builder() {
super();
@@ -72,16 +83,20 @@
private Builder(KeepFieldInfo original) {
super(original);
allowFieldTypeStrengthening = original.internalIsFieldTypeStrengtheningAllowed();
+ allowRedundantFieldLoadElimination =
+ original.internalIsRedundantFieldLoadEliminationAllowed();
}
@Override
public Builder makeTop() {
- return super.makeTop().disallowFieldTypeStrengthening();
+ return super.makeTop()
+ .disallowFieldTypeStrengthening()
+ .disallowRedundantFieldLoadElimination();
}
@Override
public Builder makeBottom() {
- return super.makeBottom().allowFieldTypeStrengthening();
+ return super.makeBottom().allowFieldTypeStrengthening().allowRedundantFieldLoadElimination();
}
public boolean isFieldTypeStrengtheningAllowed() {
@@ -101,6 +116,24 @@
return setAllowFieldTypeStrengthening(false);
}
+ public boolean isRedundantFieldLoadEliminationAllowed() {
+ return allowRedundantFieldLoadElimination;
+ }
+
+ public Builder setAllowRedundantFieldLoadElimination(
+ boolean allowRedundantFieldLoadElimination) {
+ this.allowRedundantFieldLoadElimination = allowRedundantFieldLoadElimination;
+ return self();
+ }
+
+ public Builder allowRedundantFieldLoadElimination() {
+ return setAllowRedundantFieldLoadElimination(true);
+ }
+
+ public Builder disallowRedundantFieldLoadElimination() {
+ return setAllowRedundantFieldLoadElimination(false);
+ }
+
@Override
public KeepFieldInfo getTopInfo() {
return TOP;
@@ -124,7 +157,9 @@
@Override
boolean internalIsEqualTo(KeepFieldInfo other) {
return super.internalIsEqualTo(other)
- && isFieldTypeStrengtheningAllowed() == other.internalIsFieldTypeStrengtheningAllowed();
+ && isFieldTypeStrengtheningAllowed() == other.internalIsFieldTypeStrengtheningAllowed()
+ && isRedundantFieldLoadEliminationAllowed()
+ == other.internalIsRedundantFieldLoadEliminationAllowed();
}
@Override
@@ -144,6 +179,11 @@
return self();
}
+ public Joiner disallowRedundantFieldLoadElimination() {
+ builder.disallowRedundantFieldLoadElimination();
+ return self();
+ }
+
@Override
public Joiner asFieldJoiner() {
return this;
@@ -155,7 +195,10 @@
return super.merge(joiner)
.applyIf(
!joiner.builder.isFieldTypeStrengtheningAllowed(),
- KeepFieldInfo.Joiner::disallowFieldTypeStrengthening);
+ KeepFieldInfo.Joiner::disallowFieldTypeStrengthening)
+ .applyIf(
+ !joiner.builder.isRedundantFieldLoadEliminationAllowed(),
+ KeepFieldInfo.Joiner::disallowRedundantFieldLoadElimination);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/NoRedundantFieldLoadEliminationRule.java b/src/main/java/com/android/tools/r8/shaking/NoRedundantFieldLoadEliminationRule.java
new file mode 100644
index 0000000..87c5ee0
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/NoRedundantFieldLoadEliminationRule.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class NoRedundantFieldLoadEliminationRule
+ extends NoOptimizationBaseRule<NoRedundantFieldLoadEliminationRule> {
+
+ public static final String RULE_NAME = "noredundantfieldloadelimination";
+
+ public static class Builder
+ extends NoOptimizationBaseRule.Builder<NoRedundantFieldLoadEliminationRule, Builder> {
+
+ Builder() {
+ super();
+ }
+
+ @Override
+ public NoRedundantFieldLoadEliminationRule.Builder self() {
+ return this;
+ }
+
+ @Override
+ public NoRedundantFieldLoadEliminationRule build() {
+ return new NoRedundantFieldLoadEliminationRule(
+ origin,
+ getPosition(),
+ source,
+ buildClassAnnotations(),
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ buildInheritanceAnnotations(),
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules);
+ }
+ }
+
+ NoRedundantFieldLoadEliminationRule(
+ Origin origin,
+ Position position,
+ String source,
+ List<ProguardTypeMatcher> classAnnotations,
+ ProguardAccessFlags classAccessFlags,
+ ProguardAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ ProguardClassNameList classNames,
+ List<ProguardTypeMatcher> inheritanceAnnotations,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ List<ProguardMemberRule> memberRules) {
+ super(
+ origin,
+ position,
+ source,
+ classAnnotations,
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ inheritanceAnnotations,
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ String typeString() {
+ return RULE_NAME;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java b/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
index 9bd37a5..cdf0264 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardAccessFlags.java
@@ -132,8 +132,9 @@
return isSet(Constants.ACC_FINAL);
}
- public void setAbstract() {
+ public ProguardAccessFlags setAbstract() {
set(Constants.ACC_ABSTRACT);
+ return this;
}
public boolean isAbstract() {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index c1de766..2c51922 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -599,6 +599,12 @@
configurationBuilder.addRule(rule);
return true;
}
+ if (acceptString(NoRedundantFieldLoadEliminationRule.RULE_NAME)) {
+ ProguardConfigurationRule rule =
+ parseNoOptimizationRule(optionStart, NoRedundantFieldLoadEliminationRule.builder());
+ configurationBuilder.addRule(rule);
+ return true;
+ }
if (acceptString(NoReturnTypeStrengtheningRule.RULE_NAME)) {
ProguardConfigurationRule rule =
parseNoOptimizationRule(optionStart, NoReturnTypeStrengtheningRule.builder());
@@ -2378,6 +2384,10 @@
this.wildcards = wildcards;
}
+ static IdentifierPatternWithWildcards init() {
+ return withoutWildcards("<init>");
+ }
+
static IdentifierPatternWithWildcards withoutWildcards(String pattern) {
return new IdentifierPatternWithWildcards(pattern, ImmutableList.of());
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
index 94ab239..9b0ed2e 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationUtils.java
@@ -4,17 +4,53 @@
package com.android.tools.r8.shaking;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.shaking.ProguardConfigurationParser.IdentifierPatternWithWildcards;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.LongInterval;
import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
public class ProguardConfigurationUtils {
- public static ProguardAssumeNoSideEffectRule buildAssumeNoSideEffectsRuleForApiLevel(
+ public static List<ProguardConfigurationRule> synthesizeRules(AppView<?> appView) {
+ List<ProguardConfigurationRule> synthesizedRules = new ArrayList<>();
+ DexItemFactory factory = appView.dexItemFactory();
+ InternalOptions options = appView.options();
+ // Add synthesized -assumenosideeffects from min api if relevant.
+ if (options.isGeneratingDex()) {
+ if (!hasExplicitAssumeValuesOrAssumeNoSideEffectsRuleForMinSdk(
+ factory, options.getProguardConfiguration().getRules())) {
+ synthesizedRules.add(
+ buildAssumeNoSideEffectsRuleForApiLevel(factory, options.getMinApiLevel()));
+ }
+ }
+ // Add synthesized -keepclassmembers rules for the default initializer of classes that inherit
+ // from android.app.Fragment and android.app.ZygotePreload. This is needed since the Android
+ // Platform may reflectively access these instance initializers.
+ DexClass androidAppFragment =
+ appView.appInfo().definitionForWithoutExistenceAssert(factory.androidAppFragment);
+ if (androidAppFragment != null) {
+ synthesizedRules.add(
+ buildKeepClassMembersNoShrinkingOfInitializerOnSubclasses(factory, androidAppFragment));
+ }
+ DexClass androidAppZygotePreload =
+ appView.appInfo().definitionForWithoutExistenceAssert(factory.androidAppZygotePreload);
+ if (androidAppZygotePreload != null) {
+ synthesizedRules.add(
+ buildKeepClassMembersNoShrinkingOfInitializerOnSubclasses(
+ factory, androidAppZygotePreload));
+ }
+ return synthesizedRules;
+ }
+
+ private static ProguardAssumeNoSideEffectRule buildAssumeNoSideEffectsRuleForApiLevel(
DexItemFactory factory, AndroidApiLevel apiLevel) {
Origin synthesizedFromApiLevel =
new Origin(Origin.root()) {
@@ -53,7 +89,7 @@
* Check if an explicit rule matching the field public static final int
* android.os.Build$VERSION.SDK_INT is present.
*/
- public static boolean hasExplicitAssumeValuesOrAssumeNoSideEffectsRuleForMinSdk(
+ private static boolean hasExplicitAssumeValuesOrAssumeNoSideEffectsRuleForMinSdk(
DexItemFactory factory, List<ProguardConfigurationRule> rules) {
for (ProguardConfigurationRule rule : rules) {
if (!(rule instanceof ProguardAssumeValuesRule
@@ -108,4 +144,28 @@
}
return false;
}
+
+ // -keepclassmembers,allow* !abstract class * extends T { void <init>(); }
+ private static ProguardKeepRule buildKeepClassMembersNoShrinkingOfInitializerOnSubclasses(
+ DexItemFactory factory, DexClass clazz) {
+ return ProguardKeepRule.builder()
+ .setClassNames(ProguardClassNameList.singletonList(ProguardTypeMatcher.allClassesMatcher()))
+ .setClassType(ProguardClassType.CLASS)
+ .setInheritanceClassName(ProguardTypeMatcher.create(clazz.getType()))
+ .setInheritanceIsExtends(!clazz.isInterface())
+ .setMemberRules(
+ Collections.singletonList(
+ ProguardMemberRule.builder()
+ .setRuleType(ProguardMemberType.INIT)
+ .setName(IdentifierPatternWithWildcards.init())
+ .setArguments(Collections.emptyList())
+ .setTypeMatcher(ProguardTypeMatcher.create(factory.voidType))
+ .build()))
+ .setNegatedClassAccessFlags(new ProguardAccessFlags().setAbstract())
+ .setOrigin(clazz.getOrigin())
+ .setType(ProguardKeepRuleType.KEEP_CLASS_MEMBERS)
+ .updateModifiers(
+ modifiersBuilder -> modifiersBuilder.setAllowsAll().setAllowsShrinking(false).build())
+ .build();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
index a2619f7..ec825c3 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepRuleModifiers.java
@@ -18,6 +18,16 @@
private Builder() {}
+ public Builder setAllowsAll() {
+ setAllowsAccessModification(true);
+ setAllowsAnnotationRemoval(true);
+ setAllowsObfuscation(true);
+ setAllowsOptimization(true);
+ setAllowsRepackaging(true);
+ setAllowsShrinking(true);
+ return this;
+ }
+
public Builder setAllowsAccessModification(boolean allowsAccessModification) {
this.allowsAccessModification = allowsAccessModification;
return this;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
index 19bd876..fee5278 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardTypeMatcher.java
@@ -125,6 +125,10 @@
return new MatchSpecificType(type);
}
+ public static ProguardTypeMatcher allClassesMatcher() {
+ return MatchClassTypes.MATCH_CLASS_TYPES;
+ }
+
public static ProguardTypeMatcher defaultAllMatcher() {
return MatchAllTypes.MATCH_ALL_TYPES;
}
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 2b6c654..a8fa6fb 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -288,7 +288,8 @@
markMatchingOverriddenMethods(clazz, memberKeepRules, rule, null, true, ifRule);
markMatchingVisibleFields(clazz, memberKeepRules, rule, null, true, ifRule);
}
- } else if (rule instanceof NoFieldTypeStrengtheningRule) {
+ } else if (rule instanceof NoFieldTypeStrengtheningRule
+ || rule instanceof NoRedundantFieldLoadEliminationRule) {
markMatchingFields(clazz, memberKeepRules, rule, null, ifRule);
} else if (rule instanceof InlineRule
|| rule instanceof KeepConstantArgumentRule
@@ -1237,6 +1238,13 @@
.asFieldJoiner()
.disallowFieldTypeStrengthening();
context.markAsUsed();
+ } else if (context instanceof NoRedundantFieldLoadEliminationRule) {
+ assert item.isProgramField();
+ dependentMinimumKeepInfo
+ .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+ .asFieldJoiner()
+ .disallowRedundantFieldLoadElimination();
+ context.markAsUsed();
} else if (context instanceof NoUnusedInterfaceRemovalRule) {
noUnusedInterfaceRemoval.add(item.asClass().type);
context.markAsUsed();
@@ -2003,9 +2011,9 @@
if (field != null
&& (field.getAccessFlags().isStatic()
|| isKeptDirectlyOrIndirectly(field.getHolderType(), appView))) {
- assert appView.appInfo().isFieldRead(field.getDefinition())
+ assert appView.appInfo().isFieldRead(field)
: "Expected kept field `" + fieldReference.toSourceString() + "` to be read";
- assert appView.appInfo().isFieldWritten(field.getDefinition())
+ assert appView.appInfo().isFieldWritten(field)
: "Expected kept field `"
+ fieldReference.toSourceString()
+ "` to be written";
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 150e7c8..b1673ba 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1208,12 +1208,12 @@
builder.addFormalTypeParameters(targetSignature.getFormalTypeParameters());
if (!source.isInterface()) {
if (rewrittenSource.hasSignature()) {
- builder.setSuperClassSignature(rewrittenSource.superClassSignature());
+ builder.setSuperClassSignature(rewrittenSource.getSuperClassSignatureOrNull());
} else {
builder.setSuperClassSignature(new ClassTypeSignature(source.superType));
}
} else {
- builder.setSuperClassSignature(targetSignature.superClassSignature());
+ builder.setSuperClassSignature(targetSignature.getSuperClassSignatureOrNull());
}
// Compute the seen set for interfaces to add. This is similar to the merging of interfaces
// but allow us to maintain the type arguments.
@@ -1221,26 +1221,26 @@
if (source.isInterface()) {
seenInterfaces.add(source.type);
}
- for (ClassTypeSignature iFace : targetSignature.superInterfaceSignatures()) {
+ for (ClassTypeSignature iFace : targetSignature.getSuperInterfaceSignatures()) {
if (seenInterfaces.add(iFace.type())) {
- builder.addInterface(iFace);
+ builder.addSuperInterfaceSignature(iFace);
}
}
if (rewrittenSource.hasSignature()) {
- for (ClassTypeSignature iFace : rewrittenSource.superInterfaceSignatures()) {
+ for (ClassTypeSignature iFace : rewrittenSource.getSuperInterfaceSignatures()) {
if (!seenInterfaces.contains(iFace.type())) {
- builder.addInterface(iFace);
+ builder.addSuperInterfaceSignature(iFace);
}
}
} else {
// Synthesize raw uses of interfaces to align with the actual class
for (DexType iFace : source.interfaces) {
if (!seenInterfaces.contains(iFace)) {
- builder.addInterface(new ClassTypeSignature(iFace));
+ builder.addSuperInterfaceSignature(new ClassTypeSignature(iFace));
}
}
}
- target.setClassSignature(builder.build());
+ target.setClassSignature(builder.build(appView.dexItemFactory()));
// Go through all type-variable references for members and update them.
CollectionUtils.forEach(
@@ -1273,7 +1273,9 @@
// We can assert proper structure below because the generic signature validator has run
// before and pruned invalid signatures.
List<FieldTypeSignature> genericArgumentsToSuperType =
- target.getClassSignature().getGenericArgumentsToSuperType(source.type);
+ target
+ .getClassSignature()
+ .getGenericArgumentsToSuperType(source.type, appView.dexItemFactory());
if (genericArgumentsToSuperType == null) {
assert false : "Type should be present in generic signature";
return null;
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index 24dab29..9f7b7f2 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -1018,6 +1018,11 @@
SynthesizingContext outerContext = SynthesizingContext.fromType(globalType);
DexProgramClass globalSynthetic =
internalEnsureFixedProgramClass(kind, fn, onCreationConsumer, outerContext, appView);
+ Consumer<DexProgramClass> globalSyntheticCreatedCallback =
+ appView.options().testing.globalSyntheticCreatedCallback;
+ if (globalSyntheticCreatedCallback != null) {
+ globalSyntheticCreatedCallback.accept(globalSynthetic);
+ }
addGlobalContexts(globalSynthetic.getType(), contexts);
return globalSynthetic;
}
diff --git a/src/main/java/com/android/tools/r8/utils/CollectionUtils.java b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
index f5eea66..9a9d27c 100644
--- a/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/CollectionUtils.java
@@ -20,6 +20,14 @@
return collection;
}
+ public static <T> T getFirst(Collection<T> collection) {
+ return collection.iterator().next();
+ }
+
+ public static <T> T getFirstOrDefault(Collection<T> collection, T defaultValue) {
+ return collection.isEmpty() ? defaultValue : getFirst(collection);
+ }
+
public static <T> Set<T> mergeSets(Collection<T> first, Collection<T> second) {
ImmutableSet.Builder<T> builder = ImmutableSet.builder();
builder.addAll(first);
diff --git a/src/main/java/com/android/tools/r8/utils/DexClassAndFieldEquivalence.java b/src/main/java/com/android/tools/r8/utils/DexClassAndFieldEquivalence.java
new file mode 100644
index 0000000..8b36c33
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/DexClassAndFieldEquivalence.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.utils;
+
+import com.android.tools.r8.graph.DexClassAndField;
+import com.google.common.base.Equivalence;
+
+public class DexClassAndFieldEquivalence extends Equivalence<DexClassAndField> {
+
+ private static final DexClassAndFieldEquivalence INSTANCE = new DexClassAndFieldEquivalence();
+
+ private DexClassAndFieldEquivalence() {}
+
+ public static DexClassAndFieldEquivalence get() {
+ return INSTANCE;
+ }
+
+ @Override
+ protected boolean doEquivalent(DexClassAndField field, DexClassAndField other) {
+ return field.getDefinition() == other.getDefinition();
+ }
+
+ @Override
+ protected int doHash(DexClassAndField field) {
+ return field.getReference().hashCode();
+ }
+}
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 615c439..8a733a5 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -890,7 +890,7 @@
new OpenClosedInterfacesOptions();
private final ProtoShrinkingOptions protoShrinking = new ProtoShrinkingOptions();
private final RedundantBridgeRemovalOptions redundantBridgeRemovalOptions =
- new RedundantBridgeRemovalOptions(this);
+ new RedundantBridgeRemovalOptions();
private final KotlinOptimizationOptions kotlinOptimizationOptions =
new KotlinOptimizationOptions();
private final ApiModelTestingOptions apiModelTestingOptions = new ApiModelTestingOptions();
@@ -1626,6 +1626,8 @@
public static class InlinerOptions {
+ public boolean enableConstructorInlining = true;
+
public boolean enableInlining =
!parseSystemPropertyForDevelopmentOrDefault("com.android.tools.r8.disableinlining", false);
@@ -1683,6 +1685,14 @@
return 5;
}
+ public boolean isConstructorInliningEnabled() {
+ return enableConstructorInlining;
+ }
+
+ public void setEnableConstructorInlining(boolean enableConstructorInlining) {
+ this.enableConstructorInlining = enableConstructorInlining;
+ }
+
public boolean shouldApplyInliningToInlinee(
AppView<?> appView, ProgramMethod inlinee, int inliningDepth) {
if (applyInliningToInlineePredicateForTesting != null) {
@@ -2034,9 +2044,19 @@
public boolean shouldApplyInliningToInlinee(
AppView<?> appView, ProgramMethod inlinee, int inliningDepth) {
- if (isProtoShrinkingEnabled() && inliningDepth == 1) {
- ProtoReferences protoReferences = appView.protoShrinker().getProtoReferences();
- return inlinee.getHolderType() == protoReferences.generatedMessageLiteType;
+ if (isProtoShrinkingEnabled()) {
+ if (appView.protoShrinker().getProtoReferences().isDynamicMethodBridge(inlinee)) {
+ return true;
+ }
+ if (inliningDepth <= 1) {
+ ProtoReferences protoReferences = appView.protoShrinker().getProtoReferences();
+ if (inlinee.getHolderType() == protoReferences.generatedMessageLiteType) {
+ return true;
+ }
+ if (inlinee.getHolder().getSuperType() == protoReferences.generatedMessageLiteType) {
+ return true;
+ }
+ }
}
return false;
}
@@ -2141,6 +2161,8 @@
public Consumer<DebugRepresentation> debugRepresentationCallback = null;
+ public Consumer<DexProgramClass> globalSyntheticCreatedCallback = null;
+
/**
* If this flag is enabled, we will also compute the set of possible targets for invoke-
* interface and invoke-virtual instructions that target a library method, and add the
@@ -2541,6 +2563,10 @@
return hasFeaturePresentFrom(AndroidApiLevel.K);
}
+ public boolean canUseCanonicalizedCodeObjects() {
+ return hasFeaturePresentFrom(AndroidApiLevel.S);
+ }
+
public CfVersion classFileVersionAfterDesugaring(CfVersion version) {
assert isGeneratingClassFiles();
if (!isDesugaring()) {
@@ -2960,4 +2986,8 @@
public boolean canHaveVerifyErrorForUnknownUnusedReturnValue() {
return isGeneratingDex() && canHaveBugPresentUntil(AndroidApiLevel.T);
}
+
+ public boolean canInitNewInstanceUsingSuperclassConstructor() {
+ return canHaveNonReboundConstructorInvoke();
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/IterableUtils.java b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
index 5ca1e24e..3da57da 100644
--- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -48,6 +48,19 @@
return false;
}
+ public static <T> boolean anyBefore(
+ Iterable<T> iterable, Predicate<T> predicate, Predicate<T> stoppingCriterion) {
+ for (T element : iterable) {
+ if (stoppingCriterion.test(element)) {
+ return false;
+ }
+ if (predicate.test(element)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public static <T> Iterable<T> append(Iterable<T> iterable, T element) {
return Iterables.concat(iterable, singleton(element));
}
diff --git a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
index a34ec96..0dd5b36 100644
--- a/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/IteratorUtils.java
@@ -156,8 +156,14 @@
}
public static void skip(InstructionIterator iterator, int times) {
- for (int i = 0; i < times; i++) {
- iterator.next();
+ if (times >= 0) {
+ for (int i = 0; i < times; i++) {
+ iterator.next();
+ }
+ } else {
+ for (int i = 0; i > times; i--) {
+ iterator.previous();
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/utils/ListUtils.java b/src/main/java/com/android/tools/r8/utils/ListUtils.java
index 47549a6..c998cf6 100644
--- a/src/main/java/com/android/tools/r8/utils/ListUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ListUtils.java
@@ -55,12 +55,14 @@
return result;
}
- public static <T> List<T> filter(Collection<T> list, Predicate<? super T> predicate) {
+ @SuppressWarnings("unchecked")
+ public static <S, T extends S> List<T> filter(
+ Collection<S> list, Predicate<? super S> predicate) {
ArrayList<T> filtered = new ArrayList<>(list.size());
list.forEach(
- t -> {
- if (predicate.test(t)) {
- filtered.add(t);
+ s -> {
+ if (predicate.test(s)) {
+ filtered.add((T) s);
}
});
return filtered;
diff --git a/src/main/java/com/android/tools/r8/utils/OffOrAuto.java b/src/main/java/com/android/tools/r8/utils/OffOrAuto.java
index 83d6244..3375b25 100644
--- a/src/main/java/com/android/tools/r8/utils/OffOrAuto.java
+++ b/src/main/java/com/android/tools/r8/utils/OffOrAuto.java
@@ -4,31 +4,7 @@
package com.android.tools.r8.utils;
-import joptsimple.ValueConversionException;
-import joptsimple.ValueConverter;
-
public enum OffOrAuto {
- Off, Auto;
-
- static final ValueConverter<OffOrAuto> CONVERTER = new ValueConverter<OffOrAuto>() {
- @Override
- public OffOrAuto convert(String input) {
- try {
- input = Character.toUpperCase(input.charAt(0)) + input.substring(1).toLowerCase();
- return Enum.valueOf(OffOrAuto.class, input);
- } catch (Exception e) {
- throw new ValueConversionException("Value must be one of: " + valuePattern());
- }
- }
-
- @Override
- public Class<OffOrAuto> valueType() {
- return OffOrAuto.class;
- }
-
- @Override
- public String valuePattern() {
- return "off|auto";
- }
- };
+ Off,
+ Auto
}
diff --git a/src/main/java/com/android/tools/r8/utils/PartitionMapZipContainer.java b/src/main/java/com/android/tools/r8/utils/PartitionMapZipContainer.java
index 242fbcc..e2af968 100644
--- a/src/main/java/com/android/tools/r8/utils/PartitionMapZipContainer.java
+++ b/src/main/java/com/android/tools/r8/utils/PartitionMapZipContainer.java
@@ -32,7 +32,6 @@
.setMappingPartitionFromKeySupplier(
key -> {
try {
- // TODO(b/274735214): The key should exist.
ZipEntry entry = zipFile.getEntry(key);
return entry == null
? EMPTY_RESULT
diff --git a/src/main/java/com/android/tools/r8/utils/ProgramFieldEquivalence.java b/src/main/java/com/android/tools/r8/utils/ProgramFieldEquivalence.java
deleted file mode 100644
index 193df46..0000000
--- a/src/main/java/com/android/tools/r8/utils/ProgramFieldEquivalence.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.utils;
-
-import com.android.tools.r8.graph.ProgramField;
-import com.google.common.base.Equivalence;
-
-public class ProgramFieldEquivalence extends Equivalence<ProgramField> {
-
- private static final ProgramFieldEquivalence INSTANCE = new ProgramFieldEquivalence();
-
- private ProgramFieldEquivalence() {}
-
- public static ProgramFieldEquivalence get() {
- return INSTANCE;
- }
-
- @Override
- protected boolean doEquivalent(ProgramField field, ProgramField other) {
- return field.getDefinition() == other.getDefinition();
- }
-
- @Override
- protected int doHash(ProgramField field) {
- return field.getReference().hashCode();
- }
-}
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index 9885ab6..af857c4 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -11,9 +11,11 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Matcher;
@@ -273,14 +275,25 @@
return join(LINE_SEPARATOR, collection, BraceType.NONE);
}
-
public static List<String> splitLines(String content) {
return splitLines(content, false);
}
+ public static Set<String> splitLinesIntoSet(String content) {
+ Set<String> set = new HashSet<>();
+ splitLines(content, false, set::add);
+ return set;
+ }
+
public static List<String> splitLines(String content, boolean includeTrailingEmptyLine) {
+ List<String> list = new ArrayList<>();
+ splitLines(content, includeTrailingEmptyLine, list::add);
+ return list;
+ }
+
+ private static void splitLines(
+ String content, boolean includeTrailingEmptyLine, Consumer<String> consumer) {
int length = content.length();
- List<String> lines = new ArrayList<>();
int start = 0;
for (int i = 0; i < length; i++) {
char c = content.charAt(i);
@@ -290,16 +303,15 @@
} else if (c != '\n') {
continue;
}
- lines.add(content.substring(start, end));
+ consumer.accept(content.substring(start, end));
start = i + 1;
}
if (start < length) {
String line = content.substring(start);
if (includeTrailingEmptyLine || !line.isEmpty()) {
- lines.add(line);
+ consumer.accept(line);
}
}
- return lines;
}
public static String zeroPrefix(int i, int width) {
diff --git a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
index 45c36b6..528ba57 100644
--- a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils;
+import static com.google.common.base.Predicates.alwaysTrue;
+
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.utils.ListUtils.ReferenceAndIntConsumer;
@@ -18,6 +20,7 @@
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.function.Consumer;
+import java.util.function.Predicate;
public class ThreadUtils {
@@ -71,6 +74,16 @@
return processItemsWithResults(items, (item, i) -> consumer.apply(item), executorService);
}
+ public static <T, R, E extends Exception> Collection<R> processItemsWithResultsThatMatches(
+ Iterable<T> items,
+ ThrowingFunction<T, R, E> consumer,
+ Predicate<R> predicate,
+ ExecutorService executorService)
+ throws ExecutionException {
+ return processItemsWithResultsThatMatches(
+ items, (item, i) -> consumer.apply(item), predicate, executorService);
+ }
+
public static <T, R, E extends Exception> Collection<R> processItemsWithResults(
Iterable<T> items,
ThrowingReferenceIntFunction<T, R, E> consumer,
@@ -79,6 +92,15 @@
return processItemsWithResults(items::forEach, consumer, executorService);
}
+ public static <T, R, E extends Exception> Collection<R> processItemsWithResultsThatMatches(
+ Iterable<T> items,
+ ThrowingReferenceIntFunction<T, R, E> consumer,
+ Predicate<R> predicate,
+ ExecutorService executorService)
+ throws ExecutionException {
+ return processItemsWithResultsThatMatches(items::forEach, consumer, predicate, executorService);
+ }
+
public static <T, R, E extends Exception> Collection<R> processItemsWithResults(
ForEachable<T> items, ThrowingFunction<T, R, E> consumer, ExecutorService executorService)
throws ExecutionException {
@@ -97,7 +119,23 @@
int index = indexSupplier.getAndIncrement();
futures.add(executorService.submit(() -> consumer.apply(item, index)));
});
- return awaitFuturesWithResults(futures);
+ return awaitFuturesWithResults(futures, alwaysTrue());
+ }
+
+ public static <T, R, E extends Exception> Collection<R> processItemsWithResultsThatMatches(
+ ForEachable<T> items,
+ ThrowingReferenceIntFunction<T, R, E> consumer,
+ Predicate<R> predicate,
+ ExecutorService executorService)
+ throws ExecutionException {
+ IntBox indexSupplier = new IntBox();
+ List<Future<R>> futures = new ArrayList<>();
+ items.forEach(
+ item -> {
+ int index = indexSupplier.getAndIncrement();
+ futures.add(executorService.submit(() -> consumer.apply(item, index)));
+ });
+ return awaitFuturesWithResults(futures, predicate);
}
public static <T> void processItems(
@@ -195,13 +233,17 @@
}
}
- public static <R> Collection<R> awaitFuturesWithResults(Collection<? extends Future<R>> futures)
- throws ExecutionException {
- List<R> results = new ArrayList<>(futures.size());
+ public static <R> Collection<R> awaitFuturesWithResults(
+ Collection<? extends Future<R>> futures, Predicate<R> predicate) throws ExecutionException {
+ List<R> results =
+ predicate == alwaysTrue() ? new ArrayList<>(futures.size()) : new ArrayList<>();
Iterator<? extends Future<R>> futureIterator = futures.iterator();
try {
while (futureIterator.hasNext()) {
- results.add(futureIterator.next().get());
+ R result = futureIterator.next().get();
+ if (predicate.test(result)) {
+ results.add(result);
+ }
}
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while waiting for future.", e);
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndFieldMap.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndFieldMap.java
new file mode 100644
index 0000000..ab2a69a
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndFieldMap.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2023, 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.collections;
+
+import com.android.tools.r8.graph.DexClassAndField;
+import com.google.common.base.Equivalence.Wrapper;
+import com.google.common.collect.ImmutableMap;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Supplier;
+
+public class DexClassAndFieldMap<V> extends DexClassAndFieldMapBase<DexClassAndField, V> {
+
+ private static final DexClassAndFieldMap<?> EMPTY = new DexClassAndFieldMap<>(ImmutableMap::of);
+
+ private DexClassAndFieldMap(Supplier<Map<Wrapper<DexClassAndField>, V>> backingFactory) {
+ super(backingFactory);
+ }
+
+ public static <V> DexClassAndFieldMap<V> create() {
+ return new DexClassAndFieldMap<>(HashMap::new);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <V> DexClassAndFieldMap<V> empty() {
+ return (DexClassAndFieldMap<V>) EMPTY;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndFieldMapBase.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndFieldMapBase.java
new file mode 100644
index 0000000..522ff17
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndFieldMapBase.java
@@ -0,0 +1,24 @@
+// Copyright (c) 2023, 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.collections;
+
+import com.android.tools.r8.graph.DexClassAndField;
+import com.android.tools.r8.utils.DexClassAndFieldEquivalence;
+import com.google.common.base.Equivalence.Wrapper;
+import java.util.Map;
+import java.util.function.Supplier;
+
+public abstract class DexClassAndFieldMapBase<K extends DexClassAndField, V>
+ extends DexClassAndMemberMap<K, V> {
+
+ DexClassAndFieldMapBase(Supplier<Map<Wrapper<K>, V>> backingFactory) {
+ super(backingFactory);
+ }
+
+ @Override
+ Wrapper<K> wrap(K field) {
+ return DexClassAndFieldEquivalence.get().wrap(field);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramMemberMap.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMemberMap.java
similarity index 88%
rename from src/main/java/com/android/tools/r8/utils/collections/ProgramMemberMap.java
rename to src/main/java/com/android/tools/r8/utils/collections/DexClassAndMemberMap.java
index ebba98d..2a044e5 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramMemberMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMemberMap.java
@@ -4,7 +4,7 @@
package com.android.tools.r8.utils.collections;
-import com.android.tools.r8.graph.ProgramMember;
+import com.android.tools.r8.graph.DexClassAndMember;
import com.google.common.base.Equivalence.Wrapper;
import java.util.Map;
import java.util.function.BiConsumer;
@@ -13,15 +13,15 @@
import java.util.function.Function;
import java.util.function.Supplier;
-public abstract class ProgramMemberMap<K extends ProgramMember<?, ?>, V> {
+public abstract class DexClassAndMemberMap<K extends DexClassAndMember<?, ?>, V> {
private final Map<Wrapper<K>, V> backing;
- ProgramMemberMap(Supplier<Map<Wrapper<K>, V>> backingFactory) {
+ DexClassAndMemberMap(Supplier<Map<Wrapper<K>, V>> backingFactory) {
this.backing = backingFactory.get();
}
- ProgramMemberMap(Map<Wrapper<K>, V> backing) {
+ DexClassAndMemberMap(Map<Wrapper<K>, V> backing) {
this.backing = backing;
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldMap.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldMap.java
index dc6a4be..a8f41e2 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramFieldMap.java
@@ -5,14 +5,13 @@
package com.android.tools.r8.utils.collections;
import com.android.tools.r8.graph.ProgramField;
-import com.android.tools.r8.utils.ProgramFieldEquivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.collect.ImmutableMap;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
-public class ProgramFieldMap<V> extends ProgramMemberMap<ProgramField, V> {
+public class ProgramFieldMap<V> extends DexClassAndFieldMapBase<ProgramField, V> {
private static final ProgramFieldMap<?> EMPTY = new ProgramFieldMap<>(ImmutableMap::of);
@@ -28,9 +27,4 @@
public static <V> ProgramFieldMap<V> empty() {
return (ProgramFieldMap<V>) EMPTY;
}
-
- @Override
- Wrapper<ProgramField> wrap(ProgramField method) {
- return ProgramFieldEquivalence.get().wrap(method);
- }
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodMap.java b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodMap.java
index d4dcca6..b5c3c26 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodMap.java
@@ -13,7 +13,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
-public class ProgramMethodMap<V> extends ProgramMemberMap<ProgramMethod, V> {
+public class ProgramMethodMap<V> extends DexClassAndMemberMap<ProgramMethod, V> {
private static final ProgramMethodMap<?> EMPTY = new ProgramMethodMap<>(ImmutableMap::of);
diff --git a/src/main/keep_retrace.txt b/src/main/keep_retrace.txt
index e8d050a..6ddb273 100644
--- a/src/main/keep_retrace.txt
+++ b/src/main/keep_retrace.txt
@@ -11,9 +11,18 @@
-keepattributes SourceFile, LineNumberTable, InnerClasses, EnclosingMethod, Exceptions, Signature
-keepparameternames
-# This is run on r8lib so keep everything in lib that is traced. That way
-# we only need a single mapping file
--keep,allowshrinking class * { *; }
+-repackageclasses com.android.tools.r8.retrace_internal
# Keep all things that can be reached from the retrace api
-keep @com.android.tools.r8.KeepForRetraceApi class * { public *; }
+
+-keep,allowshrinking @com.android.tools.r8.Keep class * { public *; }
+-keep,allowshrinking @com.android.tools.r8.KeepForSubclassing class * { public *; protected *; }
+
+-keep public class com.android.tools.r8.Version { public static final java.lang.String LABEL; }
+-keep public class com.android.tools.r8.Version { public static java.lang.String getVersionString(); }
+-keep public class com.android.tools.r8.Version { public static int getMajorVersion(); }
+-keep public class com.android.tools.r8.Version { public static int getMinorVersion(); }
+-keep public class com.android.tools.r8.Version { public static int getPatchVersion(); }
+-keep public class com.android.tools.r8.Version { public static java.lang.String getPreReleaseString(); }
+-keep public class com.android.tools.r8.Version { public static boolean isDevelopmentVersion(); }
diff --git a/src/test/examples/adaptresourcefilenames/NoInliningOfDefaultInitializer.java b/src/test/examples/adaptresourcefilenames/NoInliningOfDefaultInitializer.java
new file mode 100644
index 0000000..d7a6029
--- /dev/null
+++ b/src/test/examples/adaptresourcefilenames/NoInliningOfDefaultInitializer.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2023, 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 adaptresourcefilenames;
+
+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.TYPE})
+public @interface NoInliningOfDefaultInitializer {}
diff --git a/src/test/examples/adaptresourcefilenames/pkg/C.java b/src/test/examples/adaptresourcefilenames/pkg/C.java
index 93d4446..85d846e 100644
--- a/src/test/examples/adaptresourcefilenames/pkg/C.java
+++ b/src/test/examples/adaptresourcefilenames/pkg/C.java
@@ -4,6 +4,9 @@
package adaptresourcefilenames.pkg;
+import adaptresourcefilenames.NoInliningOfDefaultInitializer;
+
+@NoInliningOfDefaultInitializer
public class C {
public void method() {
diff --git a/src/test/examples/adaptresourcefilenames/pkg/innerpkg/D.java b/src/test/examples/adaptresourcefilenames/pkg/innerpkg/D.java
index 2107547..03fc12e 100644
--- a/src/test/examples/adaptresourcefilenames/pkg/innerpkg/D.java
+++ b/src/test/examples/adaptresourcefilenames/pkg/innerpkg/D.java
@@ -4,6 +4,9 @@
package adaptresourcefilenames.pkg.innerpkg;
+import adaptresourcefilenames.NoInliningOfDefaultInitializer;
+
+@NoInliningOfDefaultInitializer
public class D {
public void method() {
diff --git a/src/test/examples/classmerging/ConflictInGeneratedNameTest.java b/src/test/examples/classmerging/ConflictInGeneratedNameTest.java
index 4ef0b3e..528f9bf 100644
--- a/src/test/examples/classmerging/ConflictInGeneratedNameTest.java
+++ b/src/test/examples/classmerging/ConflictInGeneratedNameTest.java
@@ -4,6 +4,7 @@
package classmerging;
+
public class ConflictInGeneratedNameTest {
public static void main(String[] args) {
B obj = new B();
@@ -11,7 +12,7 @@
}
public static class A {
- @NeverPropagateValue private String name = "A";
+ @NeverPropagateValue @NoRedundantFieldLoadElimination private String name = "A";
public A() {
print("In A.<init>()");
@@ -56,8 +57,10 @@
}
public static class B extends A {
- @NeverPropagateValue private String name = "B";
- @NeverPropagateValue private String name$classmerging$ConflictInGeneratedNameTest$A = "C";
+ @NeverPropagateValue @NoRedundantFieldLoadElimination private String name = "B";
+
+ @NeverPropagateValue @NoRedundantFieldLoadElimination
+ private String name$classmerging$ConflictInGeneratedNameTest$A = "C";
public B() {
print("In B.<init>()");
diff --git a/src/test/examples/classmerging/NoRedundantFieldLoadElimination.java b/src/test/examples/classmerging/NoRedundantFieldLoadElimination.java
new file mode 100644
index 0000000..5251e01
--- /dev/null
+++ b/src/test/examples/classmerging/NoRedundantFieldLoadElimination.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2023, 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 classmerging;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.FIELD})
+public @interface NoRedundantFieldLoadElimination {}
diff --git a/src/test/examples/classmerging/keep-rules.txt b/src/test/examples/classmerging/keep-rules.txt
index 9c7188b..8b87740 100644
--- a/src/test/examples/classmerging/keep-rules.txt
+++ b/src/test/examples/classmerging/keep-rules.txt
@@ -74,3 +74,4 @@
}
-neverpropagatevalue class * { @classmerging.NeverPropagateValue *; }
-nohorizontalclassmerging @classmerging.NoHorizontalClassMerging class *
+-noredundantfieldloadelimination class * { @classmerging.NoRedundantFieldLoadElimination *; }
diff --git a/src/test/examples/throwing/proguard.cfg b/src/test/examples/throwing/proguard.cfg
deleted file mode 100644
index 6de724a..0000000
--- a/src/test/examples/throwing/proguard.cfg
+++ /dev/null
@@ -1,14 +0,0 @@
-# Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-# for details. All rights reserved. Use of this source code is governed by a
-# BSD-style license that can be found in the LICENSE file.
-
--keep class throwing.Throwing {
- public static void main(java.lang.String[]);
-}
-
--keep,allowobfuscation class throwing.Overloaded {
- *;
-}
-
--keepattributes SourceFile,LineNumberTable
-
diff --git a/src/test/examples/trivial/Trivial.java b/src/test/examples/trivial/Trivial.java
deleted file mode 100644
index b6b8299..0000000
--- a/src/test/examples/trivial/Trivial.java
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package trivial;
-
-public class Trivial {
- public static void main(String[] args) {
- }
-}
diff --git a/src/test/examplesJava17/records/RecordBlog.java b/src/test/examplesJava17/records/RecordBlog.java
new file mode 100644
index 0000000..0b40c86
--- /dev/null
+++ b/src/test/examplesJava17/records/RecordBlog.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2023, 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 records;
+
+public class RecordBlog {
+
+ record Person(String name, int age) {}
+
+ public static void main(String[] args) {
+ Person jane = new Person("Jane", 42);
+ System.out.println(jane.toString());
+ Person john = new Person("John", 42);
+ System.out.println(john.toString());
+ }
+}
diff --git a/src/test/examplesJava18/jdk8272564/A.java b/src/test/examplesJava20/jdk8272564/A.java
similarity index 100%
rename from src/test/examplesJava18/jdk8272564/A.java
rename to src/test/examplesJava20/jdk8272564/A.java
diff --git a/src/test/examplesJava18/jdk8272564/B.java b/src/test/examplesJava20/jdk8272564/B.java
similarity index 100%
rename from src/test/examplesJava18/jdk8272564/B.java
rename to src/test/examplesJava20/jdk8272564/B.java
diff --git a/src/test/examplesJava18/jdk8272564/C.java b/src/test/examplesJava20/jdk8272564/C.java
similarity index 100%
rename from src/test/examplesJava18/jdk8272564/C.java
rename to src/test/examplesJava20/jdk8272564/C.java
diff --git a/src/test/examplesJava18/jdk8272564/I.java b/src/test/examplesJava20/jdk8272564/I.java
similarity index 100%
rename from src/test/examplesJava18/jdk8272564/I.java
rename to src/test/examplesJava20/jdk8272564/I.java
diff --git a/src/test/examplesJava18/jdk8272564/J.java b/src/test/examplesJava20/jdk8272564/J.java
similarity index 100%
rename from src/test/examplesJava18/jdk8272564/J.java
rename to src/test/examplesJava20/jdk8272564/J.java
diff --git a/src/test/examplesJava18/jdk8272564/K.java b/src/test/examplesJava20/jdk8272564/K.java
similarity index 100%
rename from src/test/examplesJava18/jdk8272564/K.java
rename to src/test/examplesJava20/jdk8272564/K.java
diff --git a/src/test/examplesJava18/jdk8272564/Main.java b/src/test/examplesJava20/jdk8272564/Main.java
similarity index 100%
rename from src/test/examplesJava18/jdk8272564/Main.java
rename to src/test/examplesJava20/jdk8272564/Main.java
diff --git a/src/test/java/com/android/tools/r8/ExtractMarkerTest.java b/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
index 1f0ef3c..c7a260a 100644
--- a/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/ExtractMarkerTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.ExtractMarkerUtils;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.Set;
@@ -23,8 +24,15 @@
@RunWith(Parameterized.class)
public class ExtractMarkerTest extends TestBase {
- private static final String CLASS_FILE =
- ToolHelper.EXAMPLES_BUILD_DIR + "classes/trivial/Trivial.class";
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+
+ }
+ }
+
+ private static final Path CLASS_FILE = ToolHelper.getClassFileForTestClass(TestClass.class);
private final TestParameters parameters;
private boolean includeClassesChecksum;
@@ -55,7 +63,7 @@
boolean[] testExecuted = {false};
D8.run(
D8Command.builder()
- .addProgramFiles(Paths.get(CLASS_FILE))
+ .addProgramFiles(CLASS_FILE)
.setMinApiLevel(parameters.getApiLevel().getLevel())
.setIncludeClassesChecksum(includeClassesChecksum)
.setProgramConsumer(
@@ -98,7 +106,7 @@
boolean[] testExecuted = {false};
R8.run(
R8Command.builder()
- .addProgramFiles(Paths.get(CLASS_FILE))
+ .addProgramFiles(CLASS_FILE)
.addLibraryFiles(ToolHelper.getJava8RuntimeJar())
.setMode(CompilationMode.DEBUG)
.setDisableTreeShaking(true)
diff --git a/src/test/java/com/android/tools/r8/NoInliningOfDefaultInitializer.java b/src/test/java/com/android/tools/r8/NoInliningOfDefaultInitializer.java
new file mode 100644
index 0000000..6067415
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NoInliningOfDefaultInitializer.java
@@ -0,0 +1,13 @@
+// Copyright (c) 2023, 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.TYPE})
+public @interface NoInliningOfDefaultInitializer {}
diff --git a/src/test/java/com/android/tools/r8/NoRedundantFieldLoadElimination.java b/src/test/java/com/android/tools/r8/NoRedundantFieldLoadElimination.java
new file mode 100644
index 0000000..16b6ebe
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NoRedundantFieldLoadElimination.java
@@ -0,0 +1,10 @@
+// Copyright (c) 2023, 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.FIELD})
+public @interface NoRedundantFieldLoadElimination {}
diff --git a/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java b/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
index e38ecf7..74e36ab 100644
--- a/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
+++ b/src/test/java/com/android/tools/r8/ProguardMapMarkerTest.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ExtractMarkerUtils;
import com.android.tools.r8.utils.VersionProperties;
+import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.HashSet;
@@ -26,10 +27,16 @@
@RunWith(Parameterized.class)
public class ProguardMapMarkerTest extends TestBase {
+ static class TestClass {
+
+ public static void main(String[] args) {
+
+ }
+ }
+
private static final int EXPECTED_NUMBER_OF_KEYS_DEX = 6;
private static final int EXPECTED_NUMBER_OF_KEYS_CF = 5;
- private static final String CLASS_FILE =
- ToolHelper.EXAMPLES_BUILD_DIR + "classes/trivial/Trivial.class";
+ private static final Path CLASS_FILE = ToolHelper.getClassFileForTestClass(TestClass.class);
@Parameters(name = "{0}")
public static TestParametersCollection data() {
@@ -60,7 +67,7 @@
ProguardMapIds proguardMapIds = new ProguardMapIds();
R8.run(
R8Command.builder()
- .addProgramFiles(Paths.get(CLASS_FILE))
+ .addProgramFiles(CLASS_FILE)
.setDisableTreeShaking(true)
.setProgramConsumer(
new DexIndexedConsumer.ForwardingConsumer(null) {
@@ -107,7 +114,7 @@
ProguardMapIds buildIds = new ProguardMapIds();
R8.run(
R8Command.builder()
- .addProgramFiles(Paths.get(CLASS_FILE))
+ .addProgramFiles(CLASS_FILE)
.setDisableTreeShaking(true)
.setProgramConsumer(
new ClassFileConsumer.ForwardingConsumer(null) {
diff --git a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
index 6e40eef..e5db712 100644
--- a/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunExamplesTest.java
@@ -27,37 +27,9 @@
String[] tests = {
"arithmetic.Arithmetic",
"inlining.Inlining",
- "nestedtrycatches.NestedTryCatches",
- "regalloc.RegAlloc",
- "returns.Returns",
- "staticfield.StaticField",
- "stringbuilding.StringBuilding",
- "switches.Switches",
- "sync.Sync",
- "throwing.Throwing",
- "trivial.Trivial",
- "trycatch.TryCatch",
- "trycatchmany.TryCatchMany",
- "regress.Regress",
- "regress2.Regress2",
- "regress_37726195.Regress",
- "regress_37658666.Regress",
- "regress_37875803.Regress",
- "regress_37955340.Regress",
- "regress_62300145.Regress",
- "regress_64881691.Regress",
- "regress_65104300.Regress",
- "regress_70703087.Test",
- "regress_70736958.Test",
- "regress_70737019.Test",
- "regress_72361252.Test",
- "regress_110373181.Regress",
"memberrebinding2.Memberrebinding",
"memberrebinding3.Memberrebinding",
"minification.Minification",
- "enclosingmethod.Main",
- "enclosingmethod_proguarded.Main",
- "switchmaps.Switches",
"uninitializedfinal.UninitializedFinalFieldLeak",
};
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index b9c6c781..c6f1d08 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.shaking.NoMethodStaticizingRule;
import com.android.tools.r8.shaking.NoParameterReorderingRule;
import com.android.tools.r8.shaking.NoParameterTypeStrengtheningRule;
+import com.android.tools.r8.shaking.NoRedundantFieldLoadEliminationRule;
import com.android.tools.r8.shaking.NoReturnTypeStrengtheningRule;
import com.android.tools.r8.shaking.NoUnusedInterfaceRemovalRule;
import com.android.tools.r8.shaking.NoVerticalClassMergingRule;
@@ -555,6 +556,14 @@
NoFieldTypeStrengtheningRule.RULE_NAME, NoFieldTypeStrengthening.class);
}
+ public T enableNoInliningOfDefaultInitializerAnnotations() {
+ return addNoInliningOfDefaultInitializerAnnotation()
+ .addInternalKeepRules(
+ "-neverinline @"
+ + NoInliningOfDefaultInitializer.class.getTypeName()
+ + " class * { <init>(); }");
+ }
+
public T enableNoMethodStaticizingAnnotations() {
return addNoMethodStaticizingAnnotation()
.addInternalMatchAnnotationOnMethodRule(
@@ -573,6 +582,12 @@
NoParameterTypeStrengtheningRule.RULE_NAME, NoParameterTypeStrengthening.class);
}
+ public T enableNoRedundantFieldLoadEliminationAnnotations() {
+ return addNoRedundantFieldLoadEliminationAnnotation()
+ .addInternalMatchAnnotationOnFieldRule(
+ NoRedundantFieldLoadEliminationRule.RULE_NAME, NoRedundantFieldLoadElimination.class);
+ }
+
public T enableNoReturnTypeStrengtheningAnnotations() {
return addNoReturnTypeStrengtheningAnnotation()
.addInternalMatchAnnotationOnMethodRule(
diff --git a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
index 811c18f..8d43e3b 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesJava9Test.java
@@ -183,7 +183,7 @@
"desugared-private-interface-methods",
"privateinterfacemethods",
"PrivateInterfaceMethods")
- .withMinApiLevel(AndroidApiLevel.M.getLevel())
+ .withMinApiLevel(AndroidApiLevel.K.getLevel())
.withKeepAll()
.withDexCheck(
dexInspector -> {
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index f3fc72a..35eb6dd 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1743,6 +1743,10 @@
return AndroidApiLevel.O;
}
+ public static AndroidApiLevel apiLevelWithMethodParametersSupport() {
+ return AndroidApiLevel.O;
+ }
+
public static boolean canUseJavaUtilObjects(TestParameters parameters) {
return parameters.isCfRuntime()
|| parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K);
@@ -2040,4 +2044,12 @@
"", expected.size() > actual.size() ? expected.get(minLines) : actual.get(minLines));
}
}
+
+ public void runGlobalSyntheticsGenerator(
+ GlobalSyntheticsGeneratorCommand command, Consumer<InternalOptions> internalOptionsConsumer)
+ throws CompilationFailedException {
+ InternalOptions internalOptions = command.getInternalOptions();
+ internalOptionsConsumer.accept(internalOptions);
+ GlobalSyntheticsGenerator.runForTesting(command.getInputApp(), internalOptions);
+ }
}
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
index 33911ae..0da6332 100644
--- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -25,6 +25,7 @@
import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector;
import com.google.common.base.Suppliers;
+import com.google.common.collect.Sets;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
@@ -33,8 +34,10 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -88,6 +91,9 @@
private Optional<Integer> isAndroidBuildVersionAdded = null;
+ private static final Map<Integer, Set<String>> allowedGlobalSynthetics =
+ new ConcurrentHashMap<>();
+
LibraryDesugaringTestConfiguration libraryDesugaringTestConfiguration =
LibraryDesugaringTestConfiguration.DISABLED;
@@ -259,6 +265,25 @@
: getMinApiLevel();
builder.setMinApiLevel(minApi);
}
+ if (!noMinApiLevel && backend.isDex() && (isD8TestBuilder() || isR8TestBuilder())) {
+ int minApiLevel = builder.getMinApiLevel();
+ allowedGlobalSynthetics.computeIfAbsent(
+ minApiLevel, TestCompilerBuilder::computeAllGlobalSynthetics);
+ Consumer<InternalOptions> previousConsumer = optionsConsumer;
+ optionsConsumer =
+ options -> {
+ options.testing.globalSyntheticCreatedCallback =
+ programClass -> {
+ assertTrue(
+ allowedGlobalSynthetics
+ .get(minApiLevel)
+ .contains(programClass.getType().toDescriptorString()));
+ };
+ if (previousConsumer != null) {
+ previousConsumer.accept(options);
+ }
+ };
+ }
builder.setOptimizeMultidexForLinearAlloc(optimizeMultidexForLinearAlloc);
if (useDefaultRuntimeLibrary) {
builder.addLibraryFiles(getDefaultLibraryFiles());
@@ -534,6 +559,26 @@
return self();
}
+ private static Set<String> computeAllGlobalSynthetics(int minApiLevel) {
+ try {
+ Set<String> generatedGlobalSynthetics = Sets.newConcurrentHashSet();
+ GlobalSyntheticsGeneratorCommand command =
+ GlobalSyntheticsGeneratorCommand.builder()
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.API_DATABASE_LEVEL))
+ .setMinApiLevel(minApiLevel)
+ .setProgramConsumer(DexIndexedConsumer.emptyConsumer())
+ .build();
+ InternalOptions internalOptions = command.getInternalOptions();
+ internalOptions.testing.globalSyntheticCreatedCallback =
+ programClass ->
+ generatedGlobalSynthetics.add(programClass.getType().toDescriptorString());
+ GlobalSyntheticsGenerator.runForTesting(command.getInputApp(), internalOptions);
+ return generatedGlobalSynthetics;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private static class ChainedStringConsumer implements StringConsumer {
private final List<StringConsumer> consumers;
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index 1c44201..7e64f85 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -48,6 +48,10 @@
return builder().withNoneRuntime().build();
}
+ public boolean canHaveIssueWithInlinedMonitors() {
+ return isCfRuntime() || getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.M);
+ }
+
/**
* Returns true if the runtime uses resolution to lookup the constructor targeted by a given
* invoke, so that it is valid to have non-rebound constructor invokes.
@@ -61,6 +65,10 @@
return isDexRuntime() && getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L);
}
+ public boolean canInitNewInstanceUsingSuperclassConstructor() {
+ return canHaveNonReboundConstructorInvoke();
+ }
+
public boolean canUseDefaultAndStaticInterfaceMethods() {
assert isCfRuntime() || isDexRuntime();
assert !isCfRuntime() || apiLevel == null
@@ -138,6 +146,10 @@
return isDexRuntime() && getDexRuntimeVersion().isNewerThanOrEqual(vm);
}
+ public boolean isDexRuntimeVersionOlderThanOrEqual(DexVm.Version vm) {
+ return isDexRuntime() && getDexRuntimeVersion().isOlderThanOrEqual(vm);
+ }
+
public boolean isNoneRuntime() {
return runtime == NoneRuntime.getInstance();
}
diff --git a/src/test/java/com/android/tools/r8/TestRuntime.java b/src/test/java/com/android/tools/r8/TestRuntime.java
index 0d937dd..4f95deb 100644
--- a/src/test/java/com/android/tools/r8/TestRuntime.java
+++ b/src/test/java/com/android/tools/r8/TestRuntime.java
@@ -41,6 +41,7 @@
JDK16("jdk16", 60),
JDK17("jdk17", 61),
JDK18("jdk18", 62),
+ JDK20("jdk20", 64),
;
/** This should generally be the latest checked in CF runtime we fully support. */
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 704ce16..d9e3f91 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -520,6 +520,10 @@
return addTestingAnnotation(NoHorizontalClassMerging.class);
}
+ public final T addNoInliningOfDefaultInitializerAnnotation() {
+ return addTestingAnnotation(NoInliningOfDefaultInitializer.class);
+ }
+
public final T addNoMethodStaticizingAnnotation() {
return addTestingAnnotation(NoMethodStaticizing.class);
}
@@ -532,6 +536,10 @@
return addTestingAnnotation(NoParameterTypeStrengthening.class);
}
+ public final T addNoRedundantFieldLoadEliminationAnnotation() {
+ return addTestingAnnotation(NoRedundantFieldLoadElimination.class);
+ }
+
public final T addNoReturnTypeStrengtheningAnnotation() {
return addTestingAnnotation(NoReturnTypeStrengthening.class);
}
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java
index 7ac3869..bc066b0 100644
--- a/src/test/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -1434,22 +1434,6 @@
return compatSink.build();
}
- public static void runD8WithoutResult(
- D8Command command, Consumer<InternalOptions> optionsConsumer)
- throws CompilationFailedException {
- InternalOptions internalOptions = command.getInternalOptions();
- optionsConsumer.accept(internalOptions);
- D8.runForTesting(command.getInputApp(), internalOptions);
- }
-
- public static List<String> runGenerateMainDexList(
- GenerateMainDexListCommand command, Consumer<InternalOptions> optionsConsumer)
- throws CompilationFailedException {
- InternalOptions internalOptions = command.getInternalOptions();
- optionsConsumer.accept(internalOptions);
- return GenerateMainDexList.runForTesting(command.getInputApp(), internalOptions);
- }
-
@Deprecated
public static ProcessResult runJava(Class clazz) throws Exception {
String main = clazz.getTypeName();
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/EffectiveFinalFieldMarkedFinalTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/EffectiveFinalFieldMarkedFinalTest.java
index 83d80c8..e08fd3e 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/EffectiveFinalFieldMarkedFinalTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/EffectiveFinalFieldMarkedFinalTest.java
@@ -10,6 +10,7 @@
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.NoRedundantFieldLoadElimination;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
@@ -42,6 +43,7 @@
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.allowAccessModification(allowAccessModification)
+ .enableNoRedundantFieldLoadEliminationAnnotations()
.setMinApi(parameters)
.compile()
.inspect(
@@ -50,7 +52,12 @@
assertThat(mainClassSubject, isPresent());
assertThat(
mainClassSubject.uniqueFieldWithOriginalName("instanceField"),
- allOf(isPresent(), onlyIf(allowAccessModification, isFinal())));
+ allOf(
+ isPresent(),
+ onlyIf(
+ allowAccessModification
+ && !parameters.canInitNewInstanceUsingSuperclassConstructor(),
+ isFinal())));
assertThat(
mainClassSubject.uniqueFieldWithOriginalName("staticField"),
allOf(isPresent(), onlyIf(allowAccessModification, isFinal())));
@@ -63,6 +70,7 @@
static String staticField = System.currentTimeMillis() > 0 ? "Hello" : null;
+ @NoRedundantFieldLoadElimination
String instanceField = System.currentTimeMillis() > 0 ? ", world!" : null;
public static void main(String[] args) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
index 9ce5fd6..cda9c15 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.apimodel;
+import static com.android.tools.r8.androidapi.AndroidApiLevelDatabaseHelper.notModeledTypes;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -33,7 +34,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.junit.Test;
@@ -133,12 +133,7 @@
private static void ensureAllPublicMethodsAreMapped(
AppView<AppInfoWithClassHierarchy> appView, AndroidApiLevelCompute apiLevelCompute) {
- Set<String> notModeledTypes = new HashSet<>();
- notModeledTypes.add("androidx.annotation.RecentlyNullable");
- notModeledTypes.add("androidx.annotation.RecentlyNonNull");
- notModeledTypes.add("android.annotation.Nullable");
- notModeledTypes.add("android.annotation.NonNull");
- DexItemFactory factory = appView.dexItemFactory();
+ Set<String> notModeledTypes = notModeledTypes();
for (DexLibraryClass clazz : appView.app().asDirect().libraryClasses()) {
if (notModeledTypes.contains(clazz.getClassReference().getTypeName())) {
continue;
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelAndroidxApiImplTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelAndroidxApiImplTest.java
index 0dd154d..d52052b 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelAndroidxApiImplTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelAndroidxApiImplTest.java
@@ -63,7 +63,7 @@
.setMinApi(parameters)
.addAndroidBuildVersion(getMaxSupportedApiLevel())
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(setMockApiLevelForClass(LibraryClass23.class, AndroidApiLevel.M))
.apply(setMockApiLevelForClass(LibraryClass26.class, AndroidApiLevel.O))
.apply(setMockApiLevelForClass(LibraryClass30.class, AndroidApiLevel.R))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingPackagePrivateTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingPackagePrivateTest.java
index 9ef5912..5e1dc96 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingPackagePrivateTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelClassMergingPackagePrivateTest.java
@@ -68,6 +68,7 @@
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters)
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses)
.apply(b -> setApiLevels(b, Api1.class))
.apply(b -> setApiLevels(b, Api2.class));
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java
index 2332313..024d17a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java
@@ -73,7 +73,7 @@
LibraryClassThree.class.getDeclaredMethod("baz"), mockApiLevelThree))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses);
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck);
}
private boolean willHorizontallyMergeOutlines() {
@@ -82,7 +82,8 @@
}
private boolean willStubLibraryClassThree() {
- return parameters.getApiLevel().isLessThan(mockApiLevelThree);
+ return parameters.getApiLevel().isGreaterThan(AndroidApiLevel.L)
+ && parameters.getApiLevel().isLessThan(mockApiLevelThree);
}
public AndroidApiLevel getApiLevelForRuntime() {
@@ -222,7 +223,9 @@
// classes. Depending on the api a number of synthetic classes.
int numberOfClasses =
4
- + (willStubLibraryClassThree() ? 2 : 0)
+ + (willStubLibraryClassThree()
+ ? 2
+ : (BooleanUtils.intValue(parameters.getApiLevel().isLessThan(mockApiLevelThree))))
+ BooleanUtils.intValue(parameters.getApiLevel().isLessThan(mockApiLevelTwo))
+ BooleanUtils.intValue(parameters.getApiLevel().isLessThan(mockApiLevelOne));
assertEquals(numberOfClasses, inspector.allClasses().size());
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldAssignNewInstanceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldAssignNewInstanceTest.java
index f2a6859..4145d38 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldAssignNewInstanceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelFieldAssignNewInstanceTest.java
@@ -56,7 +56,7 @@
.setMinApi(parameters)
.addAndroidBuildVersion(getMaxSupportedApiLevel())
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(setMockApiLevelForClass(LibraryClass.class, AndroidApiLevel.B))
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, AndroidApiLevel.B))
.apply(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithDifferentApiLevelTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithDifferentApiLevelTest.java
index c84fae1..10b38c6 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithDifferentApiLevelTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithDifferentApiLevelTest.java
@@ -60,7 +60,7 @@
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters)
.addAndroidBuildVersion(parameters.getApiLevel())
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, classApiLevel))
.apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
.apply(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithSameApiLevelTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithSameApiLevelTest.java
index 6f03205..d01a450 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithSameApiLevelTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelIndirectTargetWithSameApiLevelTest.java
@@ -57,7 +57,7 @@
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters)
.addAndroidBuildVersion(parameters.getApiLevel())
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockApiLevel))
.apply(setMockApiLevelForClass(LibraryClass.class, mockApiLevel))
.apply(setMockApiLevelForMethod(LibraryClass.class.getDeclaredMethod("foo"), mockApiLevel))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMissingSuperTypeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMissingSuperTypeTest.java
index 67f9f6e..1a0197c 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMissingSuperTypeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelInlineMissingSuperTypeTest.java
@@ -47,6 +47,7 @@
.apply(
setMockApiLevelForDefaultInstanceInitializer(
LibraryClass.class, libraryAdditionApiLevel))
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses)
.setMinApi(parameters)
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockAbstractMethodOnBaseToOutlineTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockAbstractMethodOnBaseToOutlineTest.java
index bc50b66..6e942c1 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockAbstractMethodOnBaseToOutlineTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockAbstractMethodOnBaseToOutlineTest.java
@@ -54,7 +54,7 @@
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters)
.addAndroidBuildVersion()
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
.apply(setMockApiLevelForClass(LibraryClass.class, AndroidApiLevel.B))
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, AndroidApiLevel.B))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassCheckCastTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassCheckCastTest.java
index 0a9cc42..0b6b937 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassCheckCastTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassCheckCastTest.java
@@ -48,7 +48,7 @@
.addLibraryClasses(LibraryClass.class)
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(setMockApiLevelForClass(LibraryClass.class, mockLevel));
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingByClassForNameTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingByClassForNameTest.java
index dccfdbf..6d33017 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingByClassForNameTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingByClassForNameTest.java
@@ -48,7 +48,7 @@
.addLibraryClasses(LibraryClass.class)
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(setMockApiLevelForClass(LibraryClass.class, mockLevel));
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingByClassReferenceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingByClassReferenceTest.java
index 4c90ac5..e19d0e6 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingByClassReferenceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassLoadingByClassReferenceTest.java
@@ -48,7 +48,7 @@
.addLibraryClasses(LibraryClass.class)
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(setMockApiLevelForClass(LibraryClass.class, mockLevel));
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
index 9fc0985..6185e26 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockClassTest.java
@@ -52,7 +52,7 @@
.addLibraryClasses(LibraryClass.class)
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(ApiModelingTestHelper::disableOutlining)
.apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel));
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockDalvikVerifyErrorTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockDalvikVerifyErrorTest.java
index 74e7ef0..5931ef7 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockDalvikVerifyErrorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockDalvikVerifyErrorTest.java
@@ -50,7 +50,7 @@
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters)
.addAndroidBuildVersion()
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel))
.apply(setMockApiLevelForMethod(LibraryClass.class.getDeclaredMethod("foo"), mockLevel));
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionTest.java
index ea43cbb..fff6c4a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockExceptionTest.java
@@ -59,7 +59,7 @@
.addLibraryClasses(LibrarySuperException.class, LibrarySubException.class, Thrower.class)
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(setMockApiLevelForClass(LibrarySuperException.class, mockSuperExceptionLevel))
.apply(setMockApiLevelForClass(LibrarySubException.class, mockSubExceptionLevel));
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
index c7a2674..739600d 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
@@ -47,7 +47,7 @@
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters)
.addAndroidBuildVersion()
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel));
}
@@ -97,7 +97,8 @@
}
private void inspect(CodeInspector inspector) {
- verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel);
+ verifyThat(inspector, parameters, LibraryClass.class)
+ .stubbedBetween(AndroidApiLevel.L_MR1, mockLevel);
}
private void checkOutput(SingleTestRunResult<?> runResult) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java
index 8e974f1..bb28672 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeProgramDefinedDuplicateTest.java
@@ -56,7 +56,7 @@
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters)
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(ApiModelingTestHelper::disableOutlining)
.apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeTest.java
index ab6e2148..15ffd25 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockMergeTest.java
@@ -56,7 +56,7 @@
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters)
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(ApiModelingTestHelper::disableOutlining)
.apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, mockLevel))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockRetraceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockRetraceTest.java
index 9394280..8512c5a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockRetraceTest.java
@@ -48,7 +48,7 @@
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters)
.addAndroidBuildVersion()
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
.addKeepMainRule(Main.class)
.addKeepClassRules(ProgramClass.class)
@@ -60,10 +60,15 @@
}
private void inspect(CodeInspector inspector) {
- verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel);
+ verifyThat(inspector, parameters, LibraryClass.class)
+ .stubbedBetween(AndroidApiLevel.L_MR1, mockLevel);
}
private void checkOutput(SingleTestRunResult<?> runResult) {
+ if (!addToBootClasspath()) {
+ runResult.assertFailureWithErrorThatThrows(NoClassDefFoundError.class);
+ return;
+ }
StackTraceLine clinitFrame =
StackTraceLine.builder()
.setClassName(typeName(LibraryClass.class))
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
index 3d31170..9ef1d91 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
@@ -49,7 +49,7 @@
.addDefaultRuntimeLibrary(parameters)
.setMinApi(parameters)
.addAndroidBuildVersion()
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(setMockApiLevelForClass(LibraryClass.class, lowerMockApiLevel))
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, lowerMockApiLevel))
.apply(setMockApiLevelForClass(OtherLibraryClass.class, mockApiLevel))
@@ -121,9 +121,12 @@
}
private void inspect(CodeInspector inspector) {
- verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(lowerMockApiLevel);
- verifyThat(inspector, parameters, LibraryInterface.class).stubbedUntil(lowerMockApiLevel);
- verifyThat(inspector, parameters, OtherLibraryClass.class).stubbedUntil(mockApiLevel);
+ verifyThat(inspector, parameters, LibraryClass.class)
+ .stubbedBetween(AndroidApiLevel.L_MR1, lowerMockApiLevel);
+ verifyThat(inspector, parameters, LibraryInterface.class)
+ .stubbedBetween(AndroidApiLevel.L_MR1, lowerMockApiLevel);
+ verifyThat(inspector, parameters, OtherLibraryClass.class)
+ .stubbedBetween(AndroidApiLevel.L_MR1, mockApiLevel);
}
private void checkOutput(SingleTestRunResult<?> runResult) {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
index 57e692a..1749728 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
@@ -54,7 +54,7 @@
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, libraryApiLevel))
.apply(setMockApiLevelForMethod(methodOn23, libraryApiLevel))
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses);
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck);
}
private boolean addToBootClasspath() {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
index aa584e4..dff5d14 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingSubReferenceApiTest.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
import static com.android.tools.r8.utils.AndroidApiLevel.L_MR1;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
import static org.hamcrest.CoreMatchers.not;
@@ -13,6 +14,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoInliningOfDefaultInitializer;
import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -55,6 +57,7 @@
.apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoInliningOfDefaultInitializerAnnotations()
.enableNoMethodStaticizingAnnotations()
.addVerticallyMergedClassesInspector(
inspector -> {
@@ -75,7 +78,9 @@
assertThat(base, not(isPresent()));
ClassSubject sub = inspector.clazz(Sub.class);
assertThat(sub, isPresent());
- assertThat(sub.uniqueInstanceInitializer(), isPresent());
+ assertThat(
+ sub.uniqueInstanceInitializer(),
+ isAbsentIf(parameters.canHaveNonReboundConstructorInvoke()));
assertEquals(1, sub.virtualMethods().size());
FoundMethodSubject callCallApi = sub.virtualMethods().get(0);
assertEquals("callCallApi", callCallApi.getOriginalName());
@@ -104,6 +109,7 @@
}
@NeverClassInline
+ @NoInliningOfDefaultInitializer
public static class Sub extends Base {
@NeverInline
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
index 3933284..2e368e9 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoVerticalMergingTest.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
import static com.android.tools.r8.utils.AndroidApiLevel.L_MR1;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static junit.framework.TestCase.assertEquals;
import static org.hamcrest.CoreMatchers.not;
@@ -13,6 +14,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoInliningOfDefaultInitializer;
import com.android.tools.r8.NoMethodStaticizing;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -55,6 +57,7 @@
.apply(ApiModelingTestHelper::disableOutliningAndStubbing)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoInliningOfDefaultInitializerAnnotations()
.enableNoMethodStaticizingAnnotations()
.addVerticallyMergedClassesInspector(
inspector -> {
@@ -74,7 +77,9 @@
assertThat(base, not(isPresent()));
ClassSubject sub = inspector.clazz(Sub.class);
assertThat(sub, isPresent());
- assertThat(sub.uniqueInstanceInitializer(), isPresent());
+ assertThat(
+ sub.uniqueInstanceInitializer(),
+ isAbsentIf(parameters.canHaveNonReboundConstructorInvoke()));
assertEquals(1, sub.virtualMethods().size());
FoundMethodSubject callCallApi = sub.virtualMethods().get(0);
assertEquals("callCallApi", callCallApi.getOriginalName());
@@ -104,6 +109,7 @@
}
@NeverClassInline
+ @NoInliningOfDefaultInitializer
public static class Sub extends Base {
@NeverInline
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineCheckCastTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineCheckCastTest.java
index 86630ca..4c6763e 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineCheckCastTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineCheckCastTest.java
@@ -53,7 +53,7 @@
.apply(setMockApiLevelForMethod(LibraryClass.class.getDeclaredMethod("foo"), classApiLevel))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses);
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck);
}
public AndroidApiLevel getApiLevelForRuntime() {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineConstClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineConstClassTest.java
index 371531b..7c18193 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineConstClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineConstClassTest.java
@@ -46,7 +46,7 @@
.apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses);
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck);
}
public AndroidApiLevel getApiLevelForRuntime() {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceOfTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceOfTest.java
index a4e7c2e..d6d7137 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceOfTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineInstanceOfTest.java
@@ -52,7 +52,7 @@
.apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses);
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck);
}
public AndroidApiLevel getApiLevelForRuntime() {
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
index 4663607..8fd5ba1 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodAndStubClassTest.java
@@ -54,7 +54,7 @@
.addAndroidBuildVersion()
// TODO(b/213552119): Remove when enabled by default.
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
// We only model the class and not the default initializer, otherwise we outline the new
// instance call and remove the last reference in non-outlined code.
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java
index 39b1db2..4883f17 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelOutlineMethodProtectedTest.java
@@ -72,8 +72,8 @@
.transform())
.setMinApi(AndroidApiLevel.B)
.addAndroidBuildVersion(runApiLevel())
- // TODO(b/213552119): Remove when enabled by default.
.apply(ApiModelingTestHelper::enableApiCallerIdentification)
+ .apply(ApiModelingTestHelper::disableStubbingOfClasses)
.apply(setMockApiLevelForClass(LibraryClass.class, classApiLevel))
.apply(setMockApiLevelForDefaultInstanceInitializer(LibraryClass.class, classApiLevel))
.apply(
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
index cfa2e8a..f6a698a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
@@ -134,6 +134,18 @@
});
}
+ public static void enableStubbingOfClassesAndDisableGlobalSyntheticCheck(
+ TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
+ compilerBuilder.addOptionsModification(
+ options -> {
+ options.apiModelingOptions().enableLibraryApiModeling = true;
+ options.apiModelingOptions().enableStubbingOfClasses = true;
+ // Our tests rely on us amending the library path with additional classes that are not
+ // in the library.
+ options.testing.globalSyntheticCreatedCallback = null;
+ });
+ }
+
public static void enableStubbingOfClasses(TestCompilerBuilder<?, ?, ?, ?, ?> compilerBuilder) {
compilerBuilder.addOptionsModification(
options -> {
@@ -277,6 +289,16 @@
|| parameters.getApiLevel().isGreaterThanOrEqualTo(finalApiLevel)));
}
+ public void stubbedBetween(AndroidApiLevel startingApilevel, AndroidApiLevel endingApiLevel) {
+ assertThat(
+ inspector.clazz(classOfInterest),
+ notIf(
+ isPresent(),
+ parameters.isCfRuntime()
+ || parameters.getApiLevel().isLessThan(startingApilevel)
+ || parameters.getApiLevel().isGreaterThanOrEqualTo(endingApiLevel)));
+ }
+
void hasCheckCastOutlinedFromUntil(Method method, AndroidApiLevel apiLevel) {
if (parameters.isDexRuntime() && parameters.getApiLevel().isLessThan(apiLevel)) {
hasCheckCastOutlinedFrom(method);
diff --git a/src/test/java/com/android/tools/r8/classmerging/StatefulSingletonClassesMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/StatefulSingletonClassesMergingTest.java
index 4b17a16..3604880 100644
--- a/src/test/java/com/android/tools/r8/classmerging/StatefulSingletonClassesMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/StatefulSingletonClassesMergingTest.java
@@ -34,8 +34,16 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
+ // TODO(b/280384153): A and B should always be merged in the final round of tree shaking
+ // since their class initializers are postponeable, but we fail to conclude so with
+ // constructor inlining enabled.
.addHorizontallyMergedClassesInspector(
- inspector -> inspector.assertIsCompleteMergeGroup(A.class, B.class))
+ inspector ->
+ inspector
+ .applyIf(
+ !parameters.canInitNewInstanceUsingSuperclassConstructor(),
+ i -> i.assertIsCompleteMergeGroup(A.class, B.class))
+ .assertNoOtherClassesMerged())
.enableConstantArgumentAnnotations()
.enableInliningAnnotations()
.enableMemberValuePropagationAnnotations()
@@ -77,7 +85,7 @@
static final B INSTANCE = new B("B");
- @NeverPropagateValue private final String data;
+ @NeverPropagateValue private String data;
// TODO(b/198758663): With argument propagation the constructors end up not being equivalent,
// which prevents merging in the final round of horizontal class merging.
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithFeatureSplitTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithFeatureSplitTest.java
index b5f0cb1..2093d70 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithFeatureSplitTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithFeatureSplitTest.java
@@ -9,6 +9,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -40,6 +41,7 @@
.addFeatureSplit(Feature2Class.class, Feature2Main.class)
.addKeepFeatureMainRule(Feature1Main.class)
.addKeepFeatureMainRule(Feature2Main.class)
+ .enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters)
.compile()
@@ -100,6 +102,7 @@
@NeverClassInline
public static class Feature2Class {
+ @NeverInline
public Feature2Class() {
System.out.println("feature 2");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
index 52b7cbe..b09d358 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithNativeMethodsTest.java
@@ -45,6 +45,7 @@
@NeverClassInline
public static class A {
+ @NeverInline
public A() {
System.out.println("a");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/CompatKeepConstructorLiveTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/CompatKeepConstructorLiveTest.java
index e605671..a4634f4 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/CompatKeepConstructorLiveTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/CompatKeepConstructorLiveTest.java
@@ -8,6 +8,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -23,6 +24,7 @@
testForR8Compat(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters)
@@ -47,6 +49,7 @@
@NeverClassInline
@NoHorizontalClassMerging
public static class B {
+ @NeverInline
public B(String v) {
System.out.println("b: " + v);
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
index 90a19bc..a8a0ec2 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingPreoptimizedTest.java
@@ -34,6 +34,8 @@
.addKeepMainRule(Main.class)
.addHorizontallyMergedClassesInspector(
inspector -> inspector.assertMergedInto(B.class, A.class))
+ .addOptionsModification(
+ options -> options.inlinerOptions().setEnableConstructorInlining(false))
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EmptyClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EmptyClassTest.java
index e481a0d..66bdabb 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/EmptyClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EmptyClassTest.java
@@ -6,6 +6,7 @@
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.notIf;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
@@ -30,7 +31,9 @@
.assertSuccessWithOutputLines("a", "b: foo")
.inspect(
codeInspector -> {
- assertThat(codeInspector.clazz(A.class), isPresent());
+ assertThat(
+ codeInspector.clazz(A.class),
+ notIf(isPresent(), parameters.canInitNewInstanceUsingSuperclassConstructor()));
assertThat(codeInspector.clazz(B.class), isAbsent());
});
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithPrimitiveAndReferencesParametersTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithPrimitiveAndReferencesParametersTest.java
index f751f84..613cc97 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithPrimitiveAndReferencesParametersTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/EquivalentConstructorsWithPrimitiveAndReferencesParametersTest.java
@@ -53,7 +53,8 @@
ClassSubject aClassSubject = inspector.clazz(A.class);
assertThat(aClassSubject, isPresent());
assertEquals(
- 2, aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
+ parameters.canInitNewInstanceUsingSuperclassConstructor() ? 0 : 2,
+ aClassSubject.allMethods(FoundMethodSubject::isInstanceInitializer).size());
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("A", "B");
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java
index 4283b1a..6558cd4 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingTest.java
@@ -81,6 +81,7 @@
Object field;
+ @NeverInline
A() {
System.out.println("Ouch!");
}
@@ -103,6 +104,7 @@
Object field;
// Removed by constructor shrinking.
+ @NeverInline
B() {}
@NeverInline
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingWithRepackagingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingWithRepackagingTest.java
index e705e0c..c453a01 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingWithRepackagingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingAfterConstructorShrinkingWithRepackagingTest.java
@@ -138,6 +138,7 @@
Object field;
@KeepConstantArguments
+ @NeverInline
public A(Parent parent) {
super(parent);
System.out.println("Ouch!");
@@ -162,6 +163,7 @@
// Removed by constructor shrinking.
@KeepConstantArguments
+ @NeverInline
public B(Parent parent) {
super(parent);
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingOfInitArgumentTypesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingOfInitArgumentTypesTest.java
index 6694cdb..66f392b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingOfInitArgumentTypesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/HorizontalClassMergingOfInitArgumentTypesTest.java
@@ -8,6 +8,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -45,6 +46,7 @@
options.callSiteOptimizationOptions().setForceSyntheticsForInstanceInitializers(true);
options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging);
})
+ .enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters)
.compile()
@@ -85,9 +87,11 @@
boolean unused;
+ @NeverInline
A() {}
// Unused argument removal will rewrite this into A(A$$ExternalSynthetic$IA0)
+ @NeverInline
A(Object unused) {
this.unused = true;
}
@@ -103,9 +107,11 @@
boolean unused;
+ @NeverInline
B() {}
// Unused argument removal will rewrite this into B(B$$ExternalSynthetic$IA0)
+ @NeverInline
B(Object unused) {
this.unused = true;
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InnerOuterClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InnerOuterClassesTest.java
index 8617415..c67f07e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InnerOuterClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InnerOuterClassesTest.java
@@ -10,7 +10,9 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.utils.BooleanUtils;
+import com.google.common.collect.ImmutableSet;
import java.util.List;
import org.junit.Test;
import org.junit.runners.Parameterized;
@@ -37,6 +39,8 @@
.addKeepMainRule(Main.class)
.enableNeverClassInliningAnnotations()
.addKeepAttributeInnerClassesAndEnclosingMethod()
+ .addOptionsModification(
+ options -> options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE))
.setMinApi(parameters)
.addOptionsModification(
options ->
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InstantiatedAndUninstantiatedClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InstantiatedAndUninstantiatedClassMergingTest.java
index 6ed6c30..ac11021 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InstantiatedAndUninstantiatedClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InstantiatedAndUninstantiatedClassMergingTest.java
@@ -60,6 +60,7 @@
@NeverClassInline
public static final class Instantiated {
+ @NeverInline
Instantiated() {
System.out.println("Instantiated");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/LargeConstructorsMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/LargeConstructorsMergingTest.java
index 68bc6a8..5405ddb 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/LargeConstructorsMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/LargeConstructorsMergingTest.java
@@ -11,7 +11,9 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.google.common.collect.ImmutableSet;
import org.junit.Test;
public class LargeConstructorsMergingTest extends HorizontalClassMergingTestBase {
@@ -24,7 +26,11 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
- .addOptionsModification(options -> options.testing.verificationSizeLimitInBytesOverride = 4)
+ .addOptionsModification(
+ options -> {
+ options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE);
+ options.testing.verificationSizeLimitInBytesOverride = 4;
+ })
.enableNeverClassInliningAnnotations()
.setMinApi(parameters)
.addHorizontallyMergedClassesInspector(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoHorizontalClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoHorizontalClassMergingTest.java
index 5074df0..82517ce 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoHorizontalClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoHorizontalClassMergingTest.java
@@ -8,6 +8,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestParameters;
import org.junit.Test;
@@ -22,6 +23,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters)
@@ -36,6 +38,7 @@
@NeverClassInline
public static class A {
+ @NeverInline
public A() {
System.out.println("a");
}
@@ -44,6 +47,7 @@
@NeverClassInline
@NoHorizontalClassMerging
public static class B {
+ @NeverInline
public B(String v) {
System.out.println(v);
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java
index 2588a63..def0d9e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PackagePrivateMemberAccessTest.java
@@ -12,6 +12,8 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.classmerging.horizontal.testclasses.A;
import com.android.tools.r8.classmerging.horizontal.testclasses.B;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
+import com.google.common.collect.ImmutableSet;
import org.junit.Test;
public class PackagePrivateMemberAccessTest extends HorizontalClassMergingTestBase {
@@ -26,6 +28,8 @@
.addInnerClasses(getClass())
.addProgramClasses(A.class, B.class)
.addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE))
.enableConstantArgumentAnnotations()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
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 6dea00f..0d09fd4 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
@@ -8,6 +8,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.classmerging.horizontal.testclasses.C;
import com.android.tools.r8.classmerging.horizontal.testclasses.D;
@@ -42,6 +43,7 @@
@NeverClassInline
public static class E {
+ @NeverInline
public E(int v) {
System.out.println(v);
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberTest.java
index bf43463..005c3d4 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberTest.java
@@ -8,6 +8,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
import org.junit.Test;
@@ -22,6 +23,7 @@
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.addKeepRules("-keepclassmembers class " + B.class.getTypeName() + " { void foo(); }")
+ .enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters)
.run(parameters.getRuntime(), Main.class)
@@ -35,6 +37,7 @@
@NeverClassInline
public static class A {
+ @NeverInline
public A() {
System.out.println("a");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedClassesTest.java
index 3798008..6e898e9 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedClassesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/SynchronizedClassesTest.java
@@ -12,7 +12,9 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ir.optimize.Inliner.Reason;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.google.common.collect.ImmutableSet;
import org.junit.Test;
public class SynchronizedClassesTest extends HorizontalClassMergingTestBase {
@@ -31,6 +33,8 @@
.assertMergedInto(C.class, A.class)
.assertMergedInto(D.class, B.class)
.assertNoOtherClassesMerged())
+ .addOptionsModification(
+ options -> options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE))
.enableConstantArgumentAnnotations()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerConstructorCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerConstructorCollisionTest.java
index 30b7679..79b5725 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerConstructorCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerConstructorCollisionTest.java
@@ -24,6 +24,11 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector.assertIsCompleteMergeGroup(A.class, B.class).assertNoOtherClassesMerged())
+ .addOptionsModification(
+ options -> options.inlinerOptions().setEnableConstructorInlining(false))
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassTest.java
index be027c7..db1c530 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassTest.java
@@ -54,6 +54,8 @@
@NeverClassInline
public static class C {
+
+ @NeverInline
public C() {
System.out.println("c");
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/ForceInlineConstructorWithRetargetedLibMemberTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/ForceInlineConstructorWithRetargetedLibMemberTest.java
index 1730dc8..4c11361 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/ForceInlineConstructorWithRetargetedLibMemberTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/ForceInlineConstructorWithRetargetedLibMemberTest.java
@@ -11,6 +11,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase;
import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
@@ -53,6 +54,7 @@
.addInnerClasses(getClass())
.addKeepMainRule(TestClass.class)
.enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
.addVerticallyMergedClassesInspector(
inspector -> inspector.assertMergedIntoSubtype(A.class))
.compile()
@@ -80,6 +82,7 @@
@NeverClassInline
static class B extends A {
+ @NeverInline
B(String[] args) {
super(args);
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
index 5264b18..b6c66b6 100644
--- a/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/vertical/VerticalClassMergerTest.java
@@ -256,6 +256,7 @@
Path[] programFiles =
new Path[] {
CF_DIR.resolve("NeverPropagateValue.class"),
+ CF_DIR.resolve("NoRedundantFieldLoadElimination.class"),
CF_DIR.resolve("ConflictInGeneratedNameTest.class"),
CF_DIR.resolve("ConflictInGeneratedNameTest$A.class"),
CF_DIR.resolve("ConflictInGeneratedNameTest$B.class")
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
index c59fda4..9535f30 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -62,10 +62,11 @@
ProguardKeepRuleDiagnosticsApiTest.ApiTest.class,
SyntheticContextsConsumerTest.ApiTest.class,
ExtractMarkerApiTest.ApiTest.class,
- PartitionMapCommandTest.ApiTest.class);
+ PartitionMapCommandTest.ApiTest.class,
+ CancelCompilationCheckerTest.ApiTest.class);
private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
- ImmutableList.of(CancelCompilationCheckerTest.ApiTest.class);
+ ImmutableList.of();
private final TemporaryFolder temp;
diff --git a/src/test/java/com/android/tools/r8/d8/WriteToArchiveTest.java b/src/test/java/com/android/tools/r8/d8/WriteToArchiveTest.java
deleted file mode 100644
index 6df4c3d..0000000
--- a/src/test/java/com/android/tools/r8/d8/WriteToArchiveTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.d8;
-
-import com.android.tools.r8.D8;
-import com.android.tools.r8.D8Command;
-import com.android.tools.r8.OutputMode;
-import com.android.tools.r8.ToolHelper;
-import java.nio.file.Paths;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-// Test that we allow writing to zip and jar archives.
-public class WriteToArchiveTest {
-
- private static final String input = ToolHelper.EXAMPLES_BUILD_DIR + "/trivial.jar";
-
- @Rule public TemporaryFolder zipFolder = ToolHelper.getTemporaryFolderForTest();
- @Rule public TemporaryFolder jarFolder = ToolHelper.getTemporaryFolderForTest();
-
- @Test
- public void writeToZip() throws Exception {
- D8Command command =
- D8Command.builder()
- .addProgramFiles(Paths.get(input))
- .setOutput(
- Paths.get(zipFolder.getRoot().toString() + "/output.zip"), OutputMode.DexIndexed)
- .build();
- D8.run(command);
- }
-
- @Test
- public void writeToJar() throws Exception {
- D8Command command =
- D8Command.builder()
- .addProgramFiles(Paths.get(input))
- .setOutput(
- Paths.get(jarFolder.getRoot().toString() + "/output.jar"), OutputMode.DexIndexed)
- .build();
- D8.run(command);
- }
-}
diff --git a/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java b/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java
index 88c7358..46a83b7 100644
--- a/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java
+++ b/src/test/java/com/android/tools/r8/debug/ExamplesDebugTest.java
@@ -60,131 +60,6 @@
}
@Test
- public void testRegAlloc() throws Exception {
- testDebugging("regalloc", "RegAlloc");
- }
-
- @Test
- public void testReturns() throws Exception {
- testDebugging("returns", "Returns");
- }
-
- @Test
- public void testStaticField() throws Exception {
- // TODO(b/79671093): D8 has different line number info during stepping.
- testDebuggingJvmOnly("staticfield", "StaticField");
- }
-
- @Test
- public void testStringBuilding() throws Exception {
- testDebugging("stringbuilding", "StringBuilding");
- }
-
- @Test
- public void testSwitches() throws Exception {
- testDebugging("switches", "Switches");
- }
-
- @Test
- public void testSync() throws Exception {
- // TODO(b/79671093): Line number mismatch in D8.
- testDebuggingJvmOnly("sync", "Sync");
- }
-
- @Test
- public void testThrowing() throws Exception {
- // TODO(b/79671093): D8 has unexpected variables (this in throwing.c <init>).
- testDebuggingJvmOnly("throwing", "Throwing");
- }
-
- @Test
- public void testTrivial() throws Exception {
- testDebugging("trivial", "Trivial");
- }
-
- @Test
- public void testTryCatch() throws Exception {
- testDebugging("trycatch", "TryCatch");
- }
-
- @Test
- public void testNestedTryCatches() throws Exception {
- testDebugging("nestedtrycatches", "NestedTryCatches");
- }
-
- @Test
- public void testTryCatchMany() throws Exception {
- testDebugging("trycatchmany", "TryCatchMany");
- }
-
- @Test
- public void testRegress() throws Exception {
- testDebugging("regress", "Regress");
- }
-
- @Test
- public void testRegress2() throws Exception {
- testDebugging("regress2", "Regress2");
- }
-
- @Test
- public void testRegress37726195() throws Exception {
- testDebugging("regress_37726195", "Regress");
- }
-
- @Test
- public void testRegress37658666() throws Exception {
- testDebugging("regress_37658666", "Regress");
- }
-
- @Test
- public void testRegress37875803() throws Exception {
- testDebugging("regress_37875803", "Regress");
- }
-
- @Test
- public void testRegress37955340() throws Exception {
- testDebugging("regress_37955340", "Regress");
- }
-
- @Test
- public void testRegress62300145() throws Exception {
- // TODO(b/67936230): Executes differently for Java 8 and 9, so don't compare to DEX output.
- testDebuggingJvmOnly("regress_62300145", "Regress");
- }
-
- @Test
- public void testRegress64881691() throws Exception {
- testDebugging("regress_64881691", "Regress");
- }
-
- @Test
- public void testRegress65104300() throws Exception {
- testDebugging("regress_65104300", "Regress");
- }
-
- @Ignore("TODO(b/79671093): This test seems to take forever")
- @Test
- public void testRegress70703087() throws Exception {
- testDebugging("regress_70703087", "Test");
- }
-
- @Test
- public void testRegress70736958() throws Exception {
- testDebugging("regress_70736958", "Test");
- }
-
- @Test
- public void testRegress70737019() throws Exception {
- testDebugging("regress_70737019", "Test");
- }
-
- @Test
- public void testRegress72361252() throws Exception {
- testDebugging("regress_72361252", "Test");
- }
-
- @Test
public void testMemberrebinding2() throws Exception {
testDebugging("memberrebinding2", "Memberrebinding");
}
@@ -199,23 +74,6 @@
testDebugging("minification", "Minification");
}
- @Test
- public void testEnclosingmethod() throws Exception {
- testDebugging("enclosingmethod", "Main");
- }
-
- @Test
- public void testEnclosingmethod_proguarded() throws Exception {
- // TODO(b/79671093): We don't match JVM's behavior on this example.
- testDebuggingJvmOutputOnly("enclosingmethod_proguarded", "Main");
- }
-
- @Test
- public void testSwitchmaps() throws Exception {
- // TODO(b/79671093): D8 has different line number info during stepping.
- testDebuggingJvmOnly("switchmaps", "Switches");
- }
-
private void testDebugging(String pkg, String clazz) throws Exception {
init(pkg, clazz)
.add("Input", input())
@@ -226,19 +84,6 @@
.compare();
}
- private void testDebuggingJvmOnly(String pkg, String clazz) throws Exception {
- init(pkg, clazz)
- .add("Input", input())
- .add("R8/CfSourceCode", r8cf())
- .compare();
- }
-
- private void testDebuggingJvmOutputOnly(String pkg, String clazz) throws Exception {
- init(pkg, clazz)
- .add("R8/CfSourceCode", r8cf())
- .run();
- }
-
private DebugStreamComparator init(String pkg, String clazz) {
// TODO(b/199700280): Reenable on 12.0.0 when we have the libjdwp.so file include and the flags
// fixed.
diff --git a/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564Test.java b/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564Test.java
index bb58476..c7d877e 100644
--- a/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564Test.java
+++ b/src/test/java/com/android/tools/r8/desugar/jdk8272564/Jdk8272564Test.java
@@ -27,8 +27,8 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- // TODO(b/218293990): Right now the JDK 18 tests are built with -target 17, as our Gradle
- // version does not know of -target 18.
+ // TODO(b/218293990): Right now the JDK 20 tests are built with -target 17, as our Gradle
+ // version does not know of -target 20.
return getTestParameters()
.withCfRuntimesStartingFromIncluding(CfVm.JDK17)
.withDexRuntimes()
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordBlogTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordBlogTest.java
new file mode 100644
index 0000000..bcc90c5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/records/RecordBlogTest.java
@@ -0,0 +1,136 @@
+// Copyright (c) 2023, 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.records;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.R8FullTestBuilder;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableMap;
+import java.nio.file.Path;
+import java.util.IdentityHashMap;
+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 RecordBlogTest extends TestBase {
+
+ private static final String RECORD_NAME = "RecordBlog";
+ private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME);
+ private static final String MAIN_TYPE = RecordTestUtils.getMainType(RECORD_NAME);
+ private static final String REFERENCE_OUTPUT_FORMAT = "Person[name=%s, age=42]";
+ private static final String CLASS = "records.RecordBlog$Person";
+ private static final Map<String, String> KEEP_RULE_TO_OUTPUT_FORMAT =
+ ImmutableMap.<String, String>builder()
+ .put("-dontobfuscate\n-dontoptimize", "RecordBlog$Person[name=%s, age=42]")
+ .put("", "a[a=%s]")
+ .put("-keep,allowshrinking class " + CLASS, "RecordBlog$Person[a=%s]")
+ .put(
+ "-keepclassmembers,allowshrinking,allowoptimization class "
+ + CLASS
+ + " { <fields>; }",
+ "a[name=%s]")
+ .put(
+ "-keepclassmembers,allowobfuscation,allowoptimization class "
+ + CLASS
+ + " { <fields>; }",
+ "a[a=%s, b=42]")
+ .put("-keep class " + CLASS + " { <fields>; }", "RecordBlog$Person[name=%s, age=42]")
+ .build();
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ private boolean isCfRuntimeWithNativeRecordSupport() {
+ return parameters.isCfRuntime()
+ && parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK14)
+ && parameters.getApiLevel().equals(AndroidApiLevel.B);
+ }
+
+ private String computeOutput(String format) {
+ return StringUtils.lines(String.format(format, "Jane"), String.format(format, "John"));
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ assumeTrue(isCfRuntimeWithNativeRecordSupport());
+ testForJvm(parameters)
+ .addProgramClassFileData(PROGRAM_DATA)
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(computeOutput(REFERENCE_OUTPUT_FORMAT));
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(PROGRAM_DATA)
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .assertSuccessWithOutput(computeOutput(REFERENCE_OUTPUT_FORMAT));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ parameters.assumeR8TestParameters();
+ assumeTrue(parameters.isDexRuntime() || isCfRuntimeWithNativeRecordSupport());
+ Path[] jdk15LibraryFiles = RecordTestUtils.getJdk15LibraryFiles(temp);
+ Map<String, String> results = new IdentityHashMap<>();
+ KEEP_RULE_TO_OUTPUT_FORMAT.forEach(
+ (kr, outputFormat) -> {
+ try {
+ R8FullTestBuilder builder =
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(PROGRAM_DATA)
+ .setMinApi(parameters)
+ .addKeepRules(kr)
+ .addKeepMainRule(MAIN_TYPE);
+ String res;
+ if (parameters.isCfRuntime()) {
+ res =
+ builder
+ .addLibraryFiles(jdk15LibraryFiles)
+ .run(parameters.getRuntime(), MAIN_TYPE)
+ .getStdOut();
+ } else {
+ res = builder.run(parameters.getRuntime(), MAIN_TYPE).getStdOut();
+ }
+ results.put(kr, res);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ boolean success = true;
+ for (String kr : KEEP_RULE_TO_OUTPUT_FORMAT.keySet()) {
+ if (!computeOutput(KEEP_RULE_TO_OUTPUT_FORMAT.get(kr)).equals(results.get(kr))) {
+ success = false;
+ }
+ }
+ if (!success) {
+ for (String kr : KEEP_RULE_TO_OUTPUT_FORMAT.keySet()) {
+ System.out.println("==========");
+ System.out.println("Keep rules:\n" + kr + "\n");
+ System.out.println("Expected:\n" + computeOutput(KEEP_RULE_TO_OUTPUT_FORMAT.get(kr)));
+ System.out.println("Got:\n" + results.get(kr));
+ }
+ System.out.println("==========");
+ }
+ assertTrue(success);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/MethodParametersTest.java b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/MethodParametersTest.java
index ecf5c8d..71c94de 100644
--- a/src/test/java/com/android/tools/r8/desugaring/interfacemethods/MethodParametersTest.java
+++ b/src/test/java/com/android/tools/r8/desugaring/interfacemethods/MethodParametersTest.java
@@ -29,9 +29,9 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- // java.lang.reflect.Method.getParameters() supported from Android 8.1.
+ // java.lang.reflect.Method.getParameters() supported from Android 8.
return getTestParameters()
- .withDexRuntimesStartingFromIncluding(Version.V8_1_0)
+ .withDexRuntimesStartingFromExcluding(Version.V7_0_0)
.withCfRuntimes()
.withAllApiLevelsAlsoForCf()
.build();
diff --git a/src/test/java/com/android/tools/r8/dex/DexCodeDeduppingTest.java b/src/test/java/com/android/tools/r8/dex/DexCodeDeduppingTest.java
new file mode 100644
index 0000000..4b0b57a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/dex/DexCodeDeduppingTest.java
@@ -0,0 +1,207 @@
+// Copyright (c) 2023, 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.dex;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.DexSegments;
+import com.android.tools.r8.DexSegments.Command;
+import com.android.tools.r8.DexSegments.SegmentInfo;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Map;
+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 DexCodeDeduppingTest extends TestBase {
+ private final TestParameters parameters;
+ private static final List<String> EXPECTED = ImmutableList.of("foo", "bar", "foo", "bar");
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntimes().withAllApiLevels().build();
+ }
+
+ public DexCodeDeduppingTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testR8SingleClass() throws Exception {
+ R8TestCompileResult compile =
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Foo.class)
+ .setMinApi(parameters)
+ .addKeepAllClassesRule()
+ .compile();
+ compile.run(parameters.getRuntime(), Foo.class).assertSuccessWithOutputLines(EXPECTED);
+ assertFooSizes(compile.writeToZip());
+ }
+
+ @Test
+ public void testR8WithLinesSingleClass() throws Exception {
+ R8TestCompileResult compile =
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Foo.class)
+ .setMinApi(parameters)
+ .addKeepAllClassesRule()
+ .addKeepAttributeLineNumberTable()
+ .compile();
+ compile.run(parameters.getRuntime(), Foo.class).assertSuccessWithOutputLines(EXPECTED);
+ assertFooSizes(compile.writeToZip());
+ }
+
+ @Test
+ public void testD8SingleClassMappingOutput() throws Exception {
+ D8TestCompileResult compile =
+ testForD8(parameters.getBackend())
+ .addProgramClasses(Foo.class)
+ .setMinApi(parameters)
+ .release()
+ .internalEnableMappingOutput()
+ .compile();
+ compile.run(parameters.getRuntime(), Foo.class).assertSuccessWithOutputLines(EXPECTED);
+ assertFooSizes(compile.writeToZip());
+ }
+
+ @Test
+ public void testD8SingleClassNoMappingOutput() throws Exception {
+ D8TestCompileResult compile =
+ testForD8(parameters.getBackend())
+ .addProgramClasses(Foo.class)
+ .setMinApi(parameters)
+ .release()
+ .compile();
+ compile.run(parameters.getRuntime(), Foo.class).assertSuccessWithOutputLines(EXPECTED);
+ // When d8 has no map output we can't share debug info and hence can't share code.
+ assertSizes(compile.writeToZip(), 4, 4);
+ }
+
+ @Test
+ public void testR8TwoClasses() throws Exception {
+ R8TestCompileResult compile =
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Foo.class, Bar.class)
+ .setMinApi(parameters)
+ .addKeepAllClassesRule()
+ .compile();
+ compile.run(parameters.getRuntime(), Foo.class).assertSuccessWithOutputLines(EXPECTED);
+ assertFooAndBarSizes(compile.writeToZip());
+ }
+
+ @Test
+ public void testR8WithLinesTwoClasses() throws Exception {
+ R8TestCompileResult compile =
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Foo.class, Bar.class)
+ .addKeepAttributeLineNumberTable()
+ .setMinApi(parameters)
+ .addKeepAllClassesRule()
+ .compile();
+ compile.run(parameters.getRuntime(), Foo.class).assertSuccessWithOutputLines(EXPECTED);
+ assertFooAndBarSizes(compile.writeToZip());
+ }
+
+ @Test
+ public void testD8TwoClassesMappingOutput() throws Exception {
+ D8TestCompileResult compile =
+ testForD8(parameters.getBackend())
+ .addProgramClasses(Foo.class, Bar.class)
+ .setMinApi(parameters)
+ .release()
+ .internalEnableMappingOutput()
+ .compile();
+ compile.run(parameters.getRuntime(), Foo.class).assertSuccessWithOutputLines(EXPECTED);
+ assertFooAndBarSizes(compile.writeToZip());
+ }
+
+ @Test
+ public void testD8TwoClassesNoMappingOutput() throws Exception {
+ D8TestCompileResult compile =
+ testForD8(parameters.getBackend())
+ .addProgramClasses(Foo.class, Bar.class)
+ .setMinApi(parameters)
+ .release()
+ .compile();
+ compile.run(parameters.getRuntime(), Foo.class).assertSuccessWithOutputLines(EXPECTED);
+ // When d8 has no map output we can't share debug info and hence can't share code.
+ assertSizes(compile.writeToZip(), 6, 6);
+ }
+
+ private void assertFooSizes(Path output) throws Exception {
+ assertSizes(output, 3, 4);
+ }
+
+ private void assertFooAndBarSizes(Path output) throws Exception {
+ assertSizes(output, 3, 6);
+ }
+
+ private void assertSizes(Path output, int deduppedSize, int originalSize)
+ throws CompilationFailedException, ResourceException, IOException {
+ if (parameters.isDexRuntime()) {
+ SegmentInfo codeSegmentInfo = getCodeSegmentInfo(output);
+ if (parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.S)) {
+ assertEquals(codeSegmentInfo.getItemCount(), deduppedSize);
+ } else {
+ assertEquals(codeSegmentInfo.getItemCount(), originalSize);
+ }
+ }
+ }
+
+ public SegmentInfo getCodeSegmentInfo(Path path)
+ throws CompilationFailedException, ResourceException, IOException {
+ Command.Builder builder = Command.builder().addProgramFiles(path);
+ Map<Integer, SegmentInfo> segmentInfoMap = DexSegments.run(builder.build());
+ return segmentInfoMap.get(Constants.TYPE_CODE_ITEM);
+ }
+
+ public static class Foo {
+ public static void main(String[] args) {
+ foo();
+ bar();
+ }
+
+ public static void foo() {
+ if (System.currentTimeMillis() == 0) {
+ System.out.println("That was early");
+ } else {
+ System.out.println("foo");
+ }
+ System.out.println("bar");
+ }
+
+ public static void bar() {
+ if (System.currentTimeMillis() == 0) {
+ System.out.println("That was early");
+ } else {
+ System.out.println("foo");
+ }
+ System.out.println("bar");
+ }
+ }
+
+ public static class Bar {
+ public static void foo() {
+ if (System.currentTimeMillis() == 0) {
+ System.out.println("That was early");
+ } else {
+ System.out.println("foo");
+ }
+ System.out.println("bar");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingMethods.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingMethods.java
index cc2aa0c..48818ec 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingMethods.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumUnboxingMethods.java
@@ -52,6 +52,12 @@
return unboxedEnum1 == unboxedEnum2;
}
+ // Objects#equals is similar to equals without the NPE for null entries.
+ // Objects.equals(null,null) answers true.
+ public static boolean objectEquals(int unboxedEnum1, int unboxedEnum2) {
+ return unboxedEnum1 == unboxedEnum2;
+ }
+
// Methods zeroCheck and zeroCheckMessage are used to replace null checks on unboxed enums.
public static void zeroCheck(int unboxedEnum) {
if (unboxedEnum == 0) {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EqualsCompareToEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EqualsCompareToEnumUnboxingTest.java
index 82689d3..172e118 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/EqualsCompareToEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/EqualsCompareToEnumUnboxingTest.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.enumunboxing;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
import java.util.List;
+import java.util.Objects;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -40,6 +42,7 @@
.addEnumUnboxingInspector(
inspector -> inspector.assertUnboxed(EnumEqualscompareTo.MyEnum.class))
.enableNeverClassInliningAnnotations()
+ .enableInliningAnnotations()
.addKeepRules(enumKeepRules.getKeepRules())
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
.setMinApi(parameters)
@@ -59,9 +62,31 @@
public static void main(String[] args) {
equalsTest();
+ objectsEqualsTest();
compareToTest();
}
+ @NeverInline
+ @SuppressWarnings({"ConstantConditions", "EqualsWithItself", "ResultOfMethodCallIgnored"})
+ private static void objectsEqualsTest() {
+ System.out.println(performObjectsEquals(MyEnum.A, MyEnum.B));
+ System.out.println(false);
+ System.out.println(performObjectsEquals(MyEnum.A, MyEnum.A));
+ System.out.println(true);
+ System.out.println(performObjectsEquals(MyEnum.A, null));
+ System.out.println(false);
+ System.out.println(performObjectsEquals(null, MyEnum.A));
+ System.out.println(false);
+ System.out.println(performObjectsEquals(null, null));
+ System.out.println(true);
+ }
+
+ @NeverInline
+ private static boolean performObjectsEquals(MyEnum a, MyEnum b) {
+ return Objects.equals(a, b);
+ }
+
+ @NeverInline
@SuppressWarnings({"ConstantConditions", "EqualsWithItself", "ResultOfMethodCallIgnored"})
private static void equalsTest() {
System.out.println(MyEnum.A.equals(MyEnum.B));
@@ -78,6 +103,7 @@
}
}
+ @NeverInline
@SuppressWarnings({"ConstantConditions", "EqualsWithItself", "ResultOfMethodCallIgnored"})
private static void compareToTest() {
System.out.println(MyEnum.B.compareTo(MyEnum.A) > 0);
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/StringValueOfEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/StringValueOfEnumUnboxingTest.java
index d8c656d..a09183d 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/StringValueOfEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/StringValueOfEnumUnboxingTest.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestParameters;
import java.util.List;
+import java.util.Objects;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -60,9 +61,17 @@
System.out.println(MyEnum.A.ordinal());
System.out.println(0);
stringValueOf();
+ objectsToString();
stringBuilder();
}
+ private static void objectsToString() {
+ System.out.println(getStringThroughObjects(MyEnum.A));
+ System.out.println("A");
+ System.out.println(getStringThroughObjects(null));
+ System.out.println("null");
+ }
+
private static void stringValueOf() {
System.out.println(getString(MyEnum.A));
System.out.println("A");
@@ -133,5 +142,10 @@
private static String getString(MyEnum e) {
return String.valueOf(e);
}
+
+ @NeverInline
+ private static String getStringThroughObjects(MyEnum e) {
+ return Objects.toString(e);
+ }
}
}
diff --git a/src/test/examples/enclosingmethod/AbstractClass.java b/src/test/java/com/android/tools/r8/examples/enclosingmethod/AbstractClass.java
similarity index 83%
rename from src/test/examples/enclosingmethod/AbstractClass.java
rename to src/test/java/com/android/tools/r8/examples/enclosingmethod/AbstractClass.java
index 90dfe27..65eb0fc 100644
--- a/src/test/examples/enclosingmethod/AbstractClass.java
+++ b/src/test/java/com/android/tools/r8/examples/enclosingmethod/AbstractClass.java
@@ -1,7 +1,7 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package enclosingmethod;
+package com.android.tools.r8.examples.enclosingmethod;
public abstract class AbstractClass {
public abstract int anInt();
diff --git a/src/test/java/com/android/tools/r8/examples/enclosingmethod/EnclosingMethodTestRunner.java b/src/test/java/com/android/tools/r8/examples/enclosingmethod/EnclosingMethodTestRunner.java
new file mode 100644
index 0000000..091df7b
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/enclosingmethod/EnclosingMethodTestRunner.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.enclosingmethod;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class EnclosingMethodTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public EnclosingMethodTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Main.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ return ImmutableList.of(
+ getMainClass(),
+ AbstractClass.class,
+ OuterClass.class,
+ OuterClass.AClass.class,
+ Class.forName(OuterClass.class.getTypeName() + "$1"),
+ Class.forName(OuterClass.class.getTypeName() + "$2"),
+ Class.forName(OuterClass.class.getTypeName() + "$1AnotherClass"),
+ Class.forName(OuterClass.class.getTypeName() + "$1LocalClass"));
+ }
+
+ private boolean isDalvikWithIncorrectBehavior() {
+ // Dalvik does not correctly report the enclosing classes.
+ return parameters.isDexRuntimeVersionOlderThanOrEqual(Version.V4_4_4);
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines(
+ "42",
+ "class com.android.tools.r8.examples.enclosingmethod.OuterClass",
+ "null",
+ "true",
+ isDalvikWithIncorrectBehavior() ? "true" : "false",
+ "7",
+ "class com.android.tools.r8.examples.enclosingmethod.OuterClass",
+ "null",
+ "false",
+ "true",
+ "42",
+ "class com.android.tools.r8.examples.enclosingmethod.OuterClass",
+ "public void com.android.tools.r8.examples.enclosingmethod.OuterClass.aMethod()",
+ "true",
+ "false",
+ "48",
+ "class com.android.tools.r8.examples.enclosingmethod.OuterClass",
+ "public void com.android.tools.r8.examples.enclosingmethod.OuterClass.aMethod()",
+ "false",
+ "true",
+ "InnerClass com.android.tools.r8.examples.enclosingmethod.OuterClass$AClass");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ Assume.assumeFalse(isDalvikWithIncorrectBehavior());
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // The program reflects on inner-outer classes so disable all shrinking and optimization.
+ runTestR8(b -> b.addDontShrink().addDontOptimize().addDontObfuscate().addKeepAllAttributes());
+ }
+
+ @Ignore("TODO(b/281805219): R8 output steps to a different line number.")
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/enclosingmethod/Main.java b/src/test/java/com/android/tools/r8/examples/enclosingmethod/Main.java
similarity index 90%
rename from src/test/examples/enclosingmethod/Main.java
rename to src/test/java/com/android/tools/r8/examples/enclosingmethod/Main.java
index c1c0b75..2eeb794 100644
--- a/src/test/examples/enclosingmethod/Main.java
+++ b/src/test/java/com/android/tools/r8/examples/enclosingmethod/Main.java
@@ -1,7 +1,7 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package enclosingmethod;
+package com.android.tools.r8.examples.enclosingmethod;
public class Main {
public static void main(String... args) {
diff --git a/src/test/examples/enclosingmethod/OuterClass.java b/src/test/java/com/android/tools/r8/examples/enclosingmethod/OuterClass.java
similarity index 96%
rename from src/test/examples/enclosingmethod/OuterClass.java
rename to src/test/java/com/android/tools/r8/examples/enclosingmethod/OuterClass.java
index 5268954..5f4562c 100644
--- a/src/test/examples/enclosingmethod/OuterClass.java
+++ b/src/test/java/com/android/tools/r8/examples/enclosingmethod/OuterClass.java
@@ -1,7 +1,7 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package enclosingmethod;
+package com.android.tools.r8.examples.enclosingmethod;
public class OuterClass {
// Named member class.
diff --git a/src/test/java/com/android/tools/r8/examples/jdk18/jdk8272564/Jdk8272564.java b/src/test/java/com/android/tools/r8/examples/jdk18/jdk8272564/Jdk8272564.java
index 5e891aa..5497f91 100644
--- a/src/test/java/com/android/tools/r8/examples/jdk18/jdk8272564/Jdk8272564.java
+++ b/src/test/java/com/android/tools/r8/examples/jdk18/jdk8272564/Jdk8272564.java
@@ -9,7 +9,7 @@
public class Jdk8272564 {
- private static final String EXAMPLE_FILE = "examplesJava18/jdk8272564";
+ private static final String EXAMPLE_FILE = "examplesJava20/jdk8272564";
public static final JavaExampleClassProxy A =
new JavaExampleClassProxy(EXAMPLE_FILE, "jdk8272564/A");
diff --git a/src/test/examples/nestedtrycatches/NestedTryCatches.java b/src/test/java/com/android/tools/r8/examples/nestedtrycatches/NestedTryCatches.java
similarity index 94%
rename from src/test/examples/nestedtrycatches/NestedTryCatches.java
rename to src/test/java/com/android/tools/r8/examples/nestedtrycatches/NestedTryCatches.java
index 00bec2a..43b4532 100644
--- a/src/test/examples/nestedtrycatches/NestedTryCatches.java
+++ b/src/test/java/com/android/tools/r8/examples/nestedtrycatches/NestedTryCatches.java
@@ -1,7 +1,7 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package nestedtrycatches;
+package com.android.tools.r8.examples.nestedtrycatches;
public class NestedTryCatches {
private static void throwException() {
diff --git a/src/test/java/com/android/tools/r8/examples/nestedtrycatches/NestedTryCatchesTestRunner.java b/src/test/java/com/android/tools/r8/examples/nestedtrycatches/NestedTryCatchesTestRunner.java
new file mode 100644
index 0000000..91c5e9f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/nestedtrycatches/NestedTryCatchesTestRunner.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.nestedtrycatches;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class NestedTryCatchesTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public NestedTryCatchesTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return NestedTryCatches.class;
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines("EXCEPTION: PRIMARY");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/regalloc/RegAlloc.java b/src/test/java/com/android/tools/r8/examples/regalloc/RegAlloc.java
similarity index 99%
rename from src/test/examples/regalloc/RegAlloc.java
rename to src/test/java/com/android/tools/r8/examples/regalloc/RegAlloc.java
index 68ad054..0b7474c 100644
--- a/src/test/examples/regalloc/RegAlloc.java
+++ b/src/test/java/com/android/tools/r8/examples/regalloc/RegAlloc.java
@@ -5,7 +5,7 @@
// This code is not run directly. It needs to be compiled to dex code.
// 'regalloc.dex' is what is run.
-package regalloc;
+package com.android.tools.r8.examples.regalloc;
// Various test cases that are challenging for the register allocator.
public class RegAlloc {
diff --git a/src/test/java/com/android/tools/r8/examples/regalloc/RegAllocTestRunner.java b/src/test/java/com/android/tools/r8/examples/regalloc/RegAllocTestRunner.java
new file mode 100644
index 0000000..df0c295
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/regalloc/RegAllocTestRunner.java
@@ -0,0 +1,65 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.regalloc;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.examples.regalloc.RegAlloc.BoxedInteger;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RegAllocTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public RegAllocTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return RegAlloc.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ return ImmutableList.of(getMainClass(), BoxedInteger.class);
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines(
+ "binaryOpUsingHighRegistersArguments: 17016 257 507",
+ "binaryDoubleOpUsingHighRegistersArguments: 251.0 125.0",
+ "binaryOpUsingHighRegistersLocals 518",
+ "instance get many registers42",
+ "sum: 33670",
+ "binaryOpUsingHighRegistersLocals 518.0",
+ "sum: 33670.0");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/regress/Regress.java b/src/test/java/com/android/tools/r8/examples/regress/Regress.java
similarity index 95%
rename from src/test/examples/regress/Regress.java
rename to src/test/java/com/android/tools/r8/examples/regress/Regress.java
index 4b931b6..8a9107e 100644
--- a/src/test/examples/regress/Regress.java
+++ b/src/test/java/com/android/tools/r8/examples/regress/Regress.java
@@ -5,7 +5,7 @@
// This code is not run directly. It needs to be compiled to dex code.
// 'arithmetic.dex' is what is run.
-package regress;
+package com.android.tools.r8.examples.regress;
public class Regress {
diff --git a/src/test/java/com/android/tools/r8/examples/regress/RegressTestRunner.java b/src/test/java/com/android/tools/r8/examples/regress/RegressTestRunner.java
new file mode 100644
index 0000000..36d5578
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/regress/RegressTestRunner.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.regress;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RegressTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public RegressTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Regress.class;
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines("LOOP", "LOOP", "LOOP", "LOOP");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/regress2/Regress2.java b/src/test/java/com/android/tools/r8/examples/regress2/Regress2.java
similarity index 94%
rename from src/test/examples/regress2/Regress2.java
rename to src/test/java/com/android/tools/r8/examples/regress2/Regress2.java
index 151a520..a1c5fcb 100644
--- a/src/test/examples/regress2/Regress2.java
+++ b/src/test/java/com/android/tools/r8/examples/regress2/Regress2.java
@@ -2,7 +2,7 @@
// for 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 regress2;
+package com.android.tools.r8.examples.regress2;
public class Regress2 {
diff --git a/src/test/java/com/android/tools/r8/examples/regress2/Regress2TestRunner.java b/src/test/java/com/android/tools/r8/examples/regress2/Regress2TestRunner.java
new file mode 100644
index 0000000..5de6bdf
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/regress2/Regress2TestRunner.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.regress2;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress2TestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public Regress2TestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Regress2.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ return ImmutableList.of(getMainClass(), Regress2.X.class);
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines("START", "LOOP", "LOOP", "LOOP", "LOOP", "LOOP", "END");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/regress_110373181/Regress.java b/src/test/java/com/android/tools/r8/examples/regress_110373181/Regress.java
similarity index 99%
rename from src/test/examples/regress_110373181/Regress.java
rename to src/test/java/com/android/tools/r8/examples/regress_110373181/Regress.java
index ae0d5bc..b4a06d4 100644
--- a/src/test/examples/regress_110373181/Regress.java
+++ b/src/test/java/com/android/tools/r8/examples/regress_110373181/Regress.java
@@ -1,7 +1,7 @@
// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_110373181;
+package com.android.tools.r8.examples.regress_110373181;
public class Regress {
public static class Inner {
diff --git a/src/test/java/com/android/tools/r8/examples/regress_110373181/Regress110373181TestRunner.java b/src/test/java/com/android/tools/r8/examples/regress_110373181/Regress110373181TestRunner.java
new file mode 100644
index 0000000..cf82548
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/regress_110373181/Regress110373181TestRunner.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.regress_110373181;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress110373181TestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public Regress110373181TestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Regress.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ return ImmutableList.of(getMainClass(), Regress.Inner.class);
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines("foo");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ // No debugging test as the execution is slow.
+}
diff --git a/src/test/examples/regress_37658666/Regress.java b/src/test/java/com/android/tools/r8/examples/regress_37658666/Regress.java
similarity index 90%
rename from src/test/examples/regress_37658666/Regress.java
rename to src/test/java/com/android/tools/r8/examples/regress_37658666/Regress.java
index 4f6019d..9173418 100644
--- a/src/test/examples/regress_37658666/Regress.java
+++ b/src/test/java/com/android/tools/r8/examples/regress_37658666/Regress.java
@@ -1,7 +1,7 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_37658666;
+package com.android.tools.r8.examples.regress_37658666;
class Float {
int cmp(float a, float b) {
diff --git a/src/test/java/com/android/tools/r8/examples/regress_37658666/Regress37658666TestRunner.java b/src/test/java/com/android/tools/r8/examples/regress_37658666/Regress37658666TestRunner.java
new file mode 100644
index 0000000..55e283a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/regress_37658666/Regress37658666TestRunner.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.regress_37658666;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress37658666TestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public Regress37658666TestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Regress.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ return ImmutableList.of(getMainClass(), Float.class);
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines("true");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/regress_37726195/Regress.java b/src/test/java/com/android/tools/r8/examples/regress_37726195/Regress.java
similarity index 95%
rename from src/test/examples/regress_37726195/Regress.java
rename to src/test/java/com/android/tools/r8/examples/regress_37726195/Regress.java
index ad9781a..6bf7e81 100644
--- a/src/test/examples/regress_37726195/Regress.java
+++ b/src/test/java/com/android/tools/r8/examples/regress_37726195/Regress.java
@@ -1,7 +1,7 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_37726195;
+package com.android.tools.r8.examples.regress_37726195;
public class Regress {
diff --git a/src/test/java/com/android/tools/r8/examples/regress_37726195/Regress37726195TestRunner.java b/src/test/java/com/android/tools/r8/examples/regress_37726195/Regress37726195TestRunner.java
new file mode 100644
index 0000000..7a8b0b6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/regress_37726195/Regress37726195TestRunner.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.regress_37726195;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress37726195TestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public Regress37726195TestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Regress.class;
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines("4", "4.0");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/regress_37875803/Regress.java b/src/test/java/com/android/tools/r8/examples/regress_37875803/Regress.java
similarity index 97%
rename from src/test/examples/regress_37875803/Regress.java
rename to src/test/java/com/android/tools/r8/examples/regress_37875803/Regress.java
index 9360046..26308cb 100644
--- a/src/test/examples/regress_37875803/Regress.java
+++ b/src/test/java/com/android/tools/r8/examples/regress_37875803/Regress.java
@@ -1,7 +1,7 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_37875803;
+package com.android.tools.r8.examples.regress_37875803;
public class Regress {
diff --git a/src/test/java/com/android/tools/r8/examples/regress_37875803/Regress37875803TestRunner.java b/src/test/java/com/android/tools/r8/examples/regress_37875803/Regress37875803TestRunner.java
new file mode 100644
index 0000000..f309514
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/regress_37875803/Regress37875803TestRunner.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.regress_37875803;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress37875803TestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public Regress37875803TestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Regress.class;
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines("10", "10", "10", "10", "204", "140", "107", "75");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/regress_37955340/Regress.java b/src/test/java/com/android/tools/r8/examples/regress_37955340/Regress.java
similarity index 89%
rename from src/test/examples/regress_37955340/Regress.java
rename to src/test/java/com/android/tools/r8/examples/regress_37955340/Regress.java
index a8b5e0b..a37737a 100644
--- a/src/test/examples/regress_37955340/Regress.java
+++ b/src/test/java/com/android/tools/r8/examples/regress_37955340/Regress.java
@@ -1,7 +1,7 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_37955340;
+package com.android.tools.r8.examples.regress_37955340;
public class Regress {
diff --git a/src/test/java/com/android/tools/r8/examples/regress_37955340/Regress37955340TestRunner.java b/src/test/java/com/android/tools/r8/examples/regress_37955340/Regress37955340TestRunner.java
new file mode 100644
index 0000000..a814068
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/regress_37955340/Regress37955340TestRunner.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.regress_37955340;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress37955340TestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public Regress37955340TestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Regress.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ return ImmutableList.of(getMainClass(), Regress.A.class, Regress.B.class);
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines("2");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/regress_62300145/Regress.java b/src/test/java/com/android/tools/r8/examples/regress_62300145/Regress.java
similarity index 80%
rename from src/test/examples/regress_62300145/Regress.java
rename to src/test/java/com/android/tools/r8/examples/regress_62300145/Regress.java
index b02d345..037cc1d 100644
--- a/src/test/examples/regress_62300145/Regress.java
+++ b/src/test/java/com/android/tools/r8/examples/regress_62300145/Regress.java
@@ -1,7 +1,7 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_62300145;
+package com.android.tools.r8.examples.regress_62300145;
import static java.lang.annotation.RetentionPolicy.CLASS;
@@ -32,12 +32,6 @@
Annotation[][] annotations = constructor.getParameterAnnotations();
int index = 0;
for (int i = 0; i < annotations.length; i++) {
- // TODO(b/67936230): Java 8 and Java 9 runtime does not have the same behavior regarding
- // implicit parameter such as 'outer this' for instance. Disable this test on Java 9 runtime
- // due to this divergence.
- if (System.getProperty("java.specification.version").equals("9") && i == 0) {
- continue;
- }
// On 9 and below, annotations are also printed for the Regress.class (first argument to the
// getDeclaredConstructor). b/120402200
if (i != 0 || annotations[i].length != 0) {
diff --git a/src/test/java/com/android/tools/r8/examples/regress_62300145/Regress62300145TestRunner.java b/src/test/java/com/android/tools/r8/examples/regress_62300145/Regress62300145TestRunner.java
new file mode 100644
index 0000000..a88c723
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/regress_62300145/Regress62300145TestRunner.java
@@ -0,0 +1,63 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.regress_62300145;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress62300145TestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public Regress62300145TestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Regress.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ return ImmutableList.of(
+ getMainClass(), Regress.A.class, Regress.B.class, Regress.InnerClass.class);
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.joinLines(
+ "0: @com.android.tools.r8.examples.regress_62300145.Regress$A()",
+ "1: @com.android.tools.r8.examples.regress_62300145.Regress$A()",
+ "2: ");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8(
+ b ->
+ b.addKeepAllAttributes()
+ .addKeepClassAndMembersRules(Regress.InnerClass.class)
+ .addKeepClassRules(Regress.A.class, Regress.B.class));
+ }
+
+ // Since DEX 9 and below have different runtime behavior it does not make sense to compare
+ // single stepping.
+}
diff --git a/src/test/examples/regress_64881691/Regress.java b/src/test/java/com/android/tools/r8/examples/regress_64881691/Regress.java
similarity index 89%
rename from src/test/examples/regress_64881691/Regress.java
rename to src/test/java/com/android/tools/r8/examples/regress_64881691/Regress.java
index f99a394..e0b14e0 100644
--- a/src/test/examples/regress_64881691/Regress.java
+++ b/src/test/java/com/android/tools/r8/examples/regress_64881691/Regress.java
@@ -1,7 +1,7 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_64881691;
+package com.android.tools.r8.examples.regress_64881691;
class A {
}
diff --git a/src/test/java/com/android/tools/r8/examples/regress_64881691/Regress64881691TestRunner.java b/src/test/java/com/android/tools/r8/examples/regress_64881691/Regress64881691TestRunner.java
new file mode 100644
index 0000000..15c1f88
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/regress_64881691/Regress64881691TestRunner.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.regress_64881691;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress64881691TestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public Regress64881691TestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Regress.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ return ImmutableList.of(getMainClass(), A.class, B.class);
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines("B");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8(b -> b.addKeepClassRules(A.class, B.class));
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/regress_65104300/Regress.java b/src/test/java/com/android/tools/r8/examples/regress_65104300/Regress.java
similarity index 92%
rename from src/test/examples/regress_65104300/Regress.java
rename to src/test/java/com/android/tools/r8/examples/regress_65104300/Regress.java
index 05a4333..44d87e7 100644
--- a/src/test/examples/regress_65104300/Regress.java
+++ b/src/test/java/com/android/tools/r8/examples/regress_65104300/Regress.java
@@ -1,7 +1,7 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_65104300;
+package com.android.tools.r8.examples.regress_65104300;
public class Regress {
// We correctly deduce that the array put cannot throw. However, we had a bug
diff --git a/src/test/java/com/android/tools/r8/examples/regress_65104300/Regress65104300TestRunner.java b/src/test/java/com/android/tools/r8/examples/regress_65104300/Regress65104300TestRunner.java
new file mode 100644
index 0000000..798cb08
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/regress_65104300/Regress65104300TestRunner.java
@@ -0,0 +1,50 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.regress_65104300;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress65104300TestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public Regress65104300TestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Regress.class;
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines("0");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/examples/regress_70703087/Regress70703087TestRunner.java b/src/test/java/com/android/tools/r8/examples/regress_70703087/Regress70703087TestRunner.java
new file mode 100644
index 0000000..11ade17
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/regress_70703087/Regress70703087TestRunner.java
@@ -0,0 +1,205 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.regress_70703087;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress70703087TestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public Regress70703087TestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return TestClass.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ return ImmutableList.of(
+ getMainClass(),
+ Class.forName(typeName(TestClass.class) + "$1"),
+ Class.forName(typeName(TestClass.class) + "$X"),
+ Class.forName(typeName(TestClass.class) + "$A"),
+ Class.forName(typeName(TestClass.class) + "$B"),
+ Class.forName(typeName(TestClass.class) + "$C"));
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines(
+ "An exception was caught.",
+ "r = 1.7532855E9",
+ "mZ = false",
+ "mI = -1938324620",
+ "mJ = -242539528963191",
+ "mF = -1.0",
+ "mD = 0.0",
+ "mArray = [1.0, 5.0, 13.0, 29.0, 61.0, 125.0, 253.0, 509.0, 1021.0, 2045.0, 4093.0, 8189.0,"
+ + " 16381.0, 32765.0, 65533.0, 131069.0, 262141.0, 524285.0, 1048573.0, 2097149.0,"
+ + " 4194301.0, 8388605.0, 1.6777213E7, 3.3554429E7, 6.7108861E7, 1.34217725E8,"
+ + " 2.68435453E8, 5.36870909E8, 1.073741821E9, 2.147483645E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9, -1.938324619E9,"
+ + " -1.938324619E9, -1.938324619E9, 1.796355124E9, 1.796355125E9, 1.796355126E9,"
+ + " 1.796355127E9, 1.796355128E9, 1.796355129E9, 1.79635513E9, 1.796355131E9,"
+ + " 1.796355132E9, 1.796355133E9, 1.796355134E9, 1.796355135E9, 1.796355136E9,"
+ + " 1.796355137E9, 1.796355138E9, 1.796355139E9, 1.79635514E9, 1.796355141E9,"
+ + " 1.796355142E9, 1.796355143E9, 1.796355144E9, 1.796355145E9, 1.796355146E9,"
+ + " 1.796355147E9, 1.796355148E9, 1.796355149E9, 1.79635515E9, 1.796355151E9,"
+ + " 1.796355152E9, 1.796355153E9, 1.796355154E9, 1.796355155E9, 1.796355156E9,"
+ + " 1.796355157E9, 1.796355158E9, 1.796355159E9, 1.79635516E9, 1.796355161E9,"
+ + " 1.796355162E9, 1.796355163E9, 1.796355164E9, 1.796355165E9, 1.796355166E9,"
+ + " 1.796355167E9, 1.796355168E9, 1.796355169E9, 1.79635517E9, 1.796355171E9,"
+ + " 1.796355172E9, 1.796355173E9, 1.796355174E9, 1.796355175E9, 1.796355176E9,"
+ + " 1.796355177E9, 1.796355178E9, 1.796355179E9, 1.79635518E9, 1.796355181E9,"
+ + " 1.796355182E9, 1.796355183E9, 1.796355184E9, 1.796355185E9, 1.796355186E9,"
+ + " 1.796355187E9, 1.796355188E9, 1.796355189E9, 1.79635519E9, 1.796355191E9,"
+ + " 1.796355192E9, 1.796355193E9, 1.796355194E9, 1.796355195E9, 1.796355196E9,"
+ + " 1.796355197E9, 1.796355198E9, 1.796355199E9, 1.7963552E9, 1.796355201E9,"
+ + " 1.796355202E9, 1.796355203E9, 1.796355204E9, 1.796355205E9, 1.796355206E9,"
+ + " 1.796355207E9, 1.796355208E9, 1.796355209E9, 1.79635521E9, 1.796355211E9,"
+ + " 1.796355212E9, 1.796355213E9, 1.796355214E9, 1.796355215E9, 1.796355216E9,"
+ + " 1.796355217E9, 1.796355218E9, 1.796355219E9, 1.79635522E9, 1.796355221E9,"
+ + " 1.796355222E9, 1.796355223E9, 1.796355224E9, 1.796355225E9, 1.796355226E9,"
+ + " 1.796355227E9, 1.796355228E9, 1.796355229E9, 1.79635523E9, 1.796355231E9,"
+ + " 1.796355232E9, 1.796355233E9, 1.796355234E9, 1.796355235E9, 1.796355236E9,"
+ + " 1.796355237E9, 1.796355238E9, 1.796355239E9, 1.79635524E9, 1.796355241E9,"
+ + " 1.796355242E9, 1.796355243E9, 1.796355244E9, 1.796355245E9, 1.796355246E9,"
+ + " 1.796355247E9, 1.796355248E9, 1.796355249E9, 1.79635525E9, 1.796355251E9,"
+ + " 1.796355252E9, 1.796355253E9, 1.796355254E9, 1.796355255E9, 1.796355256E9,"
+ + " 1.796355257E9, 1.796355258E9, 1.796355259E9, 1.79635526E9, 1.796355261E9,"
+ + " 1.796355262E9, 1.796355263E9, 1.796355264E9, 1.796355265E9, 1.796355266E9,"
+ + " 1.796355267E9, 1.796355268E9, 1.796355269E9, 1.79635527E9, 1.796355271E9,"
+ + " 1.796355272E9, 1.796355273E9, 1.796355274E9, 1.796355275E9, 1.796355276E9,"
+ + " 1.796355277E9, 1.796355278E9, 1.796355279E9, 1.79635528E9, 1.796355281E9,"
+ + " 1.796355282E9, 1.796355283E9, 1.796355284E9, 1.796355285E9, 1.796355286E9,"
+ + " 1.796355287E9, 1.796355288E9, 1.796355289E9, 1.79635529E9, 1.796355291E9,"
+ + " 1.796355292E9, 1.796355293E9, 1.796355294E9, 1.796355295E9, 1.796355296E9,"
+ + " 1.796355297E9, 1.796355298E9, 1.796355299E9, 1.7963553E9, 1.796355301E9,"
+ + " 1.796355302E9, 1.796355303E9, 1.796355304E9, 1.796355305E9, 1.796355306E9,"
+ + " 1.796355307E9, 1.796355308E9, 1.796355309E9, 1.79635531E9, 1.796355311E9,"
+ + " 1.796355312E9, 1.796355313E9, 1.796355314E9, 1.796355315E9, 1.796355316E9,"
+ + " 1.796355317E9, 1.796355318E9, 1.796355319E9, 1.79635532E9, 1.796355321E9,"
+ + " 1.796355322E9, 1.796355323E9, 1.796355324E9, 1.796355325E9, 1.796355326E9,"
+ + " 1.796355327E9, 1.796355328E9, 1.796355329E9, 1.79635533E9, 1.796355331E9,"
+ + " 1.796355332E9, 1.796355333E9, 1.796355334E9, 1.796355335E9, 1.796355336E9,"
+ + " 1.796355337E9, 1.796355338E9, 1.796355339E9, 1.79635534E9, 1.796355341E9,"
+ + " 1.796355342E9, 1.796355343E9, 1.796355344E9, 1.796355345E9, 1.796355346E9,"
+ + " 1.796355347E9, 1.796355348E9, 1.796355349E9, 1.79635535E9, 1.796355351E9,"
+ + " 1.796355352E9, 1.796355353E9, 1.796355354E9, 1.796355355E9, 1.796355356E9,"
+ + " 1.796355357E9, 1.796355358E9, 1.796355359E9, 1.79635536E9, 1.796355361E9,"
+ + " 1.796355362E9, 1.796355363E9, 1.796355364E9, 1.796355365E9, 1.796355366E9,"
+ + " 1.796355367E9, 1.796355368E9, 1.796355369E9, 1.79635537E9, 1.796355371E9,"
+ + " 1.796355372E9, 1.796355373E9, 1.796355374E9, 1.796355375E9, 1.796355376E9,"
+ + " 1.796355377E9, 1.796355378E9, 1.796355379E9, 1.79635538E9, 1.796355381E9,"
+ + " 1.796355382E9, 1.796355383E9, 1.796355384E9, 1.796355385E9, 1.796355386E9,"
+ + " 1.796355387E9, 1.796355388E9, 1.796355389E9, 1.79635539E9, 1.796355391E9,"
+ + " 1.796355392E9, 1.796355393E9, 1.796355394E9, 1.796355395E9, 1.796355396E9,"
+ + " 1.796355397E9, 1.796355398E9, 1.796355399E9, 1.7963554E9, 1.796355401E9,"
+ + " 1.796355402E9, 1.796355403E9, 1.796355404E9, 1.796355405E9, 1.796355406E9,"
+ + " 1.796355407E9, 1.796355408E9, 1.796355409E9, 1.79635541E9, 1.796355411E9,"
+ + " 1.796355412E9, 1.796355413E9, 1.796355414E9, 1.796355415E9, 1.796355416E9,"
+ + " 1.796355417E9, 1.796355418E9, 1.796355419E9, 1.79635542E9, 1.796355421E9,"
+ + " 1.796355422E9, 1.796355423E9, 1.796355424E9, 1.796355425E9, 1.796355426E9,"
+ + " 1.796355427E9, 1.796355428E9, 1.796355429E9, 1.79635543E9, 1.796355431E9,"
+ + " 1.796355432E9, 1.796355433E9, 1.796355434E9, 1.796355435E9, 1.796355436E9,"
+ + " 1.796355437E9, 1.796355438E9, 1.796355439E9, 1.79635544E9, 1.796355441E9,"
+ + " 1.796355442E9, 1.796355443E9, 1.796355444E9, 1.796355445E9, 1.796355446E9,"
+ + " 1.796355447E9, 1.796355448E9, 1.796355449E9, 1.79635545E9, 1.796355451E9,"
+ + " 1.796355452E9, 1.796355453E9, 1.796355454E9, 1.796355455E9, 1.796355456E9,"
+ + " 1.796355457E9, 1.796355458E9, 1.796355459E9, 1.79635546E9, 1.796355461E9,"
+ + " 1.796355462E9, 1.796355463E9, 1.796355464E9, 1.796355465E9, 1.796355466E9,"
+ + " 1.796355467E9, 1.796355468E9, 1.796355469E9, 1.79635547E9, 1.796355471E9,"
+ + " 1.796355472E9, 1.796355473E9, 1.796355474E9, 1.796355475E9, 1.796355476E9,"
+ + " 1.796355477E9, 1.796355478E9, 1.796355479E9, 1.79635548E9, 1.796355481E9,"
+ + " 1.796355482E9, 1.796355483E9, 1.796355484E9, 1.796355485E9, 1.796355486E9,"
+ + " 1.796355487E9, 1.796355488E9, 1.796355489E9, 1.79635549E9, 1.796355491E9,"
+ + " 1.796355492E9, 1.796355493E9, 1.796355494E9, 1.796355495E9, 1.796355496E9,"
+ + " 1.796355497E9, 1.796355498E9, 1.796355499E9, 1.7963555E9, 1.796355501E9,"
+ + " 1.796355502E9, 1.796355503E9, 1.796355504E9, 1.796355505E9, 1.796355506E9,"
+ + " 1.796355507E9, 1.796355508E9, 1.796355509E9, 1.79635551E9, 1.796355511E9,"
+ + " 1.796355512E9, 1.796355513E9, 1.796355514E9, 1.796355515E9, 1.796355516E9,"
+ + " 1.796355517E9, 1.796355518E9, 1.796355519E9, 1.79635552E9, 1.796355521E9,"
+ + " 1.796355522E9, 1.796355523E9, 1.796355524E9, 1.796355525E9, 1.796355526E9,"
+ + " 1.796355527E9, 1.796355528E9, 1.796355529E9, 1.79635553E9, 1.796355531E9,"
+ + " 1.796355532E9, 1.796355533E9, 1.796355534E9, 1.796355535E9, 1.796355536E9,"
+ + " 1.796355537E9, 1.796355538E9, 1.796355539E9, 1.79635554E9, 1.796355541E9,"
+ + " 1.796355542E9, 1.796355543E9, 1.796355544E9, 1.796355545E9, 1.796355546E9,"
+ + " 1.796355547E9, 1.796355548E9, 1.796355549E9, 1.79635555E9, 1.796355551E9,"
+ + " 1.796355552E9, 1.796355553E9, 1.796355554E9, 1.796355555E9, 1.796355556E9,"
+ + " 1.796355557E9, 1.796355558E9, 1.796355559E9, 1.79635556E9, 1.796355561E9,"
+ + " 1.796355562E9, 1.796355563E9, 1.796355564E9, 1.796355565E9, 1.796355566E9,"
+ + " 1.796355567E9, 1.796355568E9, 1.796355569E9, 1.79635557E9, 1.796355571E9,"
+ + " 1.796355572E9, 1.796355573E9, 1.796355574E9, 1.796355575E9, 1.796355576E9,"
+ + " 1.796355577E9, 1.796355578E9, 1.796355579E9, 1.79635558E9, 1.796355581E9,"
+ + " 1.796355582E9, 1.796355583E9, 1.796355584E9, 1.796355585E9, 1.796355586E9,"
+ + " 1.796355587E9, 1.796355588E9, 1.796355589E9, 1.79635559E9, 1.796355591E9,"
+ + " 1.796355592E9, 1.796355593E9, 1.796355594E9, 1.796355595E9, 1.796355596E9,"
+ + " 1.796355597E9, 1.796355598E9, 1.796355599E9, 1.7963556E9, 1.796355601E9,"
+ + " 1.796355602E9, 1.796355603E9, 1.796355604E9, 1.796355605E9, 1.796355606E9,"
+ + " 1.796355607E9, 1.796355608E9, 1.796355609E9, 1.79635561E9, 1.796355611E9,"
+ + " 1.796355612E9, 1.796355613E9, 1.796355614E9, 1.796355615E9, 1.796355616E9,"
+ + " 1.796355617E9, 1.796355618E9, 1.796355619E9, 1.79635562E9, 1.796355621E9,"
+ + " 1.796355622E9, 1.796355623E9, 1.796355624E9, 1.796355625E9]");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ // Debug stepping takes forever so don't test it (b/79671093).
+}
diff --git a/src/test/examples/regress_70703087/Test.java b/src/test/java/com/android/tools/r8/examples/regress_70703087/TestClass.java
similarity index 96%
rename from src/test/examples/regress_70703087/Test.java
rename to src/test/java/com/android/tools/r8/examples/regress_70703087/TestClass.java
index 4fb3f2d..c9a324e 100644
--- a/src/test/examples/regress_70703087/Test.java
+++ b/src/test/java/com/android/tools/r8/examples/regress_70703087/TestClass.java
@@ -1,7 +1,7 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_70703087;
+package com.android.tools.r8.examples.regress_70703087;
/**
* AOSP JFuzz Tester.
@@ -11,7 +11,7 @@
import java.util.Arrays;
-public class Test {
+public class TestClass {
private interface X {
int x();
@@ -58,7 +58,7 @@
private double[] mArray = new double[700];
- private Test() {
+ private TestClass() {
double a = 1796354926.0;
for (int i0 = 0; i0 < 700; i0++) {
mArray[i0] = a;
@@ -161,7 +161,7 @@
}
public static void main(String[] args) {
- Test t = new Test();
+ TestClass t = new TestClass();
float r = 1753285454.0f;
try {
r = t.testMethod();
diff --git a/src/test/java/com/android/tools/r8/examples/regress_70736958/Regress70736958TestRunner.java b/src/test/java/com/android/tools/r8/examples/regress_70736958/Regress70736958TestRunner.java
new file mode 100644
index 0000000..5f228ee
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/regress_70736958/Regress70736958TestRunner.java
@@ -0,0 +1,153 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.regress_70736958;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress70736958TestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public Regress70736958TestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return TestClass.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ return ImmutableList.of(
+ getMainClass(),
+ Class.forName(typeName(TestClass.class) + "$1"),
+ Class.forName(typeName(TestClass.class) + "$X"),
+ Class.forName(typeName(TestClass.class) + "$A"),
+ Class.forName(typeName(TestClass.class) + "$B"),
+ Class.forName(typeName(TestClass.class) + "$C"));
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines(
+ "r = true",
+ "mZ = false",
+ "mI = 1",
+ "mJ = 936439722084176000",
+ "mF = 0.0",
+ "mD = 0.0",
+ "mArray = [1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9,"
+ + " 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9, 1.6101889E9]");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/regress_70736958/Test.java b/src/test/java/com/android/tools/r8/examples/regress_70736958/TestClass.java
similarity index 93%
rename from src/test/examples/regress_70736958/Test.java
rename to src/test/java/com/android/tools/r8/examples/regress_70736958/TestClass.java
index 3186043..1f2ae2e 100644
--- a/src/test/examples/regress_70736958/Test.java
+++ b/src/test/java/com/android/tools/r8/examples/regress_70736958/TestClass.java
@@ -2,7 +2,7 @@
// for 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 regress_70736958;
+package com.android.tools.r8.examples.regress_70736958;
/**
* AOSP JFuzz Tester.
@@ -12,7 +12,7 @@
import java.util.Arrays;
-public class Test {
+public class TestClass {
private interface X {
int x();
@@ -59,7 +59,7 @@
private float[] mArray = new float[504];
- private Test() {
+ private TestClass() {
float a = 1610188871.0f;
for (int i0 = 0; i0 < 504; i0++) {
mArray[i0] = a;
@@ -74,7 +74,7 @@
switch (360) {
case 180: {
mJ &= (mJ++);
- mZ &= (this instanceof Test);
+ mZ &= (this instanceof TestClass);
{
boolean lZ0 = (((false & mZ) | true) & false);
switch (96) {
@@ -91,7 +91,7 @@
} else {
lZ0 = (Double.isNaN(lD0));
}
- mZ = (this instanceof Test);
+ mZ = (this instanceof TestClass);
mJ /= (mJ | 513583530L);
} catch (IllegalStateException ex1_0) {
switch (354) {
@@ -117,7 +117,7 @@
{
int i2 = -1;
while (++i2 < 168) {
- mZ ^= (this instanceof Test);
+ mZ ^= (this instanceof TestClass);
}
}
nop();
@@ -165,7 +165,7 @@
public static void nop() {}
public static void main(String[] args) {
- Test t = new Test();
+ TestClass t = new TestClass();
boolean r = true;
try {
r = t.testMethod();
diff --git a/src/test/java/com/android/tools/r8/examples/regress_70737019/Regress70737019TestRunner.java b/src/test/java/com/android/tools/r8/examples/regress_70737019/Regress70737019TestRunner.java
new file mode 100644
index 0000000..93bb0d4
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/regress_70737019/Regress70737019TestRunner.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.regress_70737019;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress70737019TestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public Regress70737019TestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return TestClass.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ String outerClass = typeName(TestClass.class);
+ return ImmutableList.of(
+ getMainClass(),
+ Class.forName(outerClass + "$1"),
+ Class.forName(outerClass + "$X"),
+ Class.forName(outerClass + "$A"),
+ Class.forName(outerClass + "$B"),
+ Class.forName(outerClass + "$C"));
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines(
+ "r = NaN",
+ "mZ = false",
+ "mI = 1",
+ "mJ = 0",
+ "mF = NaN",
+ "mD = 0.0",
+ "mArray = [[[[[[[[[[1.0775E8]]]]]]]]]]");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/regress_70737019/Test.java b/src/test/java/com/android/tools/r8/examples/regress_70737019/TestClass.java
similarity index 96%
rename from src/test/examples/regress_70737019/Test.java
rename to src/test/java/com/android/tools/r8/examples/regress_70737019/TestClass.java
index 59d498e..27fb29e 100644
--- a/src/test/examples/regress_70737019/Test.java
+++ b/src/test/java/com/android/tools/r8/examples/regress_70737019/TestClass.java
@@ -2,7 +2,7 @@
// for 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 regress_70737019;
+package com.android.tools.r8.examples.regress_70737019;
/**
* AOSP JFuzz Tester.
@@ -12,7 +12,7 @@
import java.util.Arrays;
-public class Test {
+public class TestClass {
private interface X {
int x();
@@ -63,7 +63,7 @@
return a && b;
}
- private Test() {
+ private TestClass() {
float a = 107750002.0f;
for (int i0 = 0; i0 < 1; i0++) {
for (int i1 = 0; i1 < 1; i1++) {
@@ -131,7 +131,7 @@
mArray = tmp;
}
}
- mZ &= ((mI != 820389954) ? mZ : (this instanceof Test));
+ mZ &= ((mI != 820389954) ? mZ : (this instanceof TestClass));
break;
}
}
@@ -168,7 +168,7 @@
mI %= (~ 1326172655);
switch (i0) {
case 0: {
- mZ |= (true | ((logicalAnd(mZ, (this instanceof Test))) && mZ));
+ mZ |= (true | ((logicalAnd(mZ, (this instanceof TestClass))) && mZ));
if ((((boolean) new Boolean(true)) || (((boolean) new Boolean((false | (! mZ)))) | (! true)))) {
mI /= ((int)(byte)(int) (((mI / mI) >> 265234301) | 188234363));
{
@@ -193,7 +193,7 @@
}
}
}
- mZ |= (this instanceof Test);
+ mZ |= (this instanceof TestClass);
break;
}
default: {
@@ -232,7 +232,7 @@
public static void nop() {}
public static void main(String[] args) {
- Test t = new Test();
+ TestClass t = new TestClass();
float r = 1457261414.0f;
try {
r = t.testMethod();
diff --git a/src/test/java/com/android/tools/r8/examples/regress_72361252/Regress72361252TestRunner.java b/src/test/java/com/android/tools/r8/examples/regress_72361252/Regress72361252TestRunner.java
new file mode 100644
index 0000000..53d0d04
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/regress_72361252/Regress72361252TestRunner.java
@@ -0,0 +1,72 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.regress_72361252;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class Regress72361252TestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public Regress72361252TestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return TestClass.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ String outerClass = typeName(TestClass.class);
+ return ImmutableList.of(
+ getMainClass(),
+ Class.forName(outerClass + "$1"),
+ Class.forName(outerClass + "$X"),
+ Class.forName(outerClass + "$A"),
+ Class.forName(outerClass + "$B"),
+ Class.forName(outerClass + "$C"));
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines(
+ "An exception was caught.",
+ "r = -4.37780077E8",
+ "mZ = false",
+ "mI = 0",
+ "mJ = 0",
+ "mF = 0.0",
+ "mD = 0.0",
+ "mArray = [[[[[[[[[1.7861851E9]]]]]]]]]");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/regress_72361252/Test.java b/src/test/java/com/android/tools/r8/examples/regress_72361252/TestClass.java
similarity index 97%
rename from src/test/examples/regress_72361252/Test.java
rename to src/test/java/com/android/tools/r8/examples/regress_72361252/TestClass.java
index 055d80a..887e672 100644
--- a/src/test/examples/regress_72361252/Test.java
+++ b/src/test/java/com/android/tools/r8/examples/regress_72361252/TestClass.java
@@ -2,7 +2,7 @@
// for 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 regress_72361252;
+package com.android.tools.r8.examples.regress_72361252;
/**
* AOSP JFuzz Tester.
@@ -12,7 +12,7 @@
import java.util.Arrays;
-public class Test {
+public class TestClass {
private interface X {
int x();
@@ -59,7 +59,7 @@
private float[][][][][][][][][] mArray = new float[1][1][1][1][1][1][1][1][1];
- private Test() {
+ private TestClass() {
float a = 1786185107.0f;
for (int i0 = 0; i0 < 1; i0++) {
for (int i1 = 0; i1 < 1; i1++) {
@@ -104,7 +104,7 @@
while (++i2 < 1) {
try {
mD *= ((double) (++mArray[i1][0][i0][0][0][i0][i0][0][0]));
- mD -= ((this instanceof Test) ? 110559916.0 : mD);
+ mD -= ((this instanceof TestClass) ? 110559916.0 : mD);
lJ0 -= (- (+ 417176163L));
lJ0 /= (+ -491731430L);
mI /= (-651651736 * ((int)(byte)(int) mI));
@@ -284,7 +284,7 @@
public static void nop() {}
public static void main(String[] args) {
- Test t = new Test();
+ TestClass t = new TestClass();
double r = -437780077.0;
try {
r = t.testMethod();
diff --git a/src/test/examples/returns/Returns.java b/src/test/java/com/android/tools/r8/examples/returns/Returns.java
similarity index 92%
rename from src/test/examples/returns/Returns.java
rename to src/test/java/com/android/tools/r8/examples/returns/Returns.java
index c496ce5..f3955df 100644
--- a/src/test/examples/returns/Returns.java
+++ b/src/test/java/com/android/tools/r8/examples/returns/Returns.java
@@ -2,7 +2,7 @@
// for 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 returns;
+package com.android.tools.r8.examples.returns;
public class Returns {
public static void main(String[] args) {
diff --git a/src/test/java/com/android/tools/r8/examples/returns/ReturnsTestRunner.java b/src/test/java/com/android/tools/r8/examples/returns/ReturnsTestRunner.java
new file mode 100644
index 0000000..1a18ae6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/returns/ReturnsTestRunner.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.returns;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ReturnsTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public ReturnsTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Returns.class;
+ }
+
+ @Override
+ public String getExpected() {
+ return "";
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/staticfield/StaticField.java b/src/test/java/com/android/tools/r8/examples/staticfield/StaticField.java
similarity index 95%
rename from src/test/examples/staticfield/StaticField.java
rename to src/test/java/com/android/tools/r8/examples/staticfield/StaticField.java
index f4d5854..5c48a2e 100644
--- a/src/test/examples/staticfield/StaticField.java
+++ b/src/test/java/com/android/tools/r8/examples/staticfield/StaticField.java
@@ -2,7 +2,7 @@
// for 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 staticfield;
+package com.android.tools.r8.examples.staticfield;
public class StaticField {
diff --git a/src/test/java/com/android/tools/r8/examples/staticfield/StaticFieldTestRunner.java b/src/test/java/com/android/tools/r8/examples/staticfield/StaticFieldTestRunner.java
new file mode 100644
index 0000000..3cb14eb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/staticfield/StaticFieldTestRunner.java
@@ -0,0 +1,53 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.staticfield;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StaticFieldTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public StaticFieldTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return StaticField.class;
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines("101010", "101010", "ABC", "ABC");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ // TODO(b/79671093): DEX has different line number info during stepping.
+ Assume.assumeTrue(parameters.isCfRuntime());
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/stringbuilding/StringBuilding.java b/src/test/java/com/android/tools/r8/examples/stringbuilding/StringBuilding.java
similarity index 96%
rename from src/test/examples/stringbuilding/StringBuilding.java
rename to src/test/java/com/android/tools/r8/examples/stringbuilding/StringBuilding.java
index 18152fb..0805f37 100644
--- a/src/test/examples/stringbuilding/StringBuilding.java
+++ b/src/test/java/com/android/tools/r8/examples/stringbuilding/StringBuilding.java
@@ -5,7 +5,7 @@
// This code is not run directly. It needs to be compiled to dex code.
// 'stringbuilding.dex' is what is run.
-package stringbuilding;
+package com.android.tools.r8.examples.stringbuilding;
class StringBuilding {
diff --git a/src/test/java/com/android/tools/r8/examples/stringbuilding/StringBuildingTestRunner.java b/src/test/java/com/android/tools/r8/examples/stringbuilding/StringBuildingTestRunner.java
new file mode 100644
index 0000000..db98076
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/stringbuilding/StringBuildingTestRunner.java
@@ -0,0 +1,57 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.stringbuilding;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class StringBuildingTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public StringBuildingTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return StringBuilding.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ return ImmutableList.of(getMainClass(), StringBuilding.X.class);
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.joinLines("a2c-xyz-abc7xyz", "trueABCDE1234232.21.101an Xstringbuilder");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/switches/Switches.java b/src/test/java/com/android/tools/r8/examples/switches/Switches.java
similarity index 97%
rename from src/test/examples/switches/Switches.java
rename to src/test/java/com/android/tools/r8/examples/switches/Switches.java
index 588e541..a51e94b 100644
--- a/src/test/examples/switches/Switches.java
+++ b/src/test/java/com/android/tools/r8/examples/switches/Switches.java
@@ -5,7 +5,7 @@
// This code is not run directly. It needs to be compiled to dex code.
// 'switches.dex' is what is run.
-package switches;
+package com.android.tools.r8.examples.switches;
class Switches {
diff --git a/src/test/java/com/android/tools/r8/examples/switches/SwitchesTestRunner.java b/src/test/java/com/android/tools/r8/examples/switches/SwitchesTestRunner.java
new file mode 100644
index 0000000..5bcd322
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/switches/SwitchesTestRunner.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.switches;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class SwitchesTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public SwitchesTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Switches.class;
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines(
+ "packedSwitch cases: 0 1 2 after switch 0",
+ "packedSwitch cases: 1 2 after switch 1",
+ "packedSwitch cases: 1 2 after switch 2",
+ "packedSwitch cases: after switch -1",
+ "0 ",
+ "100 ",
+ "after switch 0",
+ "100 ",
+ "after switch 100",
+ "200 ",
+ "after switch 200",
+ "after switch -1",
+ " 420",
+ " 1.02",
+ "0-21 after switch 1",
+ "0-21 after switch 10",
+ "after switch 40",
+ "60 after switch 60");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/switchmaps/Colors.java b/src/test/java/com/android/tools/r8/examples/switchmaps/Colors.java
similarity index 89%
rename from src/test/examples/switchmaps/Colors.java
rename to src/test/java/com/android/tools/r8/examples/switchmaps/Colors.java
index b2c8dc1..15b4ad6 100644
--- a/src/test/examples/switchmaps/Colors.java
+++ b/src/test/java/com/android/tools/r8/examples/switchmaps/Colors.java
@@ -1,7 +1,7 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package switchmaps;
+package com.android.tools.r8.examples.switchmaps;
public enum Colors {
RED("rar"), BLUE("blew"), GREEN("soylent"), GRAY("fifty");
diff --git a/src/test/examples/switchmaps/Days.java b/src/test/java/com/android/tools/r8/examples/switchmaps/Days.java
similarity index 85%
rename from src/test/examples/switchmaps/Days.java
rename to src/test/java/com/android/tools/r8/examples/switchmaps/Days.java
index b484bb5..710edfa 100644
--- a/src/test/examples/switchmaps/Days.java
+++ b/src/test/java/com/android/tools/r8/examples/switchmaps/Days.java
@@ -1,7 +1,7 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package switchmaps;
+package com.android.tools.r8.examples.switchmaps;
public enum Days {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
diff --git a/src/test/java/com/android/tools/r8/examples/switchmaps/SwitchMapsTestRunner.java b/src/test/java/com/android/tools/r8/examples/switchmaps/SwitchMapsTestRunner.java
new file mode 100644
index 0000000..2936fd8
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/switchmaps/SwitchMapsTestRunner.java
@@ -0,0 +1,92 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.switchmaps;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class SwitchMapsTestRunner extends ExamplesTestBase {
+
+ public static final String EXPECTED =
+ StringUtils.lines(
+ "other",
+ "1, 3 or 4",
+ "2 or 5",
+ "other",
+ "2 or 5",
+ "3 or 5",
+ "1, 3 or 4",
+ "2 or 5",
+ "other",
+ "1, 3 or 4",
+ "2 or 5",
+ "3 or 5",
+ "2 or 5",
+ "other",
+ "6",
+ "7",
+ "7",
+ "rar",
+ "colorful",
+ "blew",
+ "colorful",
+ "soylent",
+ "sooo green",
+ "fifty",
+ "not really");
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public SwitchMapsTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Switches.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ return ImmutableList.of(
+ getMainClass(),
+ Class.forName(getMainClass().getTypeName() + "$1"),
+ Days.class,
+ Colors.class);
+ }
+
+ @Override
+ public String getExpected() {
+ return EXPECTED;
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ // TODO(b/79671093): DEX has different line number info during stepping.
+ Assume.assumeTrue(parameters.isCfRuntime());
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/switchmaps/Switches.java b/src/test/java/com/android/tools/r8/examples/switchmaps/Switches.java
similarity index 96%
rename from src/test/examples/switchmaps/Switches.java
rename to src/test/java/com/android/tools/r8/examples/switchmaps/Switches.java
index bf16856..4b5cb54 100644
--- a/src/test/examples/switchmaps/Switches.java
+++ b/src/test/java/com/android/tools/r8/examples/switchmaps/Switches.java
@@ -1,7 +1,7 @@
// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package switchmaps;
+package com.android.tools.r8.examples.switchmaps;
public class Switches {
diff --git a/src/test/examples/sync/Sync.java b/src/test/java/com/android/tools/r8/examples/sync/Sync.java
similarity index 98%
rename from src/test/examples/sync/Sync.java
rename to src/test/java/com/android/tools/r8/examples/sync/Sync.java
index 7459d0e..095c8c9 100644
--- a/src/test/examples/sync/Sync.java
+++ b/src/test/java/com/android/tools/r8/examples/sync/Sync.java
@@ -1,7 +1,7 @@
// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package sync;
+package com.android.tools.r8.examples.sync;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
diff --git a/src/test/java/com/android/tools/r8/examples/sync/SyncTestRunner.java b/src/test/java/com/android/tools/r8/examples/sync/SyncTestRunner.java
new file mode 100644
index 0000000..8502356
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/sync/SyncTestRunner.java
@@ -0,0 +1,86 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.sync;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class SyncTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public SyncTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Sync.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ return ImmutableList.of(
+ getMainClass(),
+ Sync.Consumer.class,
+ Class.forName(getMainClass().getTypeName() + "$1"),
+ Class.forName(getMainClass().getTypeName() + "$2"),
+ Class.forName(getMainClass().getTypeName() + "$3"),
+ Class.forName(getMainClass().getTypeName() + "$4"),
+ Class.forName(getMainClass().getTypeName() + "$5"));
+ }
+
+ @Override
+ public String getExpected() {
+ List<String> lines = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ lines.add("static");
+ lines.add("end");
+ }
+ for (int i = 0; i < 10; i++) {
+ lines.add("instance");
+ lines.add("end");
+ }
+ for (int i = 0; i < 20; i++) {
+ lines.add("manual");
+ }
+ for (int i = 0; i < 10; i++) {
+ lines.add("trycatch");
+ lines.add("end");
+ }
+ lines.add("caught throw");
+ lines.add("caught throw");
+ return StringUtils.lines(lines);
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ // TODO(b/79671093): DEX has different line number info during stepping.
+ Assume.assumeTrue(parameters.isCfRuntime());
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/throwing/Overloaded.java b/src/test/java/com/android/tools/r8/examples/throwing/Overloaded.java
similarity index 93%
rename from src/test/examples/throwing/Overloaded.java
rename to src/test/java/com/android/tools/r8/examples/throwing/Overloaded.java
index 44b5435..26a9cd5 100644
--- a/src/test/examples/throwing/Overloaded.java
+++ b/src/test/java/com/android/tools/r8/examples/throwing/Overloaded.java
@@ -1,7 +1,7 @@
// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package throwing;
+package com.android.tools.r8.examples.throwing;
import java.util.List;
diff --git a/src/test/examples/throwing/RenamedClass.java b/src/test/java/com/android/tools/r8/examples/throwing/RenamedClass.java
similarity index 96%
rename from src/test/examples/throwing/RenamedClass.java
rename to src/test/java/com/android/tools/r8/examples/throwing/RenamedClass.java
index a3fd537..3a3f513 100644
--- a/src/test/examples/throwing/RenamedClass.java
+++ b/src/test/java/com/android/tools/r8/examples/throwing/RenamedClass.java
@@ -5,7 +5,7 @@
// This code is not run directly. It needs to be compiled to dex code.
// 'throwing.dex' is what is run.
-package throwing;
+package com.android.tools.r8.examples.throwing;
import java.util.ArrayList;
import java.util.LinkedList;
diff --git a/src/test/examples/throwing/Throwing.java b/src/test/java/com/android/tools/r8/examples/throwing/Throwing.java
similarity index 98%
rename from src/test/examples/throwing/Throwing.java
rename to src/test/java/com/android/tools/r8/examples/throwing/Throwing.java
index 80b8115..a072f14 100644
--- a/src/test/examples/throwing/Throwing.java
+++ b/src/test/java/com/android/tools/r8/examples/throwing/Throwing.java
@@ -5,7 +5,7 @@
// This code is not run directly. It needs to be compiled to dex code.
// 'throwing.dex' is what is run.
-package throwing;
+package com.android.tools.r8.examples.throwing;
import java.util.Collections;
import java.util.List;
diff --git a/src/test/java/com/android/tools/r8/examples/throwing/ThrowingTestRunner.java b/src/test/java/com/android/tools/r8/examples/throwing/ThrowingTestRunner.java
new file mode 100644
index 0000000..582aad59
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/throwing/ThrowingTestRunner.java
@@ -0,0 +1,133 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.throwing;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ThrowingTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public ThrowingTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return Throwing.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ return ImmutableList.of(
+ getMainClass(), Overloaded.class, RenamedClass.class, Throwing.Nested.class);
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines(
+ "FRAME: com.android.tools.r8.examples.throwing.Throwing.throwAtFistLine(Throwing.java:115)",
+ "FRAME: com.android.tools.r8.examples.throwing.Throwing.main(Throwing.java:19)",
+ "FRAME: com.android.tools.r8.examples.throwing.Throwing.throwInMiddle(Throwing.java:123)",
+ "FRAME: com.android.tools.r8.examples.throwing.Throwing.main(Throwing.java:24)",
+ "FRAME:"
+ + " com.android.tools.r8.examples.throwing.Throwing.throwAfterMultiInline(Throwing.java:135)",
+ "FRAME: com.android.tools.r8.examples.throwing.Throwing.main(Throwing.java:29)",
+ "FRAME: com.android.tools.r8.examples.throwing.Throwing.main(Throwing.java:41)",
+ "FRAME:"
+ + " com.android.tools.r8.examples.throwing.Throwing$Nested.justThrow(Throwing.java:207)",
+ "FRAME: com.android.tools.r8.examples.throwing.Throwing.main(Throwing.java:49)",
+ "20Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Incremented by 10.",
+ "FRAME:"
+ + " com.android.tools.r8.examples.throwing.Throwing.throwInAFunctionThatIsNotInlinedAndCalledTwice(Throwing.java:145)",
+ "FRAME: com.android.tools.r8.examples.throwing.Throwing.main(Throwing.java:65)",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Incremented by 10.",
+ "FRAME:"
+ + " com.android.tools.r8.examples.throwing.Throwing.throwInAFunctionThatIsNotInlinedAndCalledTwice(Throwing.java:145)",
+ "FRAME: com.android.tools.r8.examples.throwing.Throwing.main(Throwing.java:71)",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Incremented by 10.",
+ "FRAME:"
+ + " com.android.tools.r8.examples.throwing.Throwing.anotherThrowingMethodToInline(Throwing.java:151)",
+ "FRAME:"
+ + " com.android.tools.r8.examples.throwing.Throwing.aFunctionThatCallsAnInlinedMethodThatThrows(Throwing.java:164)",
+ "FRAME: com.android.tools.r8.examples.throwing.Throwing.main(Throwing.java:77)",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Increment by one!",
+ "Incremented by 10.",
+ "FRAME:"
+ + " com.android.tools.r8.examples.throwing.Throwing.yetAnotherThrowingMethodToInline(Throwing.java:170)",
+ "FRAME:"
+ + " com.android.tools.r8.examples.throwing.Throwing.anotherFunctionThatCallsAnInlinedMethodThatThrows(Throwing.java:183)",
+ "FRAME: com.android.tools.r8.examples.throwing.Throwing.main(Throwing.java:83)",
+ "FRAME:"
+ + " com.android.tools.r8.examples.throwing.Throwing.aFunctionsThatThrowsBeforeAnInlinedMethod(Throwing.java:188)",
+ "FRAME: com.android.tools.r8.examples.throwing.Throwing.main(Throwing.java:89)");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ // The expected output includes the reflected frames so disable all optimization.
+ runTestR8(b -> b.addDontOptimize().addDontObfuscate().addKeepAllAttributes());
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/trycatch/TryCatch.java b/src/test/java/com/android/tools/r8/examples/trycatch/TryCatch.java
similarity index 97%
rename from src/test/examples/trycatch/TryCatch.java
rename to src/test/java/com/android/tools/r8/examples/trycatch/TryCatch.java
index 509e7c9..b052ab0 100644
--- a/src/test/examples/trycatch/TryCatch.java
+++ b/src/test/java/com/android/tools/r8/examples/trycatch/TryCatch.java
@@ -5,12 +5,11 @@
// This code is not run directly. It needs to be compiled to dex code.
// 'trycatch.dex' is what is run.
-package trycatch;
+package com.android.tools.r8.examples.trycatch;
class TryCatch {
-
- private static class Thrower {
+ static class Thrower {
private boolean shouldThrow;
diff --git a/src/test/java/com/android/tools/r8/examples/trycatch/TryCatchTestRunner.java b/src/test/java/com/android/tools/r8/examples/trycatch/TryCatchTestRunner.java
new file mode 100644
index 0000000..9d668ab
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/trycatch/TryCatchTestRunner.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.trycatch;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class TryCatchTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public TryCatchTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return TryCatch.class;
+ }
+
+ @Override
+ public List<Class<?>> getTestClasses() throws Exception {
+ return ImmutableList.of(
+ getMainClass(), TryCatch.A.class, TryCatch.B.class, TryCatch.Thrower.class);
+ }
+
+ @Override
+ public String getExpected() {
+ return StringUtils.lines(
+ "Did not throw",
+ "junk 1",
+ "junk 2",
+ "Did throw",
+ "junk 2",
+ "junk 1",
+ "three",
+ "two",
+ "one",
+ "Success!Error!");
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/examples/trycatchmany/TryCatchMany.java b/src/test/java/com/android/tools/r8/examples/trycatchmany/TryCatchMany.java
similarity index 94%
rename from src/test/examples/trycatchmany/TryCatchMany.java
rename to src/test/java/com/android/tools/r8/examples/trycatchmany/TryCatchMany.java
index eb07f27..8cdce20 100644
--- a/src/test/examples/trycatchmany/TryCatchMany.java
+++ b/src/test/java/com/android/tools/r8/examples/trycatchmany/TryCatchMany.java
@@ -5,7 +5,7 @@
// This code is not run directly. It needs to be compiled to dex code.
// 'trycatchmany.dex' is what is run.
-package trycatchmany;
+package com.android.tools.r8.examples.trycatchmany;
class TryCatchMany {
public static void main(String[] args) {
diff --git a/src/test/java/com/android/tools/r8/examples/trycatchmany/TryCatchManyTestRunner.java b/src/test/java/com/android/tools/r8/examples/trycatchmany/TryCatchManyTestRunner.java
new file mode 100644
index 0000000..666c0a2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/examples/trycatchmany/TryCatchManyTestRunner.java
@@ -0,0 +1,49 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.examples.trycatchmany;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.examples.ExamplesTestBase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class TryCatchManyTestRunner extends ExamplesTestBase {
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().enableApiLevelsForCf().build();
+ }
+
+ public TryCatchManyTestRunner(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<?> getMainClass() {
+ return TryCatchMany.class;
+ }
+
+ @Override
+ public String getExpected() {
+ return "Success!";
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ runTestDesugaring();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTestR8();
+ }
+
+ @Test
+ public void testDebug() throws Exception {
+ runTestDebugComparator();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java b/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java
new file mode 100644
index 0000000..bd06608
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/globalsynthetics/GlobalSyntheticsEnsureClassesOutputTest.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2023, 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.globalsynthetics;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.GlobalSyntheticsGenerator;
+import com.android.tools.r8.GlobalSyntheticsGeneratorCommand;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.google.common.collect.Sets;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class GlobalSyntheticsEnsureClassesOutputTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public GlobalSyntheticsEnsureClassesOutputTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ @Test
+ public void testNumberOfClassesOnK() throws Exception {
+ Path output = temp.newFolder().toPath().resolve("output.zip");
+ GlobalSyntheticsGenerator.run(
+ GlobalSyntheticsGeneratorCommand.builder()
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.API_DATABASE_LEVEL))
+ .setMinApiLevel(AndroidApiLevel.K.getLevel())
+ .setProgramConsumer(new DexIndexedConsumer.ArchiveConsumer(output))
+ .build());
+ CodeInspector inspector = new CodeInspector(output);
+ assertEquals(1026, inspector.allClasses().size());
+ }
+
+ @Test
+ public void testNumberOfClassesOnLatest() throws Exception {
+ Path output = temp.newFolder().toPath().resolve("output.zip");
+ GlobalSyntheticsGenerator.run(
+ GlobalSyntheticsGeneratorCommand.builder()
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.API_DATABASE_LEVEL))
+ .setMinApiLevel(AndroidApiLevel.LATEST.getLevel())
+ .setProgramConsumer(new DexIndexedConsumer.ArchiveConsumer(output))
+ .build());
+ Set<String> expectedInOutput = new HashSet<>();
+ // The output contains a RecordTag type that is mapped back to the original java.lang.Record by
+ // our codeinspector.
+ expectedInOutput.add("Ljava/lang/Record;");
+ expectedInOutput.add("Ljava/lang/invoke/VarHandle;");
+ expectedInOutput.add("Ljava/lang/invoke/MethodHandles$Lookup;");
+ assertEquals(
+ expectedInOutput,
+ new CodeInspector(output)
+ .allClasses().stream()
+ .map(FoundClassSubject::getFinalDescriptor)
+ .collect(Collectors.toSet()));
+ }
+
+ @Test
+ public void testClassFileListOutput() throws Exception {
+ Set<String> generatedGlobalSynthetics = Sets.newConcurrentHashSet();
+ Path output = temp.newFolder().toPath().resolve("output.zip");
+ runGlobalSyntheticsGenerator(
+ GlobalSyntheticsGeneratorCommand.builder()
+ .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.API_DATABASE_LEVEL))
+ .setMinApiLevel(AndroidApiLevel.K.getLevel())
+ .setProgramConsumer(new DexIndexedConsumer.ArchiveConsumer(output))
+ .build(),
+ options ->
+ options.testing.globalSyntheticCreatedCallback =
+ programClass -> {
+ if (programClass
+ .getClassReference()
+ .getDescriptor()
+ .equals(DexItemFactory.varHandleDescriptorString)) {
+ // We emit a desugared var handle. Rewrite it here to allow checking for final
+ // type names.
+ generatedGlobalSynthetics.add(
+ Reference.classFromDescriptor(
+ DexItemFactory.desugarVarHandleDescriptorString)
+ .getTypeName());
+ } else if (programClass
+ .getClassReference()
+ .getDescriptor()
+ .equals(DexItemFactory.methodHandlesLookupDescriptorString)) {
+ // We emit a desugared var handle. Rewrite it here to allow checking for final
+ // type names.
+ generatedGlobalSynthetics.add(
+ Reference.classFromDescriptor(
+ DexItemFactory.desugarMethodHandlesLookupDescriptorString)
+ .getTypeName());
+ } else {
+ generatedGlobalSynthetics.add(programClass.getTypeName());
+ }
+ });
+ Set<String> readGlobalSynthetics =
+ new CodeInspector(output)
+ .allClasses().stream().map(FoundClassSubject::getFinalName).collect(Collectors.toSet());
+ assertEquals(generatedGlobalSynthetics, readGlobalSynthetics);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
index 4c92f65..e0f7e1d 100644
--- a/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/graph/GenericSignatureTest.java
@@ -118,8 +118,8 @@
classSignature = clazz.classSignature;
assertNotNull(classSignature);
- assertEquals(1, classSignature.formalTypeParameters.size());
- FormalTypeParameter formalTypeParameter = classSignature.formalTypeParameters.get(0);
+ assertEquals(1, classSignature.getFormalTypeParameters().size());
+ FormalTypeParameter formalTypeParameter = classSignature.getFormalTypeParameters().get(0);
assertEquals("T", formalTypeParameter.name);
assertTrue(formalTypeParameter.interfaceBounds.isEmpty());
assertTrue(formalTypeParameter.classBound.isClassTypeSignature());
@@ -135,8 +135,8 @@
.asTypeVariableSignature()
.typeVariable);
- assertTrue(classSignature.superInterfaceSignatures.isEmpty());
- classTypeSignature = classSignature.superClassSignature;
+ assertTrue(classSignature.getSuperInterfaceSignatures().isEmpty());
+ classTypeSignature = classSignature.getSuperClassSignatureOrNull();
assertEquals(cy.getDexProgramClass().type, classTypeSignature.type);
typeArguments = classTypeSignature.typeArguments;
assertEquals(1, typeArguments.size());
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureReflectiveInnerTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureReflectiveInnerTest.java
index 178b7bb..03f40e9 100644
--- a/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureReflectiveInnerTest.java
+++ b/src/test/java/com/android/tools/r8/graph/genericsignature/GenericSignatureReflectiveInnerTest.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -50,6 +51,7 @@
.addKeepAttributeSignature()
.addKeepClassRules(Foo.Bar.class)
.enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
.setMinApi(parameters)
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(EXPECTED)
@@ -60,6 +62,7 @@
});
}
+ @NeverClassInline
public static class Foo<T> {
public class Bar<S> {
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/SingletonClassInitializerPatternCanBePostponedTest.java b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/SingletonClassInitializerPatternCanBePostponedTest.java
index 4ef9896..8193867 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/SingletonClassInitializerPatternCanBePostponedTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/SingletonClassInitializerPatternCanBePostponedTest.java
@@ -5,11 +5,8 @@
package com.android.tools.r8.ir.analysis.sideeffect;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
-import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -18,28 +15,25 @@
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 SingletonClassInitializerPatternCanBePostponedTest extends TestBase {
- private final TestParameters parameters;
+ @Parameter(0)
+ public TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public SingletonClassInitializerPatternCanBePostponedTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
@Test
public void test() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(SingletonClassInitializerPatternCanBePostponedTest.class)
.addKeepMainRule(TestClass.class)
- .enableMemberValuePropagationAnnotations()
.setMinApi(parameters)
.compile()
.inspect(this::inspect)
@@ -50,10 +44,6 @@
private void inspect(CodeInspector inspector) {
ClassSubject classSubject = inspector.clazz(A.class);
assertThat(classSubject, isAbsent());
-
- // A.inlineable() should be inlined because we should be able to determine that A.<clinit>() can
- // safely be postponed.
- assertThat(classSubject.uniqueMethodWithOriginalName("inlineable"), not(isPresent()));
}
static class TestClass {
@@ -68,7 +58,7 @@
static A INSTANCE = new A(" world!");
- @NeverPropagateValue final String message;
+ final String message;
A(String message) {
this.message = message;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/InstanceGetOnNewInstanceCanonicalizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/InstanceGetOnNewInstanceCanonicalizationTest.java
index 827c194..c687cd8 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/InstanceGetOnNewInstanceCanonicalizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/canonicalization/InstanceGetOnNewInstanceCanonicalizationTest.java
@@ -8,6 +8,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.NoInliningOfDefaultInitializer;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -35,6 +36,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
+ .enableNoInliningOfDefaultInitializerAnnotations()
.setMinApi(parameters)
.compile()
.inspect(
@@ -52,6 +54,7 @@
.assertSuccessWithOutputLines("Hello, world!");
}
+ @NoInliningOfDefaultInitializer
static class Main {
final String field = System.currentTimeMillis() > 0 ? ", " : null;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ConstructorWithNonTrivialControlFlowTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ConstructorWithNonTrivialControlFlowTest.java
index 7ce73e4..1453d42 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ConstructorWithNonTrivialControlFlowTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ConstructorWithNonTrivialControlFlowTest.java
@@ -86,6 +86,7 @@
Object x;
int y;
+ @NeverInline
Candidate(Object x, int y) {
if (x == null || x.toString().isEmpty()) {
throw new RuntimeException(
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/EscapeFromParentConstructorTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/EscapeFromParentConstructorTest.java
index ed6de58..692c4b3 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/EscapeFromParentConstructorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/EscapeFromParentConstructorTest.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize.classinliner;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverInline;
@@ -47,10 +48,14 @@
}
private void inspect(CodeInspector inspector) {
- // The receiver escapes from BuilderBase.<init>(), and therefore, Builder is not considered
- // eligible for class inlining.
- assertThat(inspector.clazz(BuilderBase.class), isPresent());
- assertThat(inspector.clazz(Builder.class), isPresent());
+ // The receiver escapes from BuilderBase.<init>() (except when inlined) and therefore Builder is
+ // not considered eligible for class inlining.
+ assertThat(
+ inspector.clazz(BuilderBase.class),
+ notIf(isPresent(), parameters.canInitNewInstanceUsingSuperclassConstructor()));
+ assertThat(
+ inspector.clazz(Builder.class),
+ notIf(isPresent(), parameters.canInitNewInstanceUsingSuperclassConstructor()));
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/cost/NonMaterializingFieldAccessesAfterClassInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/cost/NonMaterializingFieldAccessesAfterClassInliningTest.java
index 2a71b26..cfcc1af 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/cost/NonMaterializingFieldAccessesAfterClassInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/cost/NonMaterializingFieldAccessesAfterClassInliningTest.java
@@ -93,6 +93,7 @@
final char c11;
final char c12;
+ @NeverInline
Greeter(
char c1,
char c2,
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
index 38eddf9..308b42b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/Regress131349148.java
@@ -70,7 +70,7 @@
setupR8TestBuilder(
b ->
b.apply(ApiModelingTestHelper::enableOutliningOfMethods)
- .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(ApiModelingTestHelper::enableStubbingOfClassesAndDisableGlobalSyntheticCheck)
.apply(ApiModelingTestHelper::enableApiCallerIdentification),
inspector -> {
ClassSubject classSubject = inspector.clazz(TestClassCallingMethodWithNonExisting.class);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
index 69ad5cc..f5b5a47 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleTargetAfterInliningTest.java
@@ -5,12 +5,15 @@
package com.android.tools.r8.ir.optimize.inliner;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.AlwaysInline;
import com.android.tools.r8.AssumeMayHaveSideEffects;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverReprocessMethod;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -48,9 +51,11 @@
.addOptionsModification(
options ->
options.inlinerOptions().applyInliningToInlineePredicateForTesting =
- (appView, inlinee, inliningDepth) -> true)
+ (appView, inlinee, inliningDepth) -> inliningDepth <= maxInliningDepth)
.enableAlwaysInliningAnnotations()
+ .enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNeverReprocessMethodAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.enableSideEffectAnnotations()
.setMinApi(parameters)
@@ -67,18 +72,29 @@
// The indirection() method should be inlined.
assertThat(testClassSubject.uniqueMethodWithOriginalName("indirection"), not(isPresent()));
- // A.foo() should be absent if the max inlining depth is 1, because indirection() has been
- // inlined into main(), which makes A.foo() eligible for inlining into main().
ClassSubject aClassSubject = inspector.clazz(A.class);
assertThat(aClassSubject, isPresent());
- assertThat(aClassSubject.uniqueMethodWithOriginalName("foo"), not(isPresent()));
- // A.bar() should always be inlined because it is marked as @AlwaysInline.
- assertThat(aClassSubject.uniqueMethodWithOriginalName("bar"), not(isPresent()));
+ // B.foo() should be absent if the max inlining depth is 1, because indirection() has been
+ // inlined into main(), which makes B.foo() eligible for inlining into main().
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+ assertThat(
+ bClassSubject.uniqueMethodWithOriginalName("foo"),
+ notIf(isPresent(), maxInliningDepth == 1));
+
+ // B.bar() should always be inlined because it is marked as @AlwaysInline.
+ assertThat(
+ bClassSubject.uniqueMethodWithOriginalName("bar"),
+ notIf(isPresent(), maxInliningDepth == 1));
+
+ ClassSubject cClassSubject = inspector.clazz(C.class);
+ assertThat(cClassSubject, isPresent());
}
static class TestClass {
+ @NeverReprocessMethod
public static void main(String[] args) {
// Ensure C is instantiated, to prevent us from finding a single target in indirection().
new C();
@@ -103,6 +119,7 @@
static class B extends A {
@AssumeMayHaveSideEffects // To ensure that new B() cannot be removed.
+ @NeverInline
B() {}
@Override
@@ -122,6 +139,7 @@
static class C extends A {
@AssumeMayHaveSideEffects // To ensure that new C() cannot be removed.
+ @NeverInline
C() {}
@Override
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlineConstructorsWithMonitors.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlineConstructorsWithMonitors.java
index 506437d..ec2bd71 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlineConstructorsWithMonitors.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/sync/InlineConstructorsWithMonitors.java
@@ -5,15 +5,14 @@
package com.android.tools.r8.ir.optimize.inliner.sync;
import static com.android.tools.r8.utils.codeinspector.Matchers.hasDefaultConstructor;
+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.notIf;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
@@ -40,6 +39,13 @@
testForR8(parameters.getBackend())
.addInnerClasses(InlineConstructorsWithMonitors.class)
.addKeepMainRule(TestClass.class)
+ .addHorizontallyMergedClassesInspector(
+ inspector ->
+ inspector
+ .applyIf(
+ !parameters.canHaveIssueWithInlinedMonitors(),
+ i -> i.assertMergedInto(Foo.class, Bar.class))
+ .assertNoOtherClassesMerged())
.setMinApi(parameters)
.compile()
.run(parameters.getRuntime(), TestClass.class)
@@ -52,18 +58,21 @@
assertThat(classSubject, isPresent());
ClassSubject fooClassSubject = inspector.clazz(Foo.class);
ClassSubject barClassSubject = inspector.clazz(Bar.class);
- if (parameters.isCfRuntime()
- || parameters.getApiLevel().isLessThanOrEqualTo(AndroidApiLevel.M)) {
+ if (parameters.canHaveIssueWithInlinedMonitors()) {
// On M and below we don't want to merge constructors when both have monitors. See b/238399429
- assertThat(fooClassSubject, hasDefaultConstructor());
+ assertThat(
+ fooClassSubject,
+ notIf(isPresent(), parameters.canInitNewInstanceUsingSuperclassConstructor()));
+ assertThat(
+ fooClassSubject,
+ notIf(
+ hasDefaultConstructor(), parameters.canInitNewInstanceUsingSuperclassConstructor()));
+ assertThat(barClassSubject, isPresent());
assertThat(barClassSubject, hasDefaultConstructor());
} else {
- assertTrue(
- fooClassSubject.uniqueInstanceInitializer().isPresent()
- || barClassSubject.uniqueInstanceInitializer().isPresent());
- assertFalse(
- fooClassSubject.uniqueInstanceInitializer().isPresent()
- && barClassSubject.uniqueInstanceInitializer().isPresent());
+ assertThat(fooClassSubject, isAbsent());
+ assertThat(barClassSubject, isPresent());
+ assertThat(barClassSubject.uniqueInstanceInitializer(), isPresent());
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FinalFieldWithDefaultValueAssignmentPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FinalFieldWithDefaultValueAssignmentPropagationTest.java
index 3cd865a..00695b4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FinalFieldWithDefaultValueAssignmentPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FinalFieldWithDefaultValueAssignmentPropagationTest.java
@@ -82,6 +82,7 @@
final boolean alwaysFalse;
+ @NeverInline
Config() {
// An instruction that causes alwaysFalse to be read.
System.out.println(this);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithDefaultValueAssignmentPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithDefaultValueAssignmentPropagationTest.java
index eb85bb7..ab8e784 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithDefaultValueAssignmentPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithDefaultValueAssignmentPropagationTest.java
@@ -84,6 +84,7 @@
boolean alwaysFalse;
+ @NeverInline
Config() {
// An instruction that cannot read alwaysFalse, because the receiver has not escaped
// (except into Object.<init>()).
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithNonDefaultValueAssignmentPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithNonDefaultValueAssignmentPropagationTest.java
index eff93c1..01acbd4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithNonDefaultValueAssignmentPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithNonDefaultValueAssignmentPropagationTest.java
@@ -85,6 +85,7 @@
boolean alwaysTrue;
+ @NeverInline
Config() {
// An instruction that cannot read alwaysTrue, because the receiver has not escaped
// (except into Object.<init>()).
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 8950ca5..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
@@ -5,7 +5,7 @@
package com.android.tools.r8.ir.optimize.membervaluepropagation.fields;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
@@ -51,7 +51,10 @@
ClassSubject testClassSubject = inspector.clazz(TestClass.class);
assertThat(testClassSubject, isPresent());
assertThat(testClassSubject.uniqueMethodWithOriginalName("live"), isPresent());
- assertThat(testClassSubject.uniqueMethodWithOriginalName("dead"), not(isPresent()));
+ // 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/membervaluepropagation/fields/FieldInitializedByNonConstantArgumentInSuperConstructorTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByNonConstantArgumentInSuperConstructorTest.java
index 90e9fc6..435cd31 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByNonConstantArgumentInSuperConstructorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/FieldInitializedByNonConstantArgumentInSuperConstructorTest.java
@@ -5,7 +5,7 @@
package com.android.tools.r8.ir.optimize.membervaluepropagation.fields;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentIf;
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
@@ -53,7 +53,10 @@
ClassSubject testClassSubject = inspector.clazz(TestClass.class);
assertThat(testClassSubject, isPresent());
assertThat(testClassSubject.uniqueMethodWithOriginalName("live"), isPresent());
- assertThat(testClassSubject.uniqueMethodWithOriginalName("dead"), not(isPresent()));
+ // TODO(b/280275115): Constructor inlining regresses field inlining.
+ assertThat(
+ testClassSubject.uniqueMethodWithOriginalName("dead"),
+ isPresentIf(parameters.canInitNewInstanceUsingSuperclassConstructor()));
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/singleton/SingletonFieldValuePropagationEnumWithSubclassTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/singleton/SingletonFieldValuePropagationEnumWithSubclassTest.java
index 657d32e..610ad58 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/singleton/SingletonFieldValuePropagationEnumWithSubclassTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/fields/singleton/SingletonFieldValuePropagationEnumWithSubclassTest.java
@@ -4,14 +4,12 @@
package com.android.tools.r8.ir.optimize.membervaluepropagation.fields.singleton;
-import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,6 +34,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(SingletonFieldValuePropagationEnumWithSubclassTest.class)
.addKeepMainRule(TestClass.class)
+ .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(Characters.class))
.setMinApi(parameters)
.compile()
.inspect(this::inspect)
@@ -44,10 +43,7 @@
}
private void inspect(CodeInspector inspector) {
- ClassSubject charactersClassSubject = inspector.clazz(Characters.class);
- assertThat(charactersClassSubject, isPresent());
- // TODO(b/150368955): Field value propagation should cause Characters.value to become dead.
- assertEquals(1, charactersClassSubject.allInstanceFields().size());
+ assertThat(inspector.clazz(Characters.class), isAbsent());
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java
index ef902bc..25140d0 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/redundantfieldloadelimination/RedundantFinalInstanceFieldLoadAfterStoreTest.java
@@ -102,6 +102,7 @@
static volatile boolean read;
static volatile boolean initialized;
+ @NeverInline
A() {
fork();
waitUntilRead();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
index 93836c1..82ef602 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerTest.java
@@ -56,6 +56,8 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Streams;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
@@ -211,19 +213,26 @@
assertThat(simpleWithThrowingGetter, isAbsent());
// TODO(b/143389508): add support for lazy init in singleton instance getter.
+ List<String> expectedReferencesInTestSimpleWithLazyInit = new ArrayList<>();
+ if (!parameters.canHaveNonReboundConstructorInvoke()) {
+ Collections.addAll(
+ expectedReferencesInTestSimpleWithLazyInit,
+ "DIRECT: void SimpleWithLazyInit.<init>()",
+ "DIRECT: void SimpleWithLazyInit.<init>()");
+ }
+ Collections.addAll(
+ expectedReferencesInTestSimpleWithLazyInit,
+ "STATIC: String SimpleWithLazyInit.bar(String)",
+ "STATIC: String SimpleWithLazyInit.foo()",
+ "STATIC: String TrivialTestClass.next()",
+ "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
+ "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
+ "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
+ "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
+ "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
+ "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE");
assertEquals(
- Lists.newArrayList(
- "DIRECT: void SimpleWithLazyInit.<init>()",
- "DIRECT: void SimpleWithLazyInit.<init>()",
- "STATIC: String SimpleWithLazyInit.bar(String)",
- "STATIC: String SimpleWithLazyInit.foo()",
- "STATIC: String TrivialTestClass.next()",
- "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
- "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
- "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
- "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
- "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE",
- "SimpleWithLazyInit SimpleWithLazyInit.INSTANCE"),
+ expectedReferencesInTestSimpleWithLazyInit,
references(clazz, "testSimpleWithLazyInit", "void"));
ClassSubject simpleWithLazyInit = inspector.clazz(SimpleWithLazyInit.class);
@@ -330,13 +339,19 @@
assertThat(inspector.clazz(HostConflictMethod.class), isPresent());
assertThat(inspector.clazz(CandidateConflictMethod.class), isPresent());
+ List<String> expectedReferencesInTestConflictField = new ArrayList<>();
+ if (!parameters.canHaveNonReboundConstructorInvoke()) {
+ Collections.addAll(
+ expectedReferencesInTestConflictField,
+ "DIRECT: void movetohost.HostConflictField.<init>()");
+ }
+ Collections.addAll(
+ expectedReferencesInTestConflictField,
+ "STATIC: String movetohost.CandidateConflictField.bar(String)",
+ "STATIC: String movetohost.CandidateConflictField.foo()",
+ "STATIC: String movetohost.MoveToHostTestClass.next()");
assertEquals(
- Lists.newArrayList(
- "DIRECT: void movetohost.HostConflictField.<init>()",
- "STATIC: String movetohost.CandidateConflictField.bar(String)",
- "STATIC: String movetohost.CandidateConflictField.foo()",
- "STATIC: String movetohost.MoveToHostTestClass.next()"),
- references(clazz, "testConflictField", "void"));
+ expectedReferencesInTestConflictField, references(clazz, "testConflictField", "void"));
assertThat(inspector.clazz(CandidateConflictMethod.class), isPresent());
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithEscapingPhiOperandTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithEscapingPhiOperandTest.java
new file mode 100644
index 0000000..a7232eb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithEscapingPhiOperandTest.java
@@ -0,0 +1,109 @@
+// Copyright (c) 2023, 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.string;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+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;
+
+/** This is a reproduction of b/280958704. */
+@RunWith(Parameterized.class)
+public class StringBuilderWithEscapingPhiOperandTest extends TestBase {
+
+ private final String EXPECTED = "(Hello World)";
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .addProgramClasses(Main.class)
+ .run(parameters.getRuntime(), Main.class, "Hello World")
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testD8Debug() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .debug()
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), Main.class, "Hello World")
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .release()
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), Main.class, "Hello World")
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters)
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject clazz = inspector.clazz(Main.class);
+ assertThat(clazz, isPresent());
+ assertThat(clazz.uniqueMethodWithOriginalName("storeInField"), isAbsent());
+ })
+ .run(parameters.getRuntime(), Main.class, "Hello World")
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ static class Main {
+
+ static StringBuilder escape;
+
+ public static void main(String[] args) {
+ addString(args[0]);
+ System.out.println(escape);
+ }
+
+ @NeverInline
+ private static void addString(CharSequence sequence) {
+ StringBuilder sb = System.currentTimeMillis() > 0 ? new StringBuilder() : null;
+ if (sb != null) {
+ sb = storeInField(sb);
+ }
+ sb.append("(").append(sequence).append(")");
+ }
+
+ // Inlining this method creates an indirection through an assume node in a separate block.
+ // Adding the store inside the if above will have the assume in the same block.
+ public static StringBuilder storeInField(StringBuilder builder) {
+ return escape = builder;
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithEscapingPhiOperandThroughAssignSideEffectTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithEscapingPhiOperandThroughAssignSideEffectTest.java
new file mode 100644
index 0000000..c480ec5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderWithEscapingPhiOperandThroughAssignSideEffectTest.java
@@ -0,0 +1,91 @@
+// Copyright (c) 2023, 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.string;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+/** This is a reproduction of b/280958704. */
+@RunWith(Parameterized.class)
+public class StringBuilderWithEscapingPhiOperandThroughAssignSideEffectTest extends TestBase {
+
+ private final String EXPECTED = "(Hello World)";
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .addProgramClasses(Main.class)
+ .run(parameters.getRuntime(), Main.class, "Hello World")
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testD8Debug() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .debug()
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), Main.class, "Hello World")
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testD8Release() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .release()
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), Main.class, "Hello World")
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), Main.class, "Hello World")
+ .assertSuccessWithOutputLines(EXPECTED);
+ }
+
+ static class Main {
+
+ static StringBuilder escape;
+
+ public static void main(String[] args) {
+ addString(args[0]);
+ System.out.println(escape);
+ }
+
+ @NeverInline
+ private static void addString(CharSequence sequence) {
+ StringBuilder sb = escape;
+ if (sb == null) {
+ sb = escape = new StringBuilder();
+ }
+ sb.append("(").append(sequence).append(")");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidOrdinalTest.java b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidOrdinalTest.java
index 76be608..51a5eeb 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidOrdinalTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/switches/SwitchMapInvalidOrdinalTest.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.ir.optimize.switches;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -45,6 +46,7 @@
+ "com.android.tools.r8.ir.optimize.switches.SwitchMapInvalidOrdinalTest$MyEnum {"
+ " static <fields>; }")
.addInnerClasses(SwitchMapInvalidOrdinalTest.class)
+ .enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters)
.compile()
@@ -61,6 +63,9 @@
B,
C,
D;
+
+ @NeverInline
+ MyEnum() {}
}
public static class Main {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsInstanceConstructorTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsInstanceConstructorTest.java
index 3132af2..0e5b15d 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsInstanceConstructorTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsInstanceConstructorTest.java
@@ -86,6 +86,7 @@
@NoHorizontalClassMerging
static class A {
+ @NeverInline
public A(B uninstantiated, C unused) {
System.out.print("Hello");
if (uninstantiated != null) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalPackageBoundaryTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalPackageBoundaryTest.java
index 7f7305d..adfe769 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalPackageBoundaryTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceRemovalPackageBoundaryTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -44,6 +45,7 @@
.addInnerClasses(getClass(), UnusedInterfaceRemovalPackageBoundaryTestClasses.class)
.addKeepMainRule(TestClass.class)
.addKeepClassRules(I_CLASS)
+ .enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters)
@@ -83,6 +85,7 @@
@NeverClassInline
static class A implements K {
+ @NeverInline
A() {
System.out.println("A");
}
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index 4d2d2d2..57dcea3 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -30,6 +30,7 @@
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
import com.android.tools.r8.utils.InternalOptions;
+import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
@@ -186,6 +187,11 @@
}
@Override
+ public void set(Collection<Instruction> instructions) {
+ throw new Unimplemented();
+ }
+
+ @Override
public void add(Instruction instruction) {
it.add(instruction);
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
index 253d335..03f1156 100644
--- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
+++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -263,7 +263,7 @@
.allowDiagnosticWarningMessages()
.enableProguardTestOptions()
.addDontObfuscate()
- .setMinApi(testParameters.getApiLevel())
+ .setMinApi(testParameters)
.apply(configuration)
.compile()
.assertAllWarningMessagesMatch(
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 31740b7..12b507d 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -250,7 +250,14 @@
@Test
public void testDataClass() throws Exception {
String mainClassName = "class_inliner_data_class.MainKt";
- runTest("class_inliner_data_class", mainClassName)
+ runTest(
+ "class_inliner_data_class",
+ mainClassName,
+ testBuilder ->
+ testBuilder.addKeepRules(
+ "-neverinline class kotlin.jvm.internal.Intrinsics {",
+ " *** checkNotNullParameter(...);",
+ "}"))
.inspect(
inspector -> {
ClassSubject clazz = inspector.clazz(mainClassName);
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
index 5a7846e..0de5624 100644
--- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -30,16 +30,8 @@
.addProperty("name", "java.lang.String", Visibility.PUBLIC)
.addProperty("age", "int", Visibility.PUBLIC);
- private static final MethodSignature NAME_GETTER_METHOD =
- TEST_DATA_CLASS.getGetterForProperty("name");
- private static final MethodSignature AGE_GETTER_METHOD =
- TEST_DATA_CLASS.getGetterForProperty("age");
-
- private static final MethodSignature COMPONENT1_METHOD =
- TEST_DATA_CLASS.getComponentNFunctionForProperty("name");
private static final MethodSignature COMPONENT2_METHOD =
TEST_DATA_CLASS.getComponentNFunctionForProperty("age");
- private static final MethodSignature COPY_METHOD = TEST_DATA_CLASS.getCopySignature();
private static final MethodSignature COPY_DEFAULT_METHOD =
TEST_DATA_CLASS.getCopyDefaultSignature();
@@ -73,28 +65,7 @@
testBuilder
.addKeepRules(keepClassMethod(mainClassName, testMethodSignature))
.addOptionsModification(disableClassInliner))
- .inspect(
- inspector -> {
- if (allowAccessModification) {
- checkClassIsRemoved(inspector, TEST_DATA_CLASS.getClassName());
- } else {
- ClassSubject dataClass =
- checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
-
- // Getters should be removed after inlining, which is possible only if access is
- // relaxed.
- checkMethodIsKept(dataClass, NAME_GETTER_METHOD);
- checkMethodIsKept(dataClass, AGE_GETTER_METHOD);
-
- // No use of componentN functions.
- checkMethodIsRemoved(dataClass, COMPONENT1_METHOD);
- checkMethodIsRemoved(dataClass, COMPONENT2_METHOD);
-
- // No use of copy functions.
- checkMethodIsRemoved(dataClass, COPY_METHOD);
- checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
- }
- });
+ .inspect(inspector -> checkClassIsRemoved(inspector, TEST_DATA_CLASS.getClassName()));
}
@Test
@@ -109,28 +80,7 @@
testBuilder
.addKeepRules(keepClassMethod(mainClassName, testMethodSignature))
.addOptionsModification(disableClassInliner))
- .inspect(
- inspector -> {
- if (allowAccessModification) {
- checkClassIsRemoved(inspector, TEST_DATA_CLASS.getClassName());
- } else {
- ClassSubject dataClass =
- checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
-
- // ComponentN functions should be removed after inlining, which is possible only if
- // access is relaxed.
- checkMethodIsKept(dataClass, COMPONENT1_METHOD);
- checkMethodIsKept(dataClass, COMPONENT2_METHOD);
-
- // No use of getter.
- checkMethodIsRemoved(dataClass, NAME_GETTER_METHOD);
- checkMethodIsRemoved(dataClass, AGE_GETTER_METHOD);
-
- // No use of copy functions.
- checkMethodIsRemoved(dataClass, COPY_METHOD);
- checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
- }
- });
+ .inspect(inspector -> checkClassIsRemoved(inspector, TEST_DATA_CLASS.getClassName()));
}
@Test
@@ -147,32 +97,11 @@
.addOptionsModification(disableClassInliner))
.inspect(
inspector -> {
- if (allowAccessModification) {
- checkClassIsRemoved(inspector, TEST_DATA_CLASS.getClassName());
- } else {
- ClassSubject dataClass =
- checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
- checkMethodIsKept(dataClass, COMPONENT2_METHOD);
-
- // Function component1 is not used.
- checkMethodIsRemoved(dataClass, COMPONENT1_METHOD);
-
- // No use of getter.
- checkMethodIsRemoved(dataClass, NAME_GETTER_METHOD);
- checkMethodIsRemoved(dataClass, AGE_GETTER_METHOD);
-
- // No use of copy functions.
- checkMethodIsRemoved(dataClass, COPY_METHOD);
- checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
- }
+ checkClassIsRemoved(inspector, TEST_DATA_CLASS.getClassName());
ClassSubject classSubject = checkClassIsKept(inspector, mainClassName);
MethodSubject testMethod = checkMethodIsKept(classSubject, testMethodSignature);
- if (allowAccessModification) {
- checkMethodIsNeverInvoked(testMethod, COMPONENT2_METHOD);
- } else {
- checkMethodIsInvokedAtLeastOnce(testMethod, COMPONENT2_METHOD);
- }
+ checkMethodIsNeverInvoked(testMethod, COMPONENT2_METHOD);
});
}
@@ -188,17 +117,7 @@
testBuilder
.addKeepRules(keepClassMethod(mainClassName, testMethodSignature))
.addOptionsModification(disableClassInliner))
- .inspect(
- inspector -> {
- if (allowAccessModification) {
- checkClassIsRemoved(inspector, TEST_DATA_CLASS.getClassName());
- } else {
- ClassSubject dataClass =
- checkClassIsKept(inspector, TEST_DATA_CLASS.getClassName());
- checkMethodIsRemoved(dataClass, COPY_METHOD);
- checkMethodIsRemoved(dataClass, COPY_DEFAULT_METHOD);
- }
- });
+ .inspect(inspector -> checkClassIsRemoved(inspector, TEST_DATA_CLASS.getClassName()));
}
@Test
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveVirtualBridgeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveVirtualBridgeTest.java
index 3266b5d..0ca6131 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveVirtualBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveVirtualBridgeTest.java
@@ -11,13 +11,13 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoInliningOfDefaultInitializer;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
import com.android.tools.r8.utils.codeinspector.HorizontallyMergedClassesInspector;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,6 +49,7 @@
.setMinApi(parameters)
.addKeepMainRule(newMainTypeName)
.enableInliningAnnotations()
+ .enableNoInliningOfDefaultInitializerAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
.addHorizontallyMergedClassesInspector(
@@ -59,9 +60,12 @@
inspector -> {
ClassSubject clazz = inspector.clazz(B.class);
assertThat(clazz, isPresent());
- assertEquals(1, clazz.allMethods().size());
- FoundMethodSubject foundMethodSubject = clazz.allMethods().get(0);
- assertThat(foundMethodSubject, isInstanceInitializer());
+ if (parameters.canHaveNonReboundConstructorInvoke()) {
+ assertEquals(0, clazz.allMethods().size());
+ } else {
+ assertEquals(1, clazz.allMethods().size());
+ assertThat(clazz.allMethods().get(0), isInstanceInitializer());
+ }
});
}
@@ -75,6 +79,7 @@
}
@NeverClassInline
+ @NoInliningOfDefaultInitializer
public static class B extends A {}
public static class /* a.Main */ Main {
diff --git a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
index 92404f0..8c3f9dc 100644
--- a/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
+++ b/src/test/java/com/android/tools/r8/naming/AdaptResourceFileNamesTest.java
@@ -101,6 +101,7 @@
" void <init>();",
"}",
"-neverclassinline class *",
+ "-neverinline @adaptresourcefilenames.NoInliningOfDefaultInitializer class * { <init>(); }",
"-nohorizontalclassmerging class adaptresourcefilenames.pkg.C",
"-nohorizontalclassmerging class adaptresourcefilenames.pkg.innerpkg.D");
}
diff --git a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
index d94ad7b..b6ef1a4 100644
--- a/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ProguardMapReaderTest.java
@@ -12,29 +12,106 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
-import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.naming.ProguardMapReader.ParseException;
import com.android.tools.r8.position.Position;
+import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.List;
import org.junit.Assert;
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 ProguardMapReaderTest extends TestBase {
- private static final String ROOT = ToolHelper.EXAMPLES_BUILD_DIR;
- private static final String EXAMPLE_MAP = "throwing/throwing.map";
private static final String EXAMPLE_MAP_WITH_PACKAGE_INFO =
"dagger.android.package-info -> dagger.android.package-info:\n";
- @Test
- public void parseThrowingMap() throws IOException {
- ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP));
+ private static List<String> EXAMPLE_MAP =
+ ImmutableList.of(
+ "throwing.Overloaded -> throwing.a:",
+ " 8:8:void <init>() -> <init>",
+ " 11:11:int aMethod(int) -> a",
+ " 15:15:int conflictingMethod(int) -> b",
+ " int bMethod(double) -> a",
+ " 21:21:int conflictingMethod(double) -> b",
+ " 25:25:int cMethod(boolean) -> a",
+ " 29:29:int conflictingMethod(boolean) -> b",
+ " 33:33:int anotherConflict(boolean) -> c",
+ " 37:37:int unique(java.util.List) -> a",
+ "throwing.RenamedClass -> throwing.b:",
+ " java.util.List list -> a",
+ " 18:19:void <init>() -> <init>",
+ " 22:25:java.util.List getList() -> a",
+ " 29:34:void setList(java.util.List) -> a",
+ " 38:43:void swap(java.util.List) -> b",
+ "throwing.Throwing -> throwing.Throwing:",
+ " int[] used -> a",
+ " 13:13:void <init>() -> <init>",
+ " 19:93:void main(java.lang.String[]) -> main",
+ " 1115:1116:int throwAtFistLine(int):115:116 -> main",
+ " 1115:1116:void main(java.lang.String[]):19 -> main",
+ " 1121:1125:int throwInMiddle(int):121:125 -> main",
+ " 1121:1125:void main(java.lang.String[]):24 -> main",
+ " 1130:1136:int throwAfterMultiInline(int):130:136 -> main",
+ " 1130:1136:void main(java.lang.String[]):29 -> main",
+ " 1195:1195:int anotherInlinedFunction(int):195:195 -> main",
+ " 1195:1195:int throwAfterMultiInline(int):131 -> main",
+ " 1195:1195:void main(java.lang.String[]):29 -> main",
+ " 2195:2195:int anotherInlinedFunction(int):195:195 -> main",
+ " 2195:2195:void main(java.lang.String[]):37 -> main",
+ " 2207:2207:int throwing.Throwing$Nested.justThrow(int):207:207 -> main",
+ " 2207:2207:void main(java.lang.String[]):49 -> main",
+ " 2212:2213:void throwing.Throwing$Nested.doSomethingUseless():212:213 -> main",
+ " 2212:2213:void main(java.lang.String[]):54 -> main",
+ " 3046:3048:throwing.RenamedClass throwing.RenamedClass.create():46:48 -> main",
+ " 3046:3048:void main(java.lang.String[]):59 -> main",
+ " 3052:3058:void throwing.RenamedClass.takeThingsForASpin(int):52:58 -> main",
+ " 3052:3058:void main(java.lang.String[]):60 -> main",
+ " 3188:3190:int aFunctionsThatThrowsBeforeAnInlinedMethod(int):188:190 -> main",
+ " 3188:3190:void main(java.lang.String[]):89 -> main",
+ " 96:99:int magicNumber(int) -> a",
+ " 103:111:void printFrameHead(java.lang.Exception) -> a",
+ " 140:146:int throwInAFunctionThatIsNotInlinedAndCalledTwice() -> a",
+ " 158:165:int aFunctionThatCallsAnInlinedMethodThatThrows(java.util.List) -> a",
+ " 4151:4152:int anotherThrowingMethodToInline(int):151:152 -> a",
+ " 4151:4152:int aFunctionThatCallsAnInlinedMethodThatThrows(java.util.List):164 -> a",
+ " 177:184:int anotherFunctionThatCallsAnInlinedMethodThatThrows(java.lang.String) ->"
+ + " a",
+ " 4170:4171:int yetAnotherThrowingMethodToInline(int):170:171 -> a",
+ " 4170:4171:int"
+ + " anotherFunctionThatCallsAnInlinedMethodThatThrows(java.lang.String):183 -> a",
+ " 15:15:void <clinit>() -> <clinit>",
+ "throwing.Throwing$Nested -> throwing.c:",
+ " 204:204:void <init>() -> <init>",
+ " 216:219:int callAMethod(throwing.Throwing$Nested,int) -> a",
+ " 224:227:int aMethod(int) -> a");
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return TestParameters.builder().withNoneRuntime().build();
+ }
+
+ private Path mapFile = null;
+
+ public ProguardMapReaderTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ private Path ensureMapFile() throws IOException {
+ if (mapFile == null) {
+ mapFile = temp.newFolder().toPath().resolve("mapping.map");
+ FileUtils.writeTextFile(mapFile, EXAMPLE_MAP);
+ }
+ return mapFile;
}
@Test
@@ -53,11 +130,9 @@
ClassNameMapper.mapperFromString(mapping);
}
-
@Test
public void roundTripTest() throws IOException {
- ClassNameMapper firstMapper =
- ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP)).sorted();
+ ClassNameMapper firstMapper = ClassNameMapper.mapperFromFile(ensureMapFile()).sorted();
ClassNameMapper secondMapper =
ClassNameMapper.mapperFromString(firstMapper.toString()).sorted();
Assert.assertEquals(firstMapper, secondMapper);
@@ -65,14 +140,13 @@
@Test
public void roundTripTestWithLeadingBOM() throws IOException {
- ClassNameMapper firstMapper =
- ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP)).sorted();
+ ClassNameMapper firstMapper = ClassNameMapper.mapperFromFile(ensureMapFile()).sorted();
assertTrue(firstMapper.toString().charAt(0) != StringUtils.BOM);
ClassNameMapper secondMapper =
ClassNameMapper.mapperFromString(StringUtils.BOM + firstMapper.toString()).sorted();
assertTrue(secondMapper.toString().charAt(0) != StringUtils.BOM);
Assert.assertEquals(firstMapper, secondMapper);
- byte[] bytes = Files.readAllBytes(Paths.get(ROOT, EXAMPLE_MAP));
+ byte[] bytes = Files.readAllBytes(ensureMapFile());
assertNotEquals(0xef, Byte.toUnsignedLong(bytes[0]));
Path mapFileWithBOM = writeTextToTempFile(StringUtils.BOM + firstMapper.toString());
bytes = Files.readAllBytes(mapFileWithBOM);
@@ -96,8 +170,7 @@
"" + StringUtils.BOM,
StringUtils.BOM + " " + StringUtils.BOM);
for (String whitespace : ws) {
- ClassNameMapper firstMapper =
- ClassNameMapper.mapperFromFile(Paths.get(ROOT, EXAMPLE_MAP)).sorted();
+ ClassNameMapper firstMapper = ClassNameMapper.mapperFromFile(ensureMapFile()).sorted();
assertTrue(firstMapper.toString().charAt(0) != StringUtils.BOM);
StringBuilder buildWithWhitespace = new StringBuilder();
char prevChar = '\0';
@@ -121,7 +194,7 @@
ClassNameMapper.mapperFromString(buildWithWhitespace.toString()).sorted();
assertFalse(firstMapper.toString().contains("" + StringUtils.BOM));
Assert.assertEquals(firstMapper, secondMapper);
- byte[] bytes = Files.readAllBytes(Paths.get(ROOT, EXAMPLE_MAP));
+ byte[] bytes = Files.readAllBytes(ensureMapFile());
assertNotEquals(0xef, Byte.toUnsignedLong(bytes[0]));
Path mapFileWithBOM = writeTextToTempFile(StringUtils.BOM + firstMapper.toString());
ClassNameMapper thirdMapper = ClassNameMapper.mapperFromFile(mapFileWithBOM).sorted();
diff --git a/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersNullValueTest.java b/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersNullValueTest.java
new file mode 100644
index 0000000..e6a1197
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersNullValueTest.java
@@ -0,0 +1,110 @@
+// Copyright (c) 2023, 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.methodparameters;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRunResult;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
+import com.android.tools.r8.utils.StringUtils;
+import java.lang.reflect.MalformedParametersException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+/** Regression test for b/281536562 */
+@RunWith(Parameterized.class)
+public class MethodParametersNullValueTest extends TestBase {
+
+ static final String EXPECTED = StringUtils.lines("bar", "arg1", "baz");
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ public MethodParametersNullValueTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .addProgramClassFileData(getTransformedTestClass())
+ .run(parameters.getRuntime(), TestClass.class)
+ .apply(this::checkExpected);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(getTransformedTestClass())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .apply(this::checkExpected);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ parameters.assumeR8TestParameters();
+ // Don't run on old API as that build is "ill configured" and triggers missing type refs.
+ assumeTrue(
+ parameters.isCfRuntime()
+ || parameters
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(apiLevelWithMethodParametersSupport()));
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(getTransformedTestClass())
+ .setMinApi(parameters)
+ .addKeepClassAndMembersRules(TestClass.class)
+ .addKeepAllAttributes()
+ .run(parameters.getRuntime(), TestClass.class)
+ .apply(this::checkExpected);
+ }
+
+ private void checkExpected(TestRunResult<?> result) {
+ if (parameters.isCfRuntime(CfVm.JDK8)) {
+ // JDK8 will throw when accessing a null valued method parameter name.
+ result.assertFailureWithErrorThatThrows(MalformedParametersException.class);
+ } else if (parameters.isDexRuntimeVersionOlderThanOrEqual(Version.V7_0_0)) {
+ // API 26 introduced the java.lang.reflect.Parameter and methods.
+ result.assertFailureWithErrorThatThrows(NoSuchMethodError.class);
+ } else {
+ result.assertSuccessWithOutput(EXPECTED);
+ }
+ }
+
+ private byte[] getTransformedTestClass() throws Exception {
+ return transformer(TestClass.class)
+ .setMethodParameters(MethodPredicate.onName("foo"), "bar", null, "baz")
+ .transform();
+ }
+
+ static class TestClass {
+
+ public static void foo(String bar, String willBeNull, String baz) {
+ System.out.println("" + bar + willBeNull + baz);
+ }
+
+ public static void main(String[] args) {
+ for (Method method : TestClass.class.getMethods()) {
+ if (method.getName().equals("foo")) {
+ for (Parameter parameter : method.getParameters()) {
+ System.out.println(parameter.getName());
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersTest.java b/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersTest.java
index 04ec6b4..f102db0 100644
--- a/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersTest.java
+++ b/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersTest.java
@@ -17,9 +17,12 @@
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.shaking.ProguardKeepAttributes;
+import com.android.tools.r8.transformers.ClassFileTransformer.MethodPredicate;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.BooleanUtils;
import java.io.IOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.ExecutionException;
@@ -41,7 +44,7 @@
return buildParameters(
getTestParameters()
.withCfRuntimes()
- .withDexRuntimesStartingFromIncluding(Version.V8_1_0)
+ .withDexRuntimesStartingFromExcluding(Version.V7_0_0)
.build(),
BooleanUtils.values());
}
@@ -55,18 +58,18 @@
public void testKeepingMethodParametersR8() throws Exception {
R8TestRunResult runResult =
testForR8(parameters.getBackend())
- .addProgramClassFileData(MethodParametersTestDump.dump())
- .addKeepClassAndMembersRulesWithAllowObfuscation(MethodParametersTest.class)
- .addKeepMainRule(MethodParametersTest.class)
+ .addProgramClassFileData(getTransformedTestClass())
+ .addKeepClassAndMembersRulesWithAllowObfuscation(TestClass.class)
+ .addKeepMainRule(TestClass.class)
.addKeepAttributeSourceFile()
.applyIf(
keepMethodParameters,
builder -> builder.addKeepAttributes(ProguardKeepAttributes.METHOD_PARAMETERS))
.setMinApi(keepMethodParameters ? AndroidApiLevel.O : AndroidApiLevel.L)
// java.lang.reflect.Parameter was introduced in API level 26 (O).
- .addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.O))
+ .addLibraryFiles(ToolHelper.getAndroidJar(apiLevelWithMethodParametersSupport()))
.compile()
- .run(parameters.getRuntime(), MethodParametersTest.class);
+ .run(parameters.getRuntime(), TestClass.class);
if (keepMethodParameters) {
checkOutputContainsAll(runResult.getStdOut());
} else {
@@ -81,9 +84,9 @@
assumeTrue(parameters.getBackend() == Backend.DEX);
D8TestRunResult runResult =
testForD8()
- .addProgramClassFileData(MethodParametersTestDump.dump())
+ .addProgramClassFileData(getTransformedTestClass())
.setMinApi(keepMethodParameters ? AndroidApiLevel.O : AndroidApiLevel.L)
- .run(parameters.getRuntime(), MethodParametersTest.class);
+ .run(parameters.getRuntime(), TestClass.class);
checkOutputContainsAll(runResult.getStdOut());
}
@@ -94,4 +97,26 @@
private void checkOutputNotContainsAll(String stdOut) {
Arrays.asList(EXPECTED).forEach(expected -> assertThat(stdOut, not(containsString(expected))));
}
+
+ private byte[] getTransformedTestClass() throws IOException {
+ return transformer(TestClass.class)
+ .setMethodParameters(MethodPredicate.onName("main"), "hello")
+ .setMethodParameters(MethodPredicate.onName("other"), "darkness", "my", "old", "friend")
+ .transform();
+ }
+
+ public static class TestClass {
+
+ public static void main(String[] hello) {
+ for (Method method : TestClass.class.getMethods()) {
+ for (Parameter parameter : method.getParameters()) {
+ System.out.println(method.getName() + ": " + parameter.getName());
+ }
+ }
+ }
+
+ public void other(int darkness, String my, Object old, boolean friend) {
+ // nothing to do, reflectively accessed...
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersTestDump.java b/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersTestDump.java
deleted file mode 100644
index f5ab8a6..0000000
--- a/src/test/java/com/android/tools/r8/naming/methodparameters/MethodParametersTestDump.java
+++ /dev/null
@@ -1,220 +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.naming.methodparameters;
-
-import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodVisitor;
-import org.objectweb.asm.Opcodes;
-import org.objectweb.asm.Type;
-
-public class MethodParametersTestDump implements Opcodes {
-
- /* The below dump was produced by the asmifier on following java code:
-
- import java.lang.reflect.Method;
- import java.lang.reflect.Parameter;
-
- public class MethodParametersTest {
-
- public static void main(String... hello) {
- for (Method method : MethodParametersTest.class.getMethods()) {
- for (Parameter parameter : method.getParameters()) {
- System.out.println(method.getName() + ": " + parameter.getName());
- }
- }
- }
-
- public void other(int darkness, String my, Object old, boolean friend) {
-
- }
- }
- */
-
- public static byte[] dump() {
-
- ClassWriter classWriter = new ClassWriter(0);
- MethodVisitor methodVisitor;
-
- classWriter.visit(
- V1_8,
- ACC_PUBLIC | ACC_SUPER,
- "com/android/tools/r8/naming/methodparameters/MethodParametersTest",
- null,
- "java/lang/Object",
- null);
-
- classWriter.visitSource("MethodParametersTest.java", null);
-
- {
- methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
- methodVisitor.visitCode();
- Label label0 = new Label();
- methodVisitor.visitLabel(label0);
- methodVisitor.visitLineNumber(4, label0);
- methodVisitor.visitVarInsn(ALOAD, 0);
- methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
- methodVisitor.visitInsn(RETURN);
- methodVisitor.visitMaxs(1, 1);
- methodVisitor.visitEnd();
- }
- {
- methodVisitor =
- classWriter.visitMethod(
- ACC_PUBLIC | ACC_STATIC | ACC_VARARGS, "main", "([Ljava/lang/String;)V", null, null);
- methodVisitor.visitParameter("hello", 0);
- methodVisitor.visitCode();
- Label label0 = new Label();
- methodVisitor.visitLabel(label0);
- methodVisitor.visitLineNumber(7, label0);
- methodVisitor.visitLdcInsn(
- Type.getType("Lcom/android/tools/r8/naming/methodparameters/MethodParametersTest;"));
- methodVisitor.visitMethodInsn(
- INVOKEVIRTUAL, "java/lang/Class", "getMethods", "()[Ljava/lang/reflect/Method;", false);
- methodVisitor.visitVarInsn(ASTORE, 1);
- methodVisitor.visitVarInsn(ALOAD, 1);
- methodVisitor.visitInsn(ARRAYLENGTH);
- methodVisitor.visitVarInsn(ISTORE, 2);
- methodVisitor.visitInsn(ICONST_0);
- methodVisitor.visitVarInsn(ISTORE, 3);
- Label label1 = new Label();
- methodVisitor.visitLabel(label1);
- methodVisitor.visitFrame(
- Opcodes.F_APPEND,
- 3,
- new Object[] {"[Ljava/lang/reflect/Method;", Opcodes.INTEGER, Opcodes.INTEGER},
- 0,
- null);
- methodVisitor.visitVarInsn(ILOAD, 3);
- methodVisitor.visitVarInsn(ILOAD, 2);
- Label label2 = new Label();
- methodVisitor.visitJumpInsn(IF_ICMPGE, label2);
- methodVisitor.visitVarInsn(ALOAD, 1);
- methodVisitor.visitVarInsn(ILOAD, 3);
- methodVisitor.visitInsn(AALOAD);
- methodVisitor.visitVarInsn(ASTORE, 4);
- Label label3 = new Label();
- methodVisitor.visitLabel(label3);
- methodVisitor.visitLineNumber(8, label3);
- methodVisitor.visitVarInsn(ALOAD, 4);
- methodVisitor.visitMethodInsn(
- INVOKEVIRTUAL,
- "java/lang/reflect/Method",
- "getParameters",
- "()[Ljava/lang/reflect/Parameter;",
- false);
- methodVisitor.visitVarInsn(ASTORE, 5);
- methodVisitor.visitVarInsn(ALOAD, 5);
- methodVisitor.visitInsn(ARRAYLENGTH);
- methodVisitor.visitVarInsn(ISTORE, 6);
- methodVisitor.visitInsn(ICONST_0);
- methodVisitor.visitVarInsn(ISTORE, 7);
- Label label4 = new Label();
- methodVisitor.visitLabel(label4);
- methodVisitor.visitFrame(
- Opcodes.F_FULL,
- 8,
- new Object[] {
- "[Ljava/lang/String;",
- "[Ljava/lang/reflect/Method;",
- Opcodes.INTEGER,
- Opcodes.INTEGER,
- "java/lang/reflect/Method",
- "[Ljava/lang/reflect/Parameter;",
- Opcodes.INTEGER,
- Opcodes.INTEGER
- },
- 0,
- new Object[] {});
- methodVisitor.visitVarInsn(ILOAD, 7);
- methodVisitor.visitVarInsn(ILOAD, 6);
- Label label5 = new Label();
- methodVisitor.visitJumpInsn(IF_ICMPGE, label5);
- methodVisitor.visitVarInsn(ALOAD, 5);
- methodVisitor.visitVarInsn(ILOAD, 7);
- methodVisitor.visitInsn(AALOAD);
- methodVisitor.visitVarInsn(ASTORE, 8);
- Label label6 = new Label();
- methodVisitor.visitLabel(label6);
- methodVisitor.visitLineNumber(9, label6);
- methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
- methodVisitor.visitTypeInsn(NEW, "java/lang/StringBuilder");
- methodVisitor.visitInsn(DUP);
- methodVisitor.visitMethodInsn(
- INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
- methodVisitor.visitVarInsn(ALOAD, 4);
- methodVisitor.visitMethodInsn(
- INVOKEVIRTUAL, "java/lang/reflect/Method", "getName", "()Ljava/lang/String;", false);
- methodVisitor.visitMethodInsn(
- INVOKEVIRTUAL,
- "java/lang/StringBuilder",
- "append",
- "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
- false);
- methodVisitor.visitLdcInsn(": ");
- methodVisitor.visitMethodInsn(
- INVOKEVIRTUAL,
- "java/lang/StringBuilder",
- "append",
- "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
- false);
- methodVisitor.visitVarInsn(ALOAD, 8);
- methodVisitor.visitMethodInsn(
- INVOKEVIRTUAL, "java/lang/reflect/Parameter", "getName", "()Ljava/lang/String;", false);
- methodVisitor.visitMethodInsn(
- INVOKEVIRTUAL,
- "java/lang/StringBuilder",
- "append",
- "(Ljava/lang/String;)Ljava/lang/StringBuilder;",
- false);
- methodVisitor.visitMethodInsn(
- INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
- methodVisitor.visitMethodInsn(
- INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
- Label label7 = new Label();
- methodVisitor.visitLabel(label7);
- methodVisitor.visitLineNumber(8, label7);
- methodVisitor.visitIincInsn(7, 1);
- methodVisitor.visitJumpInsn(GOTO, label4);
- methodVisitor.visitLabel(label5);
- methodVisitor.visitLineNumber(7, label5);
- methodVisitor.visitFrame(
- Opcodes.F_FULL,
- 4,
- new Object[] {
- "[Ljava/lang/String;", "[Ljava/lang/reflect/Method;", Opcodes.INTEGER, Opcodes.INTEGER
- },
- 0,
- new Object[] {});
- methodVisitor.visitIincInsn(3, 1);
- methodVisitor.visitJumpInsn(GOTO, label1);
- methodVisitor.visitLabel(label2);
- methodVisitor.visitLineNumber(12, label2);
- methodVisitor.visitFrame(Opcodes.F_CHOP, 3, null, 0, null);
- methodVisitor.visitInsn(RETURN);
- methodVisitor.visitMaxs(3, 9);
- methodVisitor.visitEnd();
- }
- {
- methodVisitor =
- classWriter.visitMethod(
- ACC_PUBLIC, "other", "(ILjava/lang/String;Ljava/lang/Object;Z)V", null, null);
- methodVisitor.visitParameter("darkness", 0);
- methodVisitor.visitParameter("my", 0);
- methodVisitor.visitParameter("old", 0);
- methodVisitor.visitParameter("friend", 0);
- methodVisitor.visitCode();
- Label label0 = new Label();
- methodVisitor.visitLabel(label0);
- methodVisitor.visitLineNumber(16, label0);
- methodVisitor.visitInsn(RETURN);
- methodVisitor.visitMaxs(0, 5);
- methodVisitor.visitEnd();
- }
- classWriter.visitEnd();
-
- return classWriter.toByteArray();
- }
-}
diff --git a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithInstanceInitializerCollisionTest.java b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithInstanceInitializerCollisionTest.java
index 1910397..57dddce 100644
--- a/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithInstanceInitializerCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/proto/ProtoNormalizationWithInstanceInitializerCollisionTest.java
@@ -7,6 +7,7 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -36,6 +37,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters)
.compile()
@@ -74,11 +76,13 @@
new Main(new B(), new A());
}
+ @NeverInline
Main(A a, B b) {
System.out.println(a);
System.out.println(b);
}
+ @NeverInline
Main(B b, A a) {
System.out.println(a);
System.out.println(b);
diff --git a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
index 385f0e1..5e7f89b 100644
--- a/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
+++ b/src/test/java/com/android/tools/r8/regress/b69825683/Regress69825683Test.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.regress.b69825683;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -47,6 +48,7 @@
testForR8(parameters.getBackend())
.addProgramFiles(ToolHelper.getClassFilesForTestPackage(outer.getPackage()))
.addKeepMainRule(outer)
+ .enableInliningAnnotations()
.enableSideEffectAnnotations()
.addKeepRules(
"-assumemayhavesideeffects class " + inner.getName() + " {",
@@ -95,8 +97,10 @@
.inspector();
List<FoundClassSubject> classes = inspector.allClasses();
- assertEquals(2, classes.size());
+ assertEquals(parameters.canInitNewInstanceUsingSuperclassConstructor() ? 1 : 2, classes.size());
assertThat(inspector.clazz(clazz), isPresent());
- assertThat(inspector.clazz(innerClass), isPresent());
+ assertThat(
+ inspector.clazz(innerClass),
+ notIf(isPresent(), parameters.canInitNewInstanceUsingSuperclassConstructor()));
}
}
diff --git a/src/test/java/com/android/tools/r8/regress/b69825683/outerconstructsinner/Outer.java b/src/test/java/com/android/tools/r8/regress/b69825683/outerconstructsinner/Outer.java
index dfaf6f9..3f8e5c2 100644
--- a/src/test/java/com/android/tools/r8/regress/b69825683/outerconstructsinner/Outer.java
+++ b/src/test/java/com/android/tools/r8/regress/b69825683/outerconstructsinner/Outer.java
@@ -5,10 +5,12 @@
package com.android.tools.r8.regress.b69825683.outerconstructsinner;
import com.android.tools.r8.AssumeMayHaveSideEffects;
+import com.android.tools.r8.NeverInline;
public class Outer {
@AssumeMayHaveSideEffects
+ @NeverInline
public Outer() {
new Inner();
}
@@ -16,12 +18,13 @@
public class Inner {
@AssumeMayHaveSideEffects
+ @NeverInline
private Inner() {}
}
- public static void main(String args[]) {
+ public static void main(String[] args) {
new Outer();
- for (java.lang.reflect.Constructor m : Outer.Inner.class.getDeclaredConstructors()) {
+ for (java.lang.reflect.Constructor<?> m : Outer.Inner.class.getDeclaredConstructors()) {
System.out.println(m.getName());
}
}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageFeatureWithSyntheticsTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageFeatureWithSyntheticsTest.java
index ee5e6d7..59f4dc4 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageFeatureWithSyntheticsTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageFeatureWithSyntheticsTest.java
@@ -93,6 +93,7 @@
Reference.methodFromMethod(TestClass.class.getDeclaredMethod("run", I.class)))
.addKeepAttributeInnerClassesAndEnclosingMethod()
.apply(this::configureRepackaging)
+ .enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.setMinApi(parameters)
.compile();
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithCollisionsTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithCollisionsTest.java
index 2001e8a..e936dd7 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithCollisionsTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithCollisionsTest.java
@@ -59,6 +59,8 @@
.addKeepMainRule(TestClass.class)
.addKeepRules("-keep class " + getRepackagePackage() + ".** { *; }")
.addKeepAttributeInnerClassesAndEnclosingMethod()
+ .addOptionsModification(
+ options -> options.inlinerOptions().setEnableConstructorInlining(false))
.apply(this::configureRepackaging)
.enableNeverClassInliningAnnotations()
.setMinApi(parameters)
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagefeaturewithsynthetics/first/Foo.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagefeaturewithsynthetics/first/Foo.java
index e7b1cf9..3d09582 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagefeaturewithsynthetics/first/Foo.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagefeaturewithsynthetics/first/Foo.java
@@ -5,11 +5,13 @@
package com.android.tools.r8.repackage.testclasses.repackagefeaturewithsynthetics.first;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.repackage.RepackageFeatureWithSyntheticsTest.TestClass;
@NeverClassInline
public class Foo {
+ @NeverInline
public Foo() {
TestClass.run(() -> PkgProtectedMethod.getStream().println("first.Foo"));
}
diff --git a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagefeaturewithsynthetics/first/first/Foo.java b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagefeaturewithsynthetics/first/first/Foo.java
index dcee445..1dcc2ca 100644
--- a/src/test/java/com/android/tools/r8/repackage/testclasses/repackagefeaturewithsynthetics/first/first/Foo.java
+++ b/src/test/java/com/android/tools/r8/repackage/testclasses/repackagefeaturewithsynthetics/first/first/Foo.java
@@ -5,11 +5,13 @@
package com.android.tools.r8.repackage.testclasses.repackagefeaturewithsynthetics.first.first;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.repackage.RepackageFeatureWithSyntheticsTest.TestClass;
@NeverClassInline
public class Foo {
+ @NeverInline
public Foo() {
TestClass.run(() -> PkgProtectedMethod.getStream().println("first.first.Foo"));
}
diff --git a/src/test/java/com/android/tools/r8/resolution/B77944861.java b/src/test/java/com/android/tools/r8/resolution/b77944861/B77944861.java
similarity index 79%
rename from src/test/java/com/android/tools/r8/resolution/B77944861.java
rename to src/test/java/com/android/tools/r8/resolution/b77944861/B77944861.java
index 5e007b7..4e1ae5c 100644
--- a/src/test/java/com/android/tools/r8/resolution/B77944861.java
+++ b/src/test/java/com/android/tools/r8/resolution/b77944861/B77944861.java
@@ -1,7 +1,7 @@
// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.resolution;
+package com.android.tools.r8.resolution.b77944861;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -11,17 +11,15 @@
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.ToolHelper;
-import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.resolution.b77944861.inner.TopLevelPolicy;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.FieldAccessInstructionSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.Iterator;
+import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -31,12 +29,9 @@
@RunWith(Parameterized.class)
public class B77944861 extends TestBase {
- private static final String MAIN = "regress_77944861.SomeView";
+ private static final Class MAIN = SomeView.class;
private static final String EXPECTED_OUTPUT = StringUtils.lines("foo");
- private static final Path PRG =
- Paths.get(ToolHelper.EXAMPLES_BUILD_DIR + "regress_77944861" + FileUtils.JAR_EXTENSION);
-
@Parameter(0)
public TestParameters parameters;
@@ -45,11 +40,20 @@
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
+ private List<Class<?>> getTestClasses() throws Exception {
+ return ImmutableList.of(
+ MAIN,
+ TopLevelPolicy.class,
+ TopLevelPolicy.MobileIconState.class,
+ Class.forName(typeName(TopLevelPolicy.class) + "$1"),
+ Class.forName(typeName(TopLevelPolicy.class) + "$IconState"));
+ }
+
@Test
public void testJvm() throws Exception {
parameters.assumeJvmTestParameters();
testForJvm(parameters)
- .addProgramFiles(PRG)
+ .addProgramClasses(getTestClasses())
.run(parameters.getRuntime(), MAIN)
.assertSuccessWithOutput(EXPECTED_OUTPUT);
}
@@ -57,7 +61,7 @@
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
- .addProgramFiles(PRG)
+ .addProgramClasses(getTestClasses())
.addKeepMainRule(MAIN)
.addDontObfuscate()
.addDontShrink()
@@ -65,9 +69,9 @@
.compile()
.inspect(
inspector -> {
- ClassSubject view = inspector.clazz("regress_77944861.SomeView");
+ ClassSubject view = inspector.clazz(MAIN);
assertThat(view, isPresent());
- String className = "regress_77944861.inner.TopLevelPolicy$MobileIconState";
+ String className = typeName(TopLevelPolicy.MobileIconState.class);
MethodSubject initView =
view.method("java.lang.String", "get", ImmutableList.of(className));
assertThat(initView, isPresent());
diff --git a/src/test/examples/regress_77944861/SomeView.java b/src/test/java/com/android/tools/r8/resolution/b77944861/SomeView.java
similarity index 73%
rename from src/test/examples/regress_77944861/SomeView.java
rename to src/test/java/com/android/tools/r8/resolution/b77944861/SomeView.java
index 483dd77..6ccd41e 100644
--- a/src/test/examples/regress_77944861/SomeView.java
+++ b/src/test/java/com/android/tools/r8/resolution/b77944861/SomeView.java
@@ -1,10 +1,10 @@
// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_77944861;
+package com.android.tools.r8.resolution.b77944861;
-import regress_77944861.inner.TopLevelPolicy;
-import regress_77944861.inner.TopLevelPolicy.MobileIconState;
+import com.android.tools.r8.resolution.b77944861.inner.TopLevelPolicy;
+import com.android.tools.r8.resolution.b77944861.inner.TopLevelPolicy.MobileIconState;
public class SomeView {
diff --git a/src/test/examples/regress_77944861/inner/TopLevelPolicy.java b/src/test/java/com/android/tools/r8/resolution/b77944861/inner/TopLevelPolicy.java
similarity index 91%
rename from src/test/examples/regress_77944861/inner/TopLevelPolicy.java
rename to src/test/java/com/android/tools/r8/resolution/b77944861/inner/TopLevelPolicy.java
index 3fc97e0..0c22769 100644
--- a/src/test/examples/regress_77944861/inner/TopLevelPolicy.java
+++ b/src/test/java/com/android/tools/r8/resolution/b77944861/inner/TopLevelPolicy.java
@@ -1,7 +1,7 @@
// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
-package regress_77944861.inner;
+package com.android.tools.r8.resolution.b77944861.inner;
public class TopLevelPolicy {
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index d06cbe6..9f82884 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.retrace.stacktraces.ActualBotStackTraceBase;
import com.android.tools.r8.retrace.stacktraces.ActualIdentityStackTrace;
import com.android.tools.r8.retrace.stacktraces.ActualRetraceBotStackTrace;
+import com.android.tools.r8.retrace.stacktraces.AmbiguousInlineFramesStackTrace;
import com.android.tools.r8.retrace.stacktraces.AmbiguousMethodVerboseStackTrace;
import com.android.tools.r8.retrace.stacktraces.AmbiguousMissingLineStackTrace;
import com.android.tools.r8.retrace.stacktraces.AmbiguousStackTrace;
@@ -215,6 +216,11 @@
}
@Test
+ public void testAmbiguousInlineFramesStackTrace() throws Exception {
+ runRetraceTest(new AmbiguousInlineFramesStackTrace());
+ }
+
+ @Test
public void testAmbiguousMissingLineStackTrace() throws Exception {
runRetraceTest(new AmbiguousMissingLineStackTrace());
}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiProxyFrameWithSourceFileTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiProxyFrameWithSourceFileTest.java
new file mode 100644
index 0000000..996e6f0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiProxyFrameWithSourceFileTest.java
@@ -0,0 +1,141 @@
+// Copyright (c) 2023, 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.retrace.api;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.retrace.ProguardMapProducer;
+import com.android.tools.r8.retrace.ProguardMappingSupplier;
+import com.android.tools.r8.retrace.Retrace;
+import com.android.tools.r8.retrace.RetraceStackFrameResult;
+import com.android.tools.r8.retrace.RetraceStackTraceContext;
+import com.android.tools.r8.retrace.RetraceStackTraceElementProxy;
+import com.android.tools.r8.retrace.StackTraceElementProxy;
+import java.util.Collections;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class RetraceApiProxyFrameWithSourceFileTest extends RetraceApiTestBase {
+
+ public RetraceApiProxyFrameWithSourceFileTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ protected Class<? extends RetraceApiBinaryTest> binaryTestClass() {
+ return ApiTest.class;
+ }
+
+ public static class ApiTest implements RetraceApiBinaryTest {
+
+ @Test
+ public void test() {
+ RetraceStackFrameResult<String> stringRetraceStackFrameResult =
+ Retrace.<String, TestProxy>builder()
+ .setMappingSupplier(
+ ProguardMappingSupplier.builder()
+ .setProguardMapProducer(ProguardMapProducer.fromString(""))
+ .build())
+ .build()
+ .retraceStackTraceParsed(
+ Collections.singletonList(new TestProxy()), RetraceStackTraceContext.empty())
+ .getResult()
+ .get(0)
+ .get(0);
+ assertEquals("com.android.tools.R8.a(Unknown Source)", stringRetraceStackFrameResult.get(0));
+ }
+
+ public static class TestProxy extends StackTraceElementProxy<String, TestProxy> {
+
+ @Override
+ public boolean hasClassName() {
+ return true;
+ }
+
+ @Override
+ public boolean hasMethodName() {
+ return true;
+ }
+
+ @Override
+ public boolean hasSourceFile() {
+ return true;
+ }
+
+ @Override
+ public boolean hasLineNumber() {
+ return true;
+ }
+
+ @Override
+ public boolean hasFieldName() {
+ return false;
+ }
+
+ @Override
+ public boolean hasFieldOrReturnType() {
+ return false;
+ }
+
+ @Override
+ public boolean hasMethodArguments() {
+ return false;
+ }
+
+ @Override
+ public ClassReference getClassReference() {
+ return Reference.classFromTypeName("com.android.tools.R8");
+ }
+
+ @Override
+ public String getMethodName() {
+ return "a";
+ }
+
+ @Override
+ public String getSourceFile() {
+ return "Unknown Source";
+ }
+
+ @Override
+ public int getLineNumber() {
+ return 1;
+ }
+
+ @Override
+ public String getFieldName() {
+ return null;
+ }
+
+ @Override
+ public String getFieldOrReturnType() {
+ return null;
+ }
+
+ @Override
+ public String getMethodArguments() {
+ return null;
+ }
+
+ @Override
+ public String toRetracedItem(
+ RetraceStackTraceElementProxy<String, TestProxy> retracedProxy, boolean verbose) {
+ assertFalse(retracedProxy.hasRetracedMethod());
+ return retracedProxy.getRetracedClass().getTypeName()
+ + "."
+ + getMethodName()
+ + "("
+ + retracedProxy.getRetracedSourceFile().getOrInferSourceFile(getSourceFile())
+ + ")";
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
index a16e5d3..6c84173 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetraceApiTestCollection.java
@@ -42,12 +42,13 @@
RetracePartitionJoinNoMetadataTest.ApiTest.class,
RetracePartitionSerializedObfuscatedKeyTest.ApiTest.class,
RetracePartitionRoundTripInlineTest.ApiTest.class,
- RetraceApiTypeResultTest.ApiTest.class);
+ RetraceApiTypeResultTest.ApiTest.class,
+ RetraceApiResidualSignatureTest.ApiTest.class,
+ RetracePartitionEmptyMappingTest.ApiTest.class,
+ RetraceApiProxyFrameWithSourceFileTest.ApiTest.class);
public static List<Class<? extends RetraceApiBinaryTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
- ImmutableList.of(
- RetraceApiResidualSignatureTest.ApiTest.class,
- RetracePartitionEmptyMappingTest.ApiTest.class);
+ ImmutableList.of();
private final TemporaryFolder temp;
diff --git a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripInlineTest.java b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripInlineTest.java
index 5d78d68..66cc1ef 100644
--- a/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripInlineTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/api/RetracePartitionRoundTripInlineTest.java
@@ -111,7 +111,7 @@
// Check that visiting all frames report all source files.
List<String> allSourceFiles =
retraceFrameElement.stream()
- .map(x -> x.getSourceFile().getOrInferSourceFile())
+ .map(x -> x.getSourceFile().getOrInferSourceFile(""))
.collect(Collectors.toList());
assertEquals(Arrays.asList("InlineeClass.kt", "CallerClass.kt"), allSourceFiles);
}
diff --git a/src/test/java/com/android/tools/r8/retrace/partition/R8ZipContainerMappingFileTest.java b/src/test/java/com/android/tools/r8/retrace/partition/R8ZipContainerMappingFileTest.java
index 7c64527..255b693 100644
--- a/src/test/java/com/android/tools/r8/retrace/partition/R8ZipContainerMappingFileTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/partition/R8ZipContainerMappingFileTest.java
@@ -26,7 +26,6 @@
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -140,11 +139,7 @@
.setMappingPartitionFromKeySupplier(
key -> {
try {
- // TODO(b/274735214): The key should exist.
- ZipEntry entry = zipFile.getEntry(key);
- return entry == null
- ? null
- : ByteStreams.toByteArray(zipFile.getInputStream(entry));
+ return ByteStreams.toByteArray(zipFile.getInputStream(zipFile.getEntry(key)));
} catch (IOException e) {
throw new RuntimeException(e);
}
diff --git a/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMultipleOutlineClassTest.java b/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMultipleOutlineClassTest.java
index 9175f41..24dfbf3 100644
--- a/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMultipleOutlineClassTest.java
+++ b/src/test/java/com/android/tools/r8/retrace/partition/RetracePartitionMultipleOutlineClassTest.java
@@ -142,7 +142,7 @@
// Check that visiting all frames report all source files.
List<String> allSourceFiles =
retraceFrameElement.stream()
- .map(x -> x.getSourceFile().getOrInferSourceFile())
+ .map(x -> x.getSourceFile().getOrInferSourceFile(""))
.collect(Collectors.toList());
assertEquals(Arrays.asList("InlineeClass.kt", "CallerClass.kt"), allSourceFiles);
}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousInlineFramesStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousInlineFramesStackTrace.java
new file mode 100644
index 0000000..85cb682
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/AmbiguousInlineFramesStackTrace.java
@@ -0,0 +1,54 @@
+// Copyright (c) 2023, 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.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class AmbiguousInlineFramesStackTrace implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "com.android.tools.r8.CompilationException:", " at a.a.a(Unknown Source:1)");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "com.android.tools.r8.CompilationException:",
+ " at com.android.tools.r8.R8.foo(R8.java:42)",
+ " <OR> at com.android.tools.r8.R8.foo(R8.java:43)",
+ " <OR> at com.android.tools.r8.R8.foo(R8.java:44)",
+ " at com.android.tools.r8.R8.bar(R8.java:32)",
+ " at com.android.tools.r8.R8.baz(R8.java:10)");
+ }
+
+ @Override
+ public List<String> retraceVerboseStackTrace() {
+ return Arrays.asList(
+ "com.android.tools.r8.CompilationException:",
+ " at com.android.tools.r8.R8.void foo(int)(R8.java:42)",
+ " <OR> at com.android.tools.r8.R8.void foo(int)(R8.java:43)",
+ " <OR> at com.android.tools.r8.R8.void foo(int)(R8.java:44)",
+ " at com.android.tools.r8.R8.void bar(int,int)(R8.java:32)",
+ " at com.android.tools.r8.R8.void baz(int,int)(R8.java:10)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.lines(
+ "com.android.tools.r8.R8 -> a.a:",
+ " 1:1:void foo(int):42:44 -> a",
+ " 1:1:void bar(int, int):32 -> a",
+ " 1:1:void baz(int, int):10 -> a");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/SingleLineNoLineNumberStackTrace.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/SingleLineNoLineNumberStackTrace.java
index 914cc4b..a78d417 100644
--- a/src/test/java/com/android/tools/r8/retrace/stacktraces/SingleLineNoLineNumberStackTrace.java
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/SingleLineNoLineNumberStackTrace.java
@@ -55,12 +55,8 @@
"\tat com.android.tools.r8.naming.retrace.Main.void main(java.lang.String[])(Main.java:28)",
"\tat com.android.tools.r8.naming.retrace.Main.void"
+ " method2(java.lang.String)(Main.java:42)",
- "\tat com.android.tools.r8.naming.retrace.Main.void"
- + " main2(java.lang.String[])(Main.java:29)",
"\t<OR> at com.android.tools.r8.naming.retrace.Main.void"
+ " method2(java.lang.String)(Main.java:43)",
- "\tat com.android.tools.r8.naming.retrace.Main.void"
- + " main2(java.lang.String[])(Main.java:29)",
"\t<OR> at com.android.tools.r8.naming.retrace.Main.void"
+ " method2(java.lang.String)(Main.java:44)",
"\tat com.android.tools.r8.naming.retrace.Main.void"
diff --git a/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayPutsToArrayAliasTest.java b/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayPutsToArrayAliasTest.java
new file mode 100644
index 0000000..8bc3bc1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/rewrite/arrays/ArrayPutsToArrayAliasTest.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.rewrite.arrays;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ArrayPutsToArrayAliasTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8()
+ .addInnerClasses(getClass())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ static class Main {
+
+ static String[] STRINGS;
+
+ public static void main(String[] args) {
+ init();
+ test();
+ }
+
+ @NeverInline
+ static void init() {
+ STRINGS = new String[2];
+ // Leads to an AssumeNotNull instruction in R8 during IR processing.
+ String[] strings = STRINGS;
+ strings[0] = "Hello";
+ strings[1] = ", world!";
+ }
+
+ @NeverInline
+ static void test() {
+ System.out.print(STRINGS[0]);
+ System.out.println(STRINGS[1]);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java b/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
index 98d8604..ab4a619 100644
--- a/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/switchmaps/RewriteSwitchMapsTest.java
@@ -4,16 +4,17 @@
package com.android.tools.r8.rewrite.switchmaps;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+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.ToolHelper;
+import com.android.tools.r8.examples.switchmaps.Colors;
+import com.android.tools.r8.examples.switchmaps.Days;
+import com.android.tools.r8.examples.switchmaps.SwitchMapsTestRunner;
+import com.android.tools.r8.examples.switchmaps.Switches;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.google.common.collect.ImmutableList;
-import java.nio.file.Paths;
-import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -31,20 +32,29 @@
return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
}
- private static final String JAR_FILE = "switchmaps.jar";
- private static final String SWITCHMAP_CLASS_NAME = "switchmaps.Switches$1";
- private static final List<String> PG_CONFIG = ImmutableList.of(
- "-keep class switchmaps.Switches { public static void main(...); }",
- "-dontobfuscate",
- "-keepattributes *");
+ private static final String SWITCHMAP_CLASS_NAME = typeName(Switches.class) + "$1";
+
+ @Test
+ public void testReference() throws Exception {
+ testForRuntime(parameters)
+ .addProgramClassesAndInnerClasses(Switches.class)
+ .addProgramClasses(Colors.class, Days.class)
+ .run(parameters.getRuntime(), Switches.class)
+ .assertSuccessWithOutput(SwitchMapsTestRunner.EXPECTED)
+ .inspect(inspector -> assertThat(inspector.clazz(SWITCHMAP_CLASS_NAME), isPresent()));
+ }
@Test
public void checkSwitchMapsRemoved() throws Exception {
testForR8(parameters.getBackend())
- .addProgramFiles(Paths.get(ToolHelper.EXAMPLES_BUILD_DIR).resolve(JAR_FILE))
- .addKeepRules(PG_CONFIG)
+ .addProgramClassesAndInnerClasses(Switches.class)
+ .addProgramClasses(Colors.class, Days.class)
+ .addKeepMainRule(Switches.class)
+ .addDontObfuscate()
+ .addKeepAllAttributes()
.setMinApi(parameters)
- .compile()
+ .run(parameters.getRuntime(), Switches.class)
+ .assertSuccessWithOutput(SwitchMapsTestRunner.EXPECTED)
.inspect(inspector -> assertThat(inspector.clazz(SWITCHMAP_CLASS_NAME), isAbsent()));
}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java b/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
index f44de8c..a3d1fef 100644
--- a/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/RetainIndirectlyReferencedConstructorShakingOnDexTest.java
@@ -9,6 +9,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -47,11 +48,14 @@
.addInnerClasses(getClass())
.addKeepMainRule(Main.class)
.addOptionsModification(
- options ->
- options
- .getRedundantBridgeRemovalOptions()
- .setEnableRetargetingOfConstructorBridgeCalls(
- enableRetargetingOfConstructorBridgeCalls))
+ options -> {
+ options.inlinerOptions().setEnableConstructorInlining(false);
+ options
+ .getRedundantBridgeRemovalOptions()
+ .setEnableRetargetingOfConstructorBridgeCalls(
+ enableRetargetingOfConstructorBridgeCalls);
+ })
+ .enableInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters)
.compile()
@@ -102,6 +106,7 @@
@NoVerticalClassMerging
public abstract static class A {
+ @NeverInline
public A() {
System.out.println("A");
}
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnFieldsTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnFieldsTest.java
index 17d3a91..629fd6e 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnFieldsTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/AnnotationsOnFieldsTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.AssumeMayHaveSideEffects;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -54,6 +55,7 @@
"-keep @interface **.*Annotation { *; }",
"-keepclassmembers class * { @**.*Annotation <fields>; }",
"-keepattributes *Annotation*")
+ .enableInliningAnnotations()
.enableSideEffectAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters)
@@ -102,6 +104,7 @@
class TestClass {
@AssumeMayHaveSideEffects
+ @NeverInline
public TestClass() {}
@StaticFieldAnnotation(clazz = StaticFieldAnnotationUse.class)
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
index 6b7c1a2..f98c16a 100644
--- a/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
+++ b/src/test/java/com/android/tools/r8/shaking/annotations/b137392797/B137392797.java
@@ -53,7 +53,10 @@
// When generating class file the field values[] is also present as values() is kept.
assertEquals(parameters.isCfRuntime() ? 3 : 2, classSubject.allFields().size());
// Methods <clinit>, <init> always present. values() present if generating class file.
- assertEquals(parameters.isCfRuntime() ? 3 : 2, classSubject.allMethods().size());
+ assertEquals(
+ (parameters.isCfRuntime() ? 3 : 2)
+ - BooleanUtils.intValue(parameters.canInitNewInstanceUsingSuperclassConstructor()),
+ classSubject.allMethods().size());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithoutMatchingDefinitionTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithoutMatchingDefinitionTest.java
index 65687cc4..0a133e5 100644
--- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithoutMatchingDefinitionTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsWithoutMatchingDefinitionTest.java
@@ -8,6 +8,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.errors.Unreachable;
@@ -109,6 +110,7 @@
.addKeepMainRule(MAIN)
.addKeepRules(config.getKeepRule())
.addDontObfuscate()
+ .enableInliningAnnotations()
.setMinApi(parameters)
.compile()
.addRunClasspathFiles(parameters.isDexRuntime() ? libDexPath : libJarPath)
@@ -128,6 +130,7 @@
static class ProgramClass extends LibrarySub {
private final String test;
+ @NeverInline
private ProgramClass() {
if (isInEditMode()) {
test = "test";
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java
index 8c65d0e..5314db5 100644
--- a/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/attributes/KeepAttributesTest.java
@@ -117,6 +117,7 @@
.addProgramClassesAndInnerClasses(CLASS)
.addKeepAllClassesRule()
.addKeepRules(keepRules)
+ .enableInliningAnnotations()
.enableSideEffectAnnotations()
.setMinApi(parameters)
.run(parameters.getRuntime(), CLASS)
diff --git a/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
index 7dacf64..f4d629f 100644
--- a/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/constructor/ForwardingConstructorUsedFromPlatformShakingOnDexTest.java
@@ -5,15 +5,13 @@
package com.android.tools.r8.shaking.constructor;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf;
import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assume.assumeFalse;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.apimodel.ApiModelingTestHelper;
-import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -41,15 +39,11 @@
private static List<byte[]> transformedLibraryClassFileData;
@Parameter(0)
- public boolean enableModeling;
-
- @Parameter(1)
public TestParameters parameters;
- @Parameters(name = "{1}, modeling: {0}")
- public static List<Object[]> data() {
- return buildParameters(
- BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
@BeforeClass
@@ -60,7 +54,6 @@
@Test
public void testRuntime() throws Exception {
- assumeFalse(enableModeling);
testForRuntime(parameters)
.addProgramClassFileData(transformedProgramClassFileData)
.addProgramClassFileData(transformedLibraryClassFileData)
@@ -75,14 +68,6 @@
.addLibraryClassFileData(transformedLibraryClassFileData)
.addLibraryFiles(parameters.getDefaultRuntimeLibrary())
.addKeepMainRule(Main.class)
- .applyIf(
- !enableModeling,
- testBuilder ->
- testBuilder.addOptionsModification(
- options ->
- options
- .getRedundantBridgeRemovalOptions()
- .clearNoConstructorShrinkingHierarchiesForTesting()))
// Since Fragment is first defined in API 11.
.apply(ApiModelingTestHelper::disableStubbingOfClasses)
.enableNeverClassInliningAnnotations()
@@ -91,10 +76,7 @@
.inspect(this::inspect)
.addRunClasspathClassFileData(transformedLibraryClassFileData)
.run(parameters.getRuntime(), Main.class)
- .applyIf(
- enableModeling || !parameters.canHaveNonReboundConstructorInvoke(),
- runResult -> runResult.assertSuccessWithOutput(EXPECTED_OUTPUT),
- runResult -> runResult.assertFailureWithErrorThatThrows(NoSuchMethodException.class));
+ .assertSuccessWithOutput(EXPECTED_OUTPUT);
}
private static List<byte[]> getTransformedProgramClasses() throws Exception {
@@ -144,15 +126,11 @@
private void inspect(CodeInspector inspector) {
ClassSubject myFragmentClassSubject = inspector.clazz(MyFragment.class);
assertThat(myFragmentClassSubject, isPresent());
- assertThat(
- myFragmentClassSubject.init(),
- onlyIf(enableModeling || !parameters.canHaveNonReboundConstructorInvoke(), isPresent()));
+ assertThat(myFragmentClassSubject.init(), isPresent());
ClassSubject myZygotePreloadClassSubject = inspector.clazz(MyZygotePreload.class);
assertThat(myZygotePreloadClassSubject, isPresent());
- assertThat(
- myZygotePreloadClassSubject.init(),
- onlyIf(enableModeling || !parameters.canHaveNonReboundConstructorInvoke(), isPresent()));
+ assertThat(myZygotePreloadClassSubject.init(), isPresent());
}
// Library classes.
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
index bb40a40..d6ac5bf 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/ForceProguardCompatibilityTest.java
@@ -458,6 +458,7 @@
"}",
keepRules)
.addOptionsModification(options -> options.enableClassInlining = false)
+ .enableInliningAnnotations()
.enableSideEffectAnnotations()
.setMinApi(parameters)
.run(parameters.getRuntime(), TestKeepAttributes.class)
diff --git a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/keepattributes/TestKeepAttributes.java b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/keepattributes/TestKeepAttributes.java
index f7c8fd6..296123f 100644
--- a/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/keepattributes/TestKeepAttributes.java
+++ b/src/test/java/com/android/tools/r8/shaking/forceproguardcompatibility/keepattributes/TestKeepAttributes.java
@@ -5,11 +5,13 @@
package com.android.tools.r8.shaking.forceproguardcompatibility.keepattributes;
import com.android.tools.r8.AssumeMayHaveSideEffects;
+import com.android.tools.r8.NeverInline;
public class TestKeepAttributes {
public static class InnerClass {
@AssumeMayHaveSideEffects
+ @NeverInline
InnerClass() {}
}
@@ -17,6 +19,7 @@
class MemberClass {
@AssumeMayHaveSideEffects
+ @NeverInline
private MemberClass() {}
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalKeepClassWithMembersTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalKeepClassWithMembersTest.java
new file mode 100644
index 0000000..b719464
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/ConditionalKeepClassWithMembersTest.java
@@ -0,0 +1,113 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.shaking.ifrule;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.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.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+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 ConditionalKeepClassWithMembersTest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testR8KeepingInit() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters)
+ .addKeepMainRule(Main.class)
+ .addKeepClassRules(SerializedName.class)
+ .addKeepRules(
+ StringUtils.lines(
+ "-if class * {",
+ " @ " + typeName(SerializedName.class) + " <fields>;",
+ "}",
+ "-keepclasseswithmembers class <1> {",
+ " <init>();",
+ "}"))
+ .addKeepRules(
+ StringUtils.lines(
+ "-if class *",
+ "-keepclasseswithmembers,allowshrinking class <1> {",
+ " @" + typeName(SerializedName.class) + " <fields>;",
+ "}"))
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject dataClass = inspector.clazz(Data.class);
+ assertThat(dataClass, isPresent());
+ assertThat(dataClass.uniqueFieldWithOriginalName("field"), isPresent());
+ assertThat(dataClass.uniqueFieldWithOriginalName("unusedField"), isAbsent());
+ assertThat(inspector.clazz(Data2.class), isAbsent());
+ })
+ .run(parameters.getRuntime(), Main.class, typeName(Data.class))
+ .assertSuccessWithOutputLines("0");
+ }
+
+ @Test
+ public void testR8NotKeepingInit() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters)
+ .addKeepMainRule(Main.class)
+ .addKeepClassRules(SerializedName.class)
+ .addKeepRules(
+ StringUtils.lines(
+ "-if class *",
+ "-keepclasseswithmembers,allowshrinking class <1> {",
+ " <init>();",
+ " @" + typeName(SerializedName.class) + " <fields>;",
+ "}"))
+ .compile()
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz(Data.class), isAbsent());
+ assertThat(inspector.clazz(Data2.class), isAbsent());
+ })
+ .run(parameters.getRuntime(), Main.class, typeName(Data.class))
+ .assertFailureWithErrorThatThrows(ClassNotFoundException.class);
+ }
+
+ public @interface SerializedName {}
+
+ public static class Data {
+
+ @SerializedName public int field;
+ }
+
+ public static class Data2 {
+
+ @SerializedName public int field;
+ @SerializedName public int unusedField;
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) throws Exception {
+ check((Data) (Class.forName(args[0])).getDeclaredConstructor().newInstance());
+ }
+
+ public static void check(Data f) {
+ System.out.println(f.field);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/classinlining/IfRuleWithClassInlining.java b/src/test/java/com/android/tools/r8/shaking/ifrule/classinlining/IfRuleWithClassInlining.java
index 35854ba..de857b0 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/classinlining/IfRuleWithClassInlining.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/classinlining/IfRuleWithClassInlining.java
@@ -8,6 +8,7 @@
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.NoRedundantFieldLoadElimination;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.AndroidApiLevel;
@@ -49,12 +50,13 @@
"-keep class " + Unused.class.getTypeName());
CodeInspector inspector =
testForR8(parameters.getBackend())
- .addInnerClasses(IfRuleWithClassInlining.class)
+ .addInnerClasses(getClass())
.addKeepMainRule(TestClass.class)
.addKeepRules(enableIfRule ? ifRule : "")
.addOptionsModification(options -> options.enableClassInlining = enableClassInlining)
// TODO(b/120061431): Should not be needed for this example.
.allowAccessModification()
+ .enableNoRedundantFieldLoadEliminationAnnotations()
.setMinApi(parameters)
.compile()
.inspector();
@@ -80,7 +82,7 @@
static class Builder {
- private String string = null;
+ @NoRedundantFieldLoadElimination private String string = null;
public Builder setString(String string) {
this.string = string;
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java
index 8e4f9f4..df888c0 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/verticalclassmerging/MergedFieldTypeTest.java
@@ -11,6 +11,7 @@
import static org.junit.Assert.assertNotEquals;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoRedundantFieldLoadElimination;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
@@ -51,7 +52,7 @@
static class SuperTestClass {
- private A field = new B();
+ @NoRedundantFieldLoadElimination private A field = new B();
public A get() {
return field;
@@ -84,7 +85,9 @@
@Override
public void configure(R8FullTestBuilder builder) {
- builder.enableNeverClassInliningAnnotations();
+ builder
+ .enableNeverClassInliningAnnotations()
+ .enableNoRedundantFieldLoadEliminationAnnotations();
}
@Override
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByFieldReflectionTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByFieldReflectionTest.java
index 3ba9b99..c106977 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByFieldReflectionTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByFieldReflectionTest.java
@@ -4,8 +4,10 @@
package com.android.tools.r8.shaking.keptgraph;
import com.android.tools.r8.Keep;
+import com.android.tools.r8.NoInliningOfDefaultInitializer;
@Keep
+@NoInliningOfDefaultInitializer
public class KeptByFieldReflectionTest {
public int foo = 42;
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByFieldReflectionTestRunner.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByFieldReflectionTestRunner.java
index b3a97a6..12bfedb 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByFieldReflectionTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByFieldReflectionTestRunner.java
@@ -74,6 +74,7 @@
.addProgramClasses(CLASSES)
.addKeepAnnotation()
.addKeepMainRule(CLASS)
+ .enableNoInliningOfDefaultInitializerAnnotations()
.setMinApi(parameters)
.run(parameters.getRuntime(), CLASS)
.assertSuccessWithOutput(EXPECTED_STDOUT)
diff --git a/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideTest.java b/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideTest.java
index 5eeb4b1..f8bccf2 100644
--- a/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/librarymethodoverride/LibraryMethodOverrideTest.java
@@ -9,6 +9,7 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
@@ -42,6 +43,7 @@
.addInnerClasses(LibraryMethodOverrideTest.class)
.addKeepMainRule(TestClass.class)
.addOptionsModification(options -> options.enableTreeShakingOfLibraryMethodOverrides = true)
+ .enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNoHorizontalClassMergingAnnotations()
@@ -129,6 +131,7 @@
@NoHorizontalClassMerging
static class DoesNotEscape {
+ @NeverInline
DoesNotEscape() {
// Side effect to ensure that the constructor is not removed from main().
System.out.print("");
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index e010aa2..2e3df74 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -783,6 +783,30 @@
});
}
+ public ClassFileTransformer setMethodParameters(
+ MethodPredicate predicate, String... parameterNames) {
+ return addClassTransformer(
+ new ClassTransformer() {
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String descriptor, String signature, String[] exceptions) {
+ MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
+ if (predicate.test(access, name, descriptor, signature, exceptions)) {
+ for (String parameterName : parameterNames) {
+ mv.visitParameter(parameterName, 0);
+ }
+ return new MethodVisitor(ASM_VERSION, mv) {
+ @Override
+ public void visitParameter(String name, int access) {
+ // Ignore all existing method parameter.
+ }
+ };
+ }
+ return mv;
+ }
+ });
+ }
+
public ClassFileTransformer setFieldType(FieldPredicate predicate, String newFieldType) {
return addClassTransformer(
new ClassTransformer() {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
index 82eaa9d..d5fa8bf 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -143,6 +143,10 @@
return not(isPresent());
}
+ public static Matcher<Subject> isAbsentIf(boolean condition) {
+ return onlyIf(condition, isAbsent());
+ }
+
public static Matcher<Subject> isPresent() {
return new TypeSafeMatcher<Subject>() {
@Override
@@ -169,6 +173,10 @@
};
}
+ public static Matcher<Subject> isPresentIf(boolean condition) {
+ return onlyIf(condition, isPresent());
+ }
+
public static Matcher<Subject> isPresentAndRenamed() {
return new TypeSafeMatcher<Subject>() {
@Override
diff --git a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1 b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
index e5b5e41..2133707 100644
--- a/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
+++ b/third_party/binary_compatibility_tests/compiler_api_tests.tar.gz.sha1
@@ -1 +1 @@
-2aaa39dd57bf8c1c21393ae6b3181ae3a10da88e
\ No newline at end of file
+9d322829ca871ff117d24fb8d7cb30001552a3ed
\ No newline at end of file
diff --git a/third_party/openjdk/jdk-20/linux.tar.gz.sha1 b/third_party/openjdk/jdk-20/linux.tar.gz.sha1
new file mode 100644
index 0000000..33f7ffa
--- /dev/null
+++ b/third_party/openjdk/jdk-20/linux.tar.gz.sha1
@@ -0,0 +1 @@
+d901c9848cf777b26b57b408fc7f6f28d8d2c5b6
\ No newline at end of file
diff --git a/third_party/openjdk/jdk-20/osx.tar.gz.sha1 b/third_party/openjdk/jdk-20/osx.tar.gz.sha1
new file mode 100644
index 0000000..51f35d1
--- /dev/null
+++ b/third_party/openjdk/jdk-20/osx.tar.gz.sha1
@@ -0,0 +1 @@
+fdee9da4ef8d01071e533858d1c76b0f0c09c362
\ No newline at end of file
diff --git a/third_party/openjdk/jdk-20/windows.tar.gz.sha1 b/third_party/openjdk/jdk-20/windows.tar.gz.sha1
new file mode 100644
index 0000000..f377055
--- /dev/null
+++ b/third_party/openjdk/jdk-20/windows.tar.gz.sha1
@@ -0,0 +1 @@
+7b602bec8421d42a22c5cab6e1f34f288c38b8da
\ No newline at end of file
diff --git a/third_party/retrace/binary_compatibility.tar.gz.sha1 b/third_party/retrace/binary_compatibility.tar.gz.sha1
index 5678440..4659358 100644
--- a/third_party/retrace/binary_compatibility.tar.gz.sha1
+++ b/third_party/retrace/binary_compatibility.tar.gz.sha1
@@ -1 +1 @@
-5c0b406a52a08af4318a104660091c61d6bb8446
\ No newline at end of file
+cc086e35ef1bb4754cbfa8498af00dc5b9e43002
\ No newline at end of file
diff --git a/tools/archive.py b/tools/archive.py
index d724b18..558842c 100755
--- a/tools/archive.py
+++ b/tools/archive.py
@@ -224,7 +224,11 @@
utils.R8LIB_EXCLUDE_DEPS_JAR + '.map',
utils.R8LIB_EXCLUDE_DEPS_JAR + '_map.zip',
utils.R8RETRACE_JAR,
+ utils.R8RETRACE_JAR + '.map',
+ utils.R8RETRACE_JAR + '_map.zip',
utils.R8RETRACE_EXCLUDE_DEPS_JAR,
+ utils.R8RETRACE_EXCLUDE_DEPS_JAR + '.map',
+ utils.R8RETRACE_EXCLUDE_DEPS_JAR + '_map.zip',
utils.MAVEN_ZIP,
utils.MAVEN_ZIP_LIB,
utils.DESUGAR_CONFIGURATION,
diff --git a/tools/test.py b/tools/test.py
index 2f0254c..a8e279f 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -59,6 +59,7 @@
'jdk9',
'jdk11',
'jdk17',
+ 'jdk20',
] + [ 'dex-%s' % dexvm for dexvm in ALL_ART_VMS ]
def ParseOptions():