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..067e0ae9 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 b9c6c78..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():