Merge commit '7c8734d1ec8841da7b99d0c09025d2ac5497a62e' into dev-release
diff --git a/.gitignore b/.gitignore index bbe6eea..a5b9426 100644 --- a/.gitignore +++ b/.gitignore
@@ -99,19 +99,27 @@ third_party/kotlin/kotlin-compiler-1.3.41 third_party/kotlin/kotlin-compiler-1.3.72.tar.gz third_party/kotlin/kotlin-compiler-1.3.72 +third_party/kotlin/kotlin-compiler-1.4.20.tar.gz +third_party/kotlin/kotlin-compiler-1.4.20 third_party/kotlinx-coroutines-1.3.6.tar.gz third_party/kotlinx-coroutines-1.3.6 third_party/nest/* third_party/openjdk/desugar_jdk_libs third_party/openjdk/desugar_jdk_libs.tar.gz +third_party/openjdk/jdk-15/linux +third_party/openjdk/jdk-15/linux.tar.gz +third_party/openjdk/jdk-15/osx +third_party/openjdk/jdk-15/osx.tar.gz +third_party/openjdk/jdk-15/windows +third_party/openjdk/jdk-15/windows.tar.gz third_party/openjdk/jdk-11-test third_party/openjdk/jdk-11-test.tar.gz -third_party/openjdk/jdk-11/Linux -third_party/openjdk/jdk-11/Linux.tar.gz -third_party/openjdk/jdk-11/Mac -third_party/openjdk/jdk-11/Mac.tar.gz -third_party/openjdk/jdk-11/Windows -third_party/openjdk/jdk-11/Windows.tar.gz +third_party/openjdk/jdk-11/linux +third_party/openjdk/jdk-11/linux.tar.gz +third_party/openjdk/jdk-11/osx +third_party/openjdk/jdk-11/osx.tar.gz +third_party/openjdk/jdk-11/windows +third_party/openjdk/jdk-11/windows.tar.gz third_party/openjdk/jdk8/darwin-x86 third_party/openjdk/jdk8/darwin-x86.tar.gz third_party/openjdk/jdk8/linux-x86
diff --git a/build.gradle b/build.gradle index 981dcfd..be64cba 100644 --- a/build.gradle +++ b/build.gradle
@@ -22,7 +22,7 @@ jcenter() } dependencies { - classpath 'com.github.jengelman.gradle.plugins:shadow:4.0.2' + classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0' } } @@ -35,7 +35,7 @@ ext { androidSupportVersion = '25.4.0' - asmVersion = '8.0' // When updating update tools/asmifier.py and Toolhelper as well. + asmVersion = '9.0' // When updating update tools/asmifier.py and Toolhelper as well. espressoVersion = '3.0.0' fastutilVersion = '7.2.0' guavaVersion = '23.0' @@ -135,6 +135,11 @@ srcDirs = ['src/test/examplesJava11'] } } + examplesJava15 { + java { + srcDirs = ['src/test/examplesJava15'] + } + } jdk11TimeTests { java { srcDirs = [ @@ -287,7 +292,7 @@ def r8DesugaredPath = "$buildDir/libs/r8desugared.jar" def r8LibGeneratedKeepRulesPath = "$buildDir/generated/keep.txt" def r8LibTestPath = "$buildDir/classes/r8libtest" -def java11ClassFiles = "build/classes/java/mainJava11" +def java11ClassFiles = "$buildDir/classes/java/mainJava11" def osString = OperatingSystem.current().isLinux() ? "linux" : OperatingSystem.current().isMacOsX() ? "mac" : "windows" @@ -362,16 +367,19 @@ linux: [ "third_party": ["openjdk/openjdk-9.0.4/linux", "openjdk/jdk8/linux-x86", - "openjdk/jdk-11/Linux"], + "openjdk/jdk-11/linux", + "openjdk/jdk-15/linux"], ], osx: [ "third_party": ["openjdk/openjdk-9.0.4/osx", "openjdk/jdk8/darwin-x86", - "openjdk/jdk-11/Mac"], + "openjdk/jdk-11/osx", + "openjdk/jdk-15/osx"], ], windows: [ "third_party": ["openjdk/openjdk-9.0.4/windows", - "openjdk/jdk-11/Windows"], + "openjdk/jdk-11/windows", + "openjdk/jdk-15/windows"], ], ] @@ -555,42 +563,61 @@ } } -tasks.named(sourceSets.examplesJava9.compileJavaTaskName).get().configure { - def jdkDir = 'third_party/openjdk/openjdk-9.0.4/' - options.fork = true - options.forkOptions.jvmArgs = [] - if (OperatingSystem.current().isLinux()) { - options.forkOptions.javaHome = file(jdkDir + 'linux') - } else if (OperatingSystem.current().isMacOsX()) { - options.forkOptions.javaHome = file(jdkDir + 'osx') - } else { - options.forkOptions.javaHome = file(jdkDir + 'windows') - } - sourceCompatibility = JavaVersion.VERSION_1_9 - targetCompatibility = JavaVersion.VERSION_1_9 -} - -def setJdk11CompilationWithCompatibility(String sourceSet, JavaVersion compatibility) { +def setJdkCompilationWithCompatibility(String sourceSet, String javaHome, JavaVersion compatibility, boolean enablePreview) { tasks.named(sourceSet).get().configure { - def jdkDir = 'third_party/openjdk/jdk-11/' + def jdkDir = "third_party/openjdk/${javaHome}/" options.fork = true options.forkOptions.jvmArgs = [] + if (enablePreview) { + options.compilerArgs.add('--enable-preview') + } if (OperatingSystem.current().isLinux()) { - options.forkOptions.javaHome = file(jdkDir + 'Linux') + options.forkOptions.javaHome = file(jdkDir + 'linux') } else if (OperatingSystem.current().isMacOsX()) { - options.forkOptions.javaHome = file(jdkDir + 'Mac/Contents/Home') + options.forkOptions.javaHome = file(jdkDir + 'osx') } else { - options.forkOptions.javaHome = file(jdkDir + 'Windows') + options.forkOptions.javaHome = file(jdkDir + 'windows') } sourceCompatibility = compatibility targetCompatibility = compatibility } } -setJdk11CompilationWithCompatibility(sourceSets.examplesJava10.compileJavaTaskName, JavaVersion.VERSION_1_10) -setJdk11CompilationWithCompatibility(sourceSets.examplesJava11.compileJavaTaskName, JavaVersion.VERSION_11) -setJdk11CompilationWithCompatibility(sourceSets.examplesTestNGRunner.compileJavaTaskName, JavaVersion.VERSION_11) -setJdk11CompilationWithCompatibility(sourceSets.jdk11TimeTests.compileJavaTaskName, JavaVersion.VERSION_11) +setJdkCompilationWithCompatibility( + sourceSets.examplesJava9.compileJavaTaskName, + 'openjdk-9.0.4', + JavaVersion.VERSION_1_9, + false) +setJdkCompilationWithCompatibility( + sourceSets.examplesJava11.compileJavaTaskName, + 'jdk-11', + JavaVersion.VERSION_11, + false) +setJdkCompilationWithCompatibility( + sourceSets.examplesJava10.compileJavaTaskName, + 'jdk-11', + JavaVersion.VERSION_1_10, + false) +setJdkCompilationWithCompatibility( + sourceSets.examplesJava11.compileJavaTaskName, + 'jdk-11', + JavaVersion.VERSION_11, + false) +setJdkCompilationWithCompatibility( + sourceSets.examplesTestNGRunner.compileJavaTaskName, + 'jdk-11', + JavaVersion.VERSION_11, + false) +setJdkCompilationWithCompatibility( + sourceSets.jdk11TimeTests.compileJavaTaskName, + 'jdk-11', + JavaVersion.VERSION_11, + false) +setJdkCompilationWithCompatibility( + sourceSets.examplesJava15.compileJavaTaskName, + 'jdk-15', + JavaVersion.VERSION_15, + true) task compileMainWithJava11 (type: JavaCompile) { dependsOn downloadDeps @@ -598,11 +625,11 @@ options.fork = true options.forkOptions.jvmArgs = [] if (OperatingSystem.current().isLinux()) { - options.forkOptions.javaHome = file(jdkDir + 'Linux') + options.forkOptions.javaHome = file(jdkDir + 'linux') } else if (OperatingSystem.current().isMacOsX()) { - options.forkOptions.javaHome = file(jdkDir + 'Mac/Contents/Home') + options.forkOptions.javaHome = file(jdkDir + 'osx/Contents/Home') } else { - options.forkOptions.javaHome = file(jdkDir + 'Windows') + options.forkOptions.javaHome = file(jdkDir + 'windows') } source = sourceSets.main.allSource destinationDir = file(java11ClassFiles) @@ -714,6 +741,8 @@ } task repackageSourcesNew(type: ShadowJar) { + // If this fails then remove all generated folders from + // build/classes/java/test that is not {com,dalvik} from sourceSets.main.output mergeServiceFiles(it) baseName 'sources_main' @@ -730,6 +759,7 @@ return tasks.create("r8Create${name}", ShadowJar) { from consolidatedLicense.outputs.files from sources + exclude "$buildDir/classes/**" baseName baseNameName classifier = null version = null @@ -1519,53 +1549,28 @@ } } -task buildExampleJava9Jars { - def examplesDir = file("src/test/examplesJava9") - examplesDir.eachDir { dir -> - def name = dir.getName(); - def exampleOutputDir = file("build/test/examplesJava9"); - def jarName = "${name}.jar" - dependsOn "jar_examplesJava9_${name}" - task "jar_examplesJava9_${name}"(type: Jar) { - archiveName = jarName - destinationDir = exampleOutputDir - from sourceSets.examplesJava9.output - include "**/" + name + "/**/*.class" +def buildExampleJarsCreateTask(javaVersion, sourceSet) { + return tasks.create("buildExample${javaVersion}Jars") { + def examplesDir = file("src/test/examples${javaVersion}") + examplesDir.eachDir { dir -> + def name = dir.getName(); + def exampleOutputDir = file("build/test/examples${javaVersion}"); + def jarName = "${name}.jar" + dependsOn "jar_examples${javaVersion}_${name}" + task "jar_examples${javaVersion}_${name}"(type: Jar) { + archiveName = jarName + destinationDir = exampleOutputDir + from sourceSet.output + include "**/" + name + "/**/*.class" + } } } } -task buildExampleJava10Jars { - def examplesDir = file("src/test/examplesJava10") - examplesDir.eachDir { dir -> - def name = dir.getName(); - def exampleOutputDir = file("build/test/examplesJava10"); - def jarName = "${name}.jar" - dependsOn "jar_examplesJava10_${name}" - task "jar_examplesJava10_${name}"(type: Jar) { - archiveName = jarName - destinationDir = exampleOutputDir - from sourceSets.examplesJava10.output - include "**/" + name + "/**/*.class" - } - } -} - -task buildExampleJava11Jars { - def examplesDir = file("src/test/examplesJava11") - examplesDir.eachDir { dir -> - def name = dir.getName(); - def exampleOutputDir = file("build/test/examplesJava11"); - def jarName = "${name}.jar" - dependsOn "jar_examplesJava11_${name}" - task "jar_examplesJava11_${name}"(type: Jar) { - archiveName = jarName - destinationDir = exampleOutputDir - from sourceSets.examplesJava11.output - include "**/" + name + "/**/*.class" - } - } -} +buildExampleJarsCreateTask("Java9", sourceSets.examplesJava9) +buildExampleJarsCreateTask("Java10", sourceSets.examplesJava10) +buildExampleJarsCreateTask("Java11", sourceSets.examplesJava11) +buildExampleJarsCreateTask("Java15", sourceSets.examplesJava15) task provideArtFrameworksDependencies { cloudDependencies.tools.forEach({ art -> @@ -1685,6 +1690,7 @@ dependsOn buildExampleJava9Jars dependsOn buildExampleJava10Jars dependsOn buildExampleJava11Jars + dependsOn buildExampleJava15Jars dependsOn buildExampleAndroidApi def examplesDir = file("src/test/examples") def noDexTests = [
diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..41fabcc --- /dev/null +++ b/gradle.properties
@@ -0,0 +1,5 @@ +# 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. + +org.gradle.jvmargs=-Xmx2048M \ No newline at end of file
diff --git a/src/main/java/com/android/tools/r8/D8Command.java b/src/main/java/com/android/tools/r8/D8Command.java index e9dbc44..f720ee9 100644 --- a/src/main/java/com/android/tools/r8/D8Command.java +++ b/src/main/java/com/android/tools/r8/D8Command.java
@@ -414,7 +414,7 @@ // Assert some of R8 optimizations are disabled. assert !internal.enableInlining; assert !internal.enableClassInlining; - assert !internal.enableHorizontalClassMerging; + assert internal.horizontalClassMergerOptions().isDisabled(); assert !internal.enableStaticClassMerging; assert !internal.enableVerticalClassMerging; assert !internal.enableClassStaticizer;
diff --git a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java index de3f800..295d52d 100644 --- a/src/main/java/com/android/tools/r8/DiagnosticsHandler.java +++ b/src/main/java/com/android/tools/r8/DiagnosticsHandler.java
@@ -39,7 +39,8 @@ */ default void warning(Diagnostic warning) { if (warning.getOrigin() != Origin.unknown()) { - System.err.print("Warning in " + warning.getOrigin() + ":\n "); + System.err.println("Warning in " + warning.getOrigin() + ":"); + System.err.print(" "); } else { System.err.print("Warning: "); } @@ -53,7 +54,10 @@ */ default void info(Diagnostic info) { if (info.getOrigin() != Origin.unknown()) { - System.out.print("In " + info.getOrigin() + ":\n "); + System.out.println("Info in " + info.getOrigin() + ":"); + System.out.print(" "); + } else { + System.out.print("Info: "); } System.out.println(info.getDiagnosticMessage()); }
diff --git a/src/main/java/com/android/tools/r8/L8Command.java b/src/main/java/com/android/tools/r8/L8Command.java index dce2b21..fff3341 100644 --- a/src/main/java/com/android/tools/r8/L8Command.java +++ b/src/main/java/com/android/tools/r8/L8Command.java
@@ -168,7 +168,7 @@ // Assert some of R8 optimizations are disabled. assert !internal.enableInlining; assert !internal.enableClassInlining; - assert !internal.enableHorizontalClassMerging; + assert internal.horizontalClassMergerOptions().isDisabled(); assert !internal.enableStaticClassMerging; assert !internal.enableVerticalClassMerging; assert !internal.enableClassStaticizer;
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java index cfb3540..089c747 100644 --- a/src/main/java/com/android/tools/r8/R8.java +++ b/src/main/java/com/android/tools/r8/R8.java
@@ -33,10 +33,10 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.DirectMappedDexApplication; import com.android.tools.r8.graph.DirectMappedDexApplication.Builder; -import com.android.tools.r8.graph.EnumValueInfoMapCollection; import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.GraphLens.NestedGraphLens; import com.android.tools.r8.graph.InitClassLens; +import com.android.tools.r8.graph.PrunedItems; import com.android.tools.r8.graph.SubtypingInfo; import com.android.tools.r8.graph.analysis.ClassInitializerAssertionEnablingAnalysis; import com.android.tools.r8.graph.analysis.InitializedClassesInInstanceMethodsAnalysis; @@ -61,7 +61,6 @@ import com.android.tools.r8.ir.optimize.UnusedArgumentsCollector; import com.android.tools.r8.ir.optimize.UnusedArgumentsCollector.UnusedArgumentsGraphLens; import com.android.tools.r8.ir.optimize.enums.EnumUnboxingCfMethods; -import com.android.tools.r8.ir.optimize.enums.EnumValueInfoMapCollector; import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple; import com.android.tools.r8.ir.optimize.templates.CfUtilityMethodsForCodeOptimizations; import com.android.tools.r8.jar.CfApplicationWriter; @@ -419,8 +418,12 @@ // Recompute the subtyping information. Set<DexType> removedClasses = pruner.getRemovedClasses(); - appView.removePrunedClasses( - prunedApp, removedClasses, pruner.getMethodsToKeepForConfigurationDebugging()); + appView.pruneItems( + PrunedItems.builder() + .setPrunedApp(prunedApp) + .addRemovedClasses(removedClasses) + .addAdditionalPinnedItems(pruner.getMethodsToKeepForConfigurationDebugging()) + .build()); new AbstractMethodRemover( appViewWithLiveness, appViewWithLiveness.appInfo().computeSubtypingInfo()) .run(); @@ -578,7 +581,9 @@ timing.end(); } } - if (options.enableHorizontalClassMerging && options.enableInlining) { + if (options.horizontalClassMergerOptions().isEnabled() + && options.enableInlining + && options.isShrinking()) { timing.begin("HorizontalClassMerger"); HorizontalClassMerger merger = new HorizontalClassMerger(appViewWithLiveness); DirectMappedDexApplication.Builder appBuilder = @@ -587,7 +592,12 @@ merger.run(appBuilder, mainDexTracingResult, runtimeTypeCheckInfo); if (lens != null) { DirectMappedDexApplication app = appBuilder.build(); - appView.removePrunedClasses(app, appView.horizontallyMergedClasses().getSources()); + appView.pruneItems( + PrunedItems.builder() + .setPrunedApp(app) + .addRemovedClasses(appView.horizontallyMergedClasses().getSources()) + .addNoLongerSyntheticItems(appView.horizontallyMergedClasses().getTargets()) + .build()); appView.rewriteWithLens(lens); // Only required for class merging, clear instance to save memory. @@ -606,18 +616,11 @@ if (options.enableEnumSwitchMapRemoval) { appViewWithLiveness.setAppInfo(new SwitchMapCollector(appViewWithLiveness).run()); } - if (options.enableEnumValueOptimization || options.enableEnumUnboxing) { - appViewWithLiveness.setAppInfo(new EnumValueInfoMapCollector(appViewWithLiveness).run()); - } // Collect the already pruned types before creating a new app info without liveness. // TODO: we should avoid removing liveness. Set<DexType> prunedTypes = appView.withLiveness().appInfo().getPrunedTypes(); - // TODO: move to appview. - EnumValueInfoMapCollection enumValueInfoMapCollection = - appViewWithLiveness.appInfo().getEnumValueInfoMapCollection(); - timing.begin("Create IR"); CfgPrinter printer = options.printCfg ? new CfgPrinter() : null; try { @@ -717,13 +720,11 @@ missingClasses, prunedTypes); appView.setAppInfo( - enqueuer - .traceApplication( - appView.rootSet(), - options.getProguardConfiguration().getDontWarnPatterns(), - executorService, - timing) - .withEnumValueInfoMaps(enumValueInfoMapCollection)); + enqueuer.traceApplication( + appView.rootSet(), + options.getProguardConfiguration().getDontWarnPatterns(), + executorService, + timing)); // Rerunning the enqueuer should not give rise to any method rewritings. assert enqueuer.buildGraphLens() == null; appView.withGeneratedMessageLiteBuilderShrinker( @@ -748,10 +749,12 @@ options.reporter, options.usageInformationConsumer); } - appView.removePrunedClasses( - application, - CollectionUtils.mergeSets(prunedTypes, removedClasses), - pruner.getMethodsToKeepForConfigurationDebugging()); + appView.pruneItems( + PrunedItems.builder() + .setPrunedApp(application) + .addRemovedClasses(CollectionUtils.mergeSets(prunedTypes, removedClasses)) + .addAdditionalPinnedItems(pruner.getMethodsToKeepForConfigurationDebugging()) + .build()); new BridgeHoisting(appViewWithLiveness).run();
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java index 40be618..29112ed 100644 --- a/src/main/java/com/android/tools/r8/R8Command.java +++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -869,7 +869,8 @@ ? LineNumberOptimization.ON : LineNumberOptimization.OFF; - assert proguardConfiguration.isOptimizing() || !internal.enableHorizontalClassMerging; + assert proguardConfiguration.isOptimizing() + || internal.horizontalClassMergerOptions().isDisabled(); assert internal.enableStaticClassMerging || !proguardConfiguration.isOptimizing(); assert !internal.enableTreeShakingOfLibraryMethodOverrides; assert internal.enableVerticalClassMerging || !proguardConfiguration.isOptimizing(); @@ -879,7 +880,7 @@ internal.getProguardConfiguration().getKeepAttributes().localVariableTypeTable = true; internal.enableInlining = false; internal.enableClassInlining = false; - internal.enableHorizontalClassMerging = false; + internal.horizontalClassMergerOptions().disable(); internal.enableStaticClassMerging = false; internal.enableVerticalClassMerging = false; internal.enableClassStaticizer = false; @@ -891,7 +892,7 @@ // If R8 is not shrinking, there is no point in running various optimizations since the // optimized classes will still remain in the program (the application size could increase). internal.enableEnumUnboxing = false; - internal.enableHorizontalClassMerging = false; + internal.horizontalClassMergerOptions().disable(); internal.enableLambdaMerging = false; internal.enableStaticClassMerging = false; internal.enableVerticalClassMerging = false; @@ -900,7 +901,7 @@ if (!internal.enableInlining) { // If R8 cannot perform inlining, then the synthetic constructors would not inline the called // constructors, producing invalid code. - internal.enableHorizontalClassMerging = false; + internal.horizontalClassMergerOptions().disable(); } // Amend the proguard-map consumer with options from the proguard configuration. @@ -958,7 +959,7 @@ if (internal.isGeneratingClassFiles()) { internal.outline.enabled = false; internal.enableEnumUnboxing = false; - internal.enableHorizontalClassMerging = false; + internal.horizontalClassMergerOptions().disable(); } // EXPERIMENTAL flags.
diff --git a/src/main/java/com/android/tools/r8/cf/CfVersion.java b/src/main/java/com/android/tools/r8/cf/CfVersion.java index 100656f..a69cc03 100644 --- a/src/main/java/com/android/tools/r8/cf/CfVersion.java +++ b/src/main/java/com/android/tools/r8/cf/CfVersion.java
@@ -23,6 +23,10 @@ public static final CfVersion V9 = new CfVersion(Opcodes.V9); public static final CfVersion V10 = new CfVersion(Opcodes.V10); public static final CfVersion V11 = new CfVersion(Opcodes.V11); + public static final CfVersion V12 = new CfVersion(Opcodes.V12); + public static final CfVersion V13 = new CfVersion(Opcodes.V13); + public static final CfVersion V14 = new CfVersion(Opcodes.V14); + public static final CfVersion V15 = new CfVersion(Opcodes.V15); private final int version; @@ -68,7 +72,7 @@ @Override public int hashCode() { - return HashCodeVisitor.run(this, CfVersion::specify); + return HashCodeVisitor.run(this); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/CheckCast.java b/src/main/java/com/android/tools/r8/code/CheckCast.java index 32df17b..9f3739a 100644 --- a/src/main/java/com/android/tools/r8/code/CheckCast.java +++ b/src/main/java/com/android/tools/r8/code/CheckCast.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; public class CheckCast extends Format21c<DexType> { @@ -44,8 +45,8 @@ } @Override - int internalCompareBBBB(Format21c<?> other) { - return BBBB.compareTo((DexType) other.BBBB); + void internalSubSpecify(StructuralSpecification<Format21c<DexType>, ?> spec) { + spec.withItem(i -> i.BBBB); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/ConstClass.java b/src/main/java/com/android/tools/r8/code/ConstClass.java index ce1c26b..b96f4aa 100644 --- a/src/main/java/com/android/tools/r8/code/ConstClass.java +++ b/src/main/java/com/android/tools/r8/code/ConstClass.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; public class ConstClass extends Format21c<DexType> { @@ -29,8 +30,8 @@ } @Override - int internalCompareBBBB(Format21c<?> other) { - return BBBB.compareTo((DexType) other.BBBB); + void internalSubSpecify(StructuralSpecification<Format21c<DexType>, ?> spec) { + spec.withItem(i -> i.BBBB); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java index 0409f06..2f05b0b 100644 --- a/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java +++ b/src/main/java/com/android/tools/r8/code/ConstMethodHandle.java
@@ -15,6 +15,7 @@ import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; public class ConstMethodHandle extends Format21c<DexMethodHandle> { @@ -51,8 +52,8 @@ } @Override - int internalCompareBBBB(Format21c<?> other) { - return BBBB.compareTo((DexMethodHandle) other.BBBB); + void internalSubSpecify(StructuralSpecification<Format21c<DexMethodHandle>, ?> spec) { + spec.withItem(i -> i.BBBB); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/ConstMethodType.java b/src/main/java/com/android/tools/r8/code/ConstMethodType.java index 8f54c24..7662fe4 100644 --- a/src/main/java/com/android/tools/r8/code/ConstMethodType.java +++ b/src/main/java/com/android/tools/r8/code/ConstMethodType.java
@@ -14,6 +14,7 @@ import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; public class ConstMethodType extends Format21c<DexProto> { @@ -50,8 +51,8 @@ } @Override - int internalCompareBBBB(Format21c<?> other) { - return BBBB.compareTo((DexProto) other.BBBB); + void internalSubSpecify(StructuralSpecification<Format21c<DexProto>, ?> spec) { + spec.withItem(i -> i.BBBB); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/ConstString.java b/src/main/java/com/android/tools/r8/code/ConstString.java index e9e910d..5fd55e4 100644 --- a/src/main/java/com/android/tools/r8/code/ConstString.java +++ b/src/main/java/com/android/tools/r8/code/ConstString.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; public class ConstString extends Format21c<DexString> { @@ -34,8 +35,8 @@ } @Override - int internalCompareBBBB(Format21c<?> other) { - return BBBB.compareTo((DexString) other.BBBB); + void internalSubSpecify(StructuralSpecification<Format21c<DexString>, ?> spec) { + spec.withItem(i -> i.BBBB); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/DexInitClass.java b/src/main/java/com/android/tools/r8/code/DexInitClass.java index ab39c88..8e85bd0 100644 --- a/src/main/java/com/android/tools/r8/code/DexInitClass.java +++ b/src/main/java/com/android/tools/r8/code/DexInitClass.java
@@ -16,8 +16,9 @@ import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; -import java.util.Comparator; public class DexInitClass extends Base2Format { @@ -28,6 +29,10 @@ private final int dest; private final DexType clazz; + private static void specify(StructuralSpecification<DexInitClass, ?> spec) { + spec.withInt(i -> i.dest).withItem(i -> i.clazz); + } + public DexInitClass(int dest, DexType clazz) { assert clazz.isClassType(); this.dest = dest; @@ -127,10 +132,8 @@ } @Override - final int internalCompareTo(Instruction other) { - return Comparator.comparingInt((DexInitClass i) -> i.dest) - .thenComparing(i -> i.clazz) - .compare(this, (DexInitClass) other); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (DexInitClass) other, DexInitClass::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/code/DexItemBasedConstString.java index 18bdfd3..1a434fd 100644 --- a/src/main/java/com/android/tools/r8/code/DexItemBasedConstString.java +++ b/src/main/java/com/android/tools/r8/code/DexItemBasedConstString.java
@@ -14,6 +14,7 @@ import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; public class DexItemBasedConstString extends Format21c<DexReference> { @@ -68,8 +69,8 @@ } @Override - int internalCompareBBBB(Format21c<?> other) { - return BBBB.referenceCompareTo(((DexItemBasedConstString) other).BBBB); + void internalSubSpecify(StructuralSpecification<Format21c<DexReference>, ?> spec) { + spec.withDexReference(i -> i.BBBB); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java b/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java index 52d3e07..cab0270 100644 --- a/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java +++ b/src/main/java/com/android/tools/r8/code/FillArrayDataPayload.java
@@ -9,11 +9,11 @@ import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; import java.util.Arrays; -import java.util.Comparator; public class FillArrayDataPayload extends Nop { @@ -21,6 +21,10 @@ public final long size; public final short[] data; + private static void specify(StructuralSpecification<FillArrayDataPayload, ?> spec) { + spec.withInt(i -> i.element_width).withLong(i -> i.size).withShortArray(i -> i.data); + } + FillArrayDataPayload(int high, BytecodeStream stream) { super(high, stream); element_width = read16BitValue(stream); @@ -62,11 +66,8 @@ } @Override - final int internalCompareTo(Instruction other) { - return Comparator.comparingInt((FillArrayDataPayload i) -> i.element_width) - .thenComparingLong(i -> i.size) - .thenComparing(i -> i.data, ComparatorUtils::compareShortArray) - .compare(this, (FillArrayDataPayload) other); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (FillArrayDataPayload) other, FillArrayDataPayload::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/FilledNewArray.java b/src/main/java/com/android/tools/r8/code/FilledNewArray.java index a1ee0ba..5198876 100644 --- a/src/main/java/com/android/tools/r8/code/FilledNewArray.java +++ b/src/main/java/com/android/tools/r8/code/FilledNewArray.java
@@ -43,11 +43,6 @@ } @Override - int internalCompareBBBB(Format35c<?> other) { - return BBBB.compareTo((DexType) other.BBBB); - } - - @Override public void collectIndexedItems( IndexedItemCollection indexedItems, ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java index 03594c2..de651f5 100644 --- a/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java +++ b/src/main/java/com/android/tools/r8/code/FilledNewArrayRange.java
@@ -43,11 +43,6 @@ } @Override - int internalCompareBBBB(Format3rc<?> other) { - return BBBB.compareTo((DexType) other.BBBB); - } - - @Override public void collectIndexedItems( IndexedItemCollection indexedItems, ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/code/Format10t.java b/src/main/java/com/android/tools/r8/code/Format10t.java index 528ef92..9b2b11c 100644 --- a/src/main/java/com/android/tools/r8/code/Format10t.java +++ b/src/main/java/com/android/tools/r8/code/Format10t.java
@@ -9,6 +9,7 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.structural.CompareToVisitor; import java.nio.ShortBuffer; abstract class Format10t extends Base1Format { @@ -43,8 +44,8 @@ } @Override - final int internalCompareTo(Instruction other) { - return Byte.compare(AA, ((Format10t) other).AA); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visitInt(AA, ((Format10t) other).AA); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format11n.java b/src/main/java/com/android/tools/r8/code/Format11n.java index 61d845e..27c0d27 100644 --- a/src/main/java/com/android/tools/r8/code/Format11n.java +++ b/src/main/java/com/android/tools/r8/code/Format11n.java
@@ -10,13 +10,18 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; abstract class Format11n extends Base1Format { public final byte A, B; + private static void specify(StructuralSpecification<Format11n, ?> spec) { + spec.withInt(i -> i.A).withInt(i -> i.B); + } + // #+B | vA | op /*package*/ Format11n(int high, BytecodeStream stream) { super(stream); @@ -53,9 +58,8 @@ } @Override - final int internalCompareTo(Instruction other) { - Format11n o = (Format11n) other; - return ComparatorUtils.compareInts(A, o.A, B, o.B); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format11n) other, Format11n::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format11x.java b/src/main/java/com/android/tools/r8/code/Format11x.java index 4509fbd..3cc3b73 100644 --- a/src/main/java/com/android/tools/r8/code/Format11x.java +++ b/src/main/java/com/android/tools/r8/code/Format11x.java
@@ -10,6 +10,7 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.structural.CompareToVisitor; import java.nio.ShortBuffer; abstract class Format11x extends Base1Format { @@ -43,8 +44,8 @@ } @Override - final int internalCompareTo(Instruction other) { - return Short.compare(AA, ((Format11x) other).AA); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visitInt(AA, ((Format11x) other).AA); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format12x.java b/src/main/java/com/android/tools/r8/code/Format12x.java index db7ca5a..f3b80aa 100644 --- a/src/main/java/com/android/tools/r8/code/Format12x.java +++ b/src/main/java/com/android/tools/r8/code/Format12x.java
@@ -10,13 +10,18 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; abstract class Format12x extends Base1Format { public final byte A, B; + private static void specify(StructuralSpecification<Format12x, ?> spec) { + spec.withInt(i -> i.A).withInt(i -> i.B); + } + // vB | vA | op Format12x(int high, BytecodeStream stream) { super(stream); @@ -47,9 +52,8 @@ } @Override - final int internalCompareTo(Instruction other) { - Format12x o = (Format12x) other; - return ComparatorUtils.compareInts(A, o.A, B, o.B); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format12x) other, Format12x::specify); }
diff --git a/src/main/java/com/android/tools/r8/code/Format20t.java b/src/main/java/com/android/tools/r8/code/Format20t.java index 8f34250..c9cd18b 100644 --- a/src/main/java/com/android/tools/r8/code/Format20t.java +++ b/src/main/java/com/android/tools/r8/code/Format20t.java
@@ -9,6 +9,7 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.structural.CompareToVisitor; import java.nio.ShortBuffer; abstract class Format20t extends Base2Format { @@ -43,8 +44,8 @@ } @Override - final int internalCompareTo(Instruction other) { - return Short.compare(AAAA, ((Format20t) other).AAAA); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visitInt(AAAA, ((Format20t) other).AAAA); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format21c.java b/src/main/java/com/android/tools/r8/code/Format21c.java index 074b79a..9416be7 100644 --- a/src/main/java/com/android/tools/r8/code/Format21c.java +++ b/src/main/java/com/android/tools/r8/code/Format21c.java
@@ -6,6 +6,8 @@ import com.android.tools.r8.dex.Constants; import com.android.tools.r8.graph.IndexedDexItem; import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.util.function.BiPredicate; abstract class Format21c<T extends IndexedDexItem> extends Base2Format { @@ -31,14 +33,16 @@ return ((BBBB.hashCode() << 8) | AA) ^ getClass().hashCode(); } + @SuppressWarnings("unchecked") @Override - final int internalCompareTo(Instruction other) { - Format21c<?> o = (Format21c<?>) other; - int aaDiff = Short.compare(AA, o.AA); - return aaDiff != 0 ? aaDiff : internalCompareBBBB(o); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit( + this, + (Format21c<T>) other, + spec -> spec.withInt(i -> i.AA).withSpec(this::internalSubSpecify)); } - abstract int internalCompareBBBB(Format21c<?> other); + abstract void internalSubSpecify(StructuralSpecification<Format21c<T>, ?> spec); @Override public String toString(ClassNameMapper naming) {
diff --git a/src/main/java/com/android/tools/r8/code/Format21h.java b/src/main/java/com/android/tools/r8/code/Format21h.java index 537a020..36b9174 100644 --- a/src/main/java/com/android/tools/r8/code/Format21h.java +++ b/src/main/java/com/android/tools/r8/code/Format21h.java
@@ -9,7 +9,8 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; -import com.android.tools.r8.utils.ComparatorUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; abstract class Format21h extends Base2Format { @@ -17,6 +18,10 @@ public final short AA; public final char BBBB; + private static void specify(StructuralSpecification<Format21h, ?> spec) { + spec.withInt(i -> i.AA).withInt(i -> i.BBBB); + } + // AA | op | BBBB0000[00000000] /*package*/ Format21h(int high, BytecodeStream stream) { super(stream); @@ -48,9 +53,8 @@ } @Override - final int internalCompareTo(Instruction other) { - Format21h o = (Format21h) other; - return ComparatorUtils.compareInts(AA, o.AA, BBBB, o.BBBB); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format21h) other, Format21h::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format21s.java b/src/main/java/com/android/tools/r8/code/Format21s.java index 3f4e0fc..550fdd4 100644 --- a/src/main/java/com/android/tools/r8/code/Format21s.java +++ b/src/main/java/com/android/tools/r8/code/Format21s.java
@@ -10,8 +10,9 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; abstract class Format21s extends Base2Format { @@ -19,6 +20,10 @@ public final short AA; public final short BBBB; + private static void specify(StructuralSpecification<Format21s, ?> spec) { + spec.withInt(i -> i.AA).withInt(i -> i.BBBB); + } + // AA | op | #+BBBB /*package*/ Format21s(int high, BytecodeStream stream) { super(stream); @@ -50,9 +55,8 @@ } @Override - final int internalCompareTo(Instruction other) { - Format21s o = (Format21s) other; - return ComparatorUtils.compareInts(AA, o.AA, BBBB, o.BBBB); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format21s) other, Format21s::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format21t.java b/src/main/java/com/android/tools/r8/code/Format21t.java index e580a1f..bde930d 100644 --- a/src/main/java/com/android/tools/r8/code/Format21t.java +++ b/src/main/java/com/android/tools/r8/code/Format21t.java
@@ -13,7 +13,8 @@ import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; public abstract class Format21t extends Base2Format { @@ -21,6 +22,10 @@ public final short AA; public /* offset */ short BBBB; + private static void specify(StructuralSpecification<Format21t, ?> spec) { + spec.withInt(i -> i.AA).withInt(i -> i.BBBB); + } + // AA | op | +BBBB Format21t(int high, BytecodeStream stream) { super(stream); @@ -52,9 +57,8 @@ } @Override - final int internalCompareTo(Instruction other) { - Format21t o = (Format21t) other; - return ComparatorUtils.compareInts(AA, o.AA, BBBB, o.BBBB); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format21t) other, Format21t::specify); } public abstract Type getType();
diff --git a/src/main/java/com/android/tools/r8/code/Format22b.java b/src/main/java/com/android/tools/r8/code/Format22b.java index 5bbd0da..6f79b50 100644 --- a/src/main/java/com/android/tools/r8/code/Format22b.java +++ b/src/main/java/com/android/tools/r8/code/Format22b.java
@@ -10,8 +10,9 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; public abstract class Format22b extends Base2Format { @@ -20,6 +21,10 @@ public final short BB; public final byte CC; + private static void specify(StructuralSpecification<Format22b, ?> spec) { + spec.withInt(i -> i.AA).withInt(i -> i.BB).withInt(i -> i.CC); + } + // vAA | op | #+CC | VBB /*package*/ Format22b(int high, BytecodeStream stream) { super(stream); @@ -54,9 +59,8 @@ } @Override - final int internalCompareTo(Instruction other) { - Format22b o = (Format22b) other; - return ComparatorUtils.compareInts(AA, o.AA, BB, o.BB, CC, o.CC); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format22b) other, Format22b::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format22c.java b/src/main/java/com/android/tools/r8/code/Format22c.java index bd16c8f..536ec65 100644 --- a/src/main/java/com/android/tools/r8/code/Format22c.java +++ b/src/main/java/com/android/tools/r8/code/Format22c.java
@@ -7,7 +7,8 @@ import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.graph.IndexedDexItem; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.util.function.BiPredicate; public abstract class Format22c<T extends DexReference> extends Base2Format { @@ -16,6 +17,10 @@ public final byte B; public T CCCC; + private static void specify(StructuralSpecification<Format22c<? extends DexReference>, ?> spec) { + spec.withInt(i -> i.A).withInt(i -> i.B).withDexReference(i -> i.CCCC); + } + // vB | vA | op | [type|field]@CCCC /*package*/ Format22c(int high, BytecodeStream stream, T[] map) { super(stream); @@ -38,10 +43,8 @@ } @Override - final int internalCompareTo(Instruction other) { - Format22c<? extends DexReference> o = (Format22c<? extends DexReference>) other; - int diff = ComparatorUtils.compareInts(A, o.A, B, o.B); - return diff != 0 ? diff : CCCC.referenceCompareTo(o.CCCC); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format22c<? extends DexReference>) other, Format22c::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format22s.java b/src/main/java/com/android/tools/r8/code/Format22s.java index 8492eae..efa14d2 100644 --- a/src/main/java/com/android/tools/r8/code/Format22s.java +++ b/src/main/java/com/android/tools/r8/code/Format22s.java
@@ -10,8 +10,9 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; public abstract class Format22s extends Base2Format { @@ -20,6 +21,10 @@ public final byte B; public final short CCCC; + private static void specify(StructuralSpecification<Format22s, ?> spec) { + spec.withInt(i -> i.A).withInt(i -> i.B).withInt(i -> i.CCCC); + } + // vB | vA | op | #+CCCC /*package*/ Format22s(int high, BytecodeStream stream) { super(stream); @@ -54,9 +59,8 @@ } @Override - final int internalCompareTo(Instruction other) { - Format22s o = (Format22s) other; - return ComparatorUtils.compareInts(A, o.A, B, o.B, CCCC, o.CCCC); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format22s) other, Format22s::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format22t.java b/src/main/java/com/android/tools/r8/code/Format22t.java index 5c64988..9688b4e 100644 --- a/src/main/java/com/android/tools/r8/code/Format22t.java +++ b/src/main/java/com/android/tools/r8/code/Format22t.java
@@ -13,7 +13,8 @@ import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; public abstract class Format22t extends Base2Format { @@ -22,6 +23,10 @@ public final byte B; public /* offset */ short CCCC; + private static void specify(StructuralSpecification<Format22t, ?> spec) { + spec.withInt(i -> i.A).withInt(i -> i.B).withInt(i -> i.CCCC); + } + // vB | vA | op | +CCCC Format22t(int high, BytecodeStream stream) { super(stream); @@ -56,9 +61,8 @@ } @Override - final int internalCompareTo(Instruction other) { - Format22t o = (Format22t) other; - return ComparatorUtils.compareInts(A, o.A, B, o.B, CCCC, o.CCCC); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format22t) other, Format22t::specify); } public abstract Type getType();
diff --git a/src/main/java/com/android/tools/r8/code/Format22x.java b/src/main/java/com/android/tools/r8/code/Format22x.java index 626e7f9..1d9d8ba 100644 --- a/src/main/java/com/android/tools/r8/code/Format22x.java +++ b/src/main/java/com/android/tools/r8/code/Format22x.java
@@ -10,7 +10,8 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; abstract class Format22x extends Base2Format { @@ -18,6 +19,10 @@ public final short AA; public final char BBBB; + private static void specify(StructuralSpecification<Format22x, ?> spec) { + spec.withInt(i -> i.AA).withInt(i -> i.BBBB); + } + // AA | op | vBBBB Format22x(int high, BytecodeStream stream) { super(stream); @@ -49,9 +54,8 @@ } @Override - final int internalCompareTo(Instruction other) { - Format22x o = (Format22x) other; - return ComparatorUtils.compareInts(AA, o.AA, BBBB, o.BBBB); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format22x) other, Format22x::specify); }
diff --git a/src/main/java/com/android/tools/r8/code/Format23x.java b/src/main/java/com/android/tools/r8/code/Format23x.java index a87dddf..ca3ef80 100644 --- a/src/main/java/com/android/tools/r8/code/Format23x.java +++ b/src/main/java/com/android/tools/r8/code/Format23x.java
@@ -10,7 +10,8 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; abstract class Format23x extends Base2Format { @@ -19,6 +20,10 @@ public final short BB; public final short CC; + private static void specify(StructuralSpecification<Format23x, ?> spec) { + spec.withInt(i -> i.AA).withInt(i -> i.BB).withInt(i -> i.CC); + } + // vAA | op | vCC | vBB Format23x(int high, BytecodeStream stream) { super(stream); @@ -53,9 +58,8 @@ } @Override - final int internalCompareTo(Instruction other) { - Format23x o = (Format23x) other; - return ComparatorUtils.compareInts(AA, o.AA, BB, o.BB, CC, o.CC); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format23x) other, Format23x::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format30t.java b/src/main/java/com/android/tools/r8/code/Format30t.java index 81dcf86..ba986cc 100644 --- a/src/main/java/com/android/tools/r8/code/Format30t.java +++ b/src/main/java/com/android/tools/r8/code/Format30t.java
@@ -9,6 +9,7 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.structural.CompareToVisitor; import java.nio.ShortBuffer; abstract class Format30t extends Base3Format { @@ -42,8 +43,8 @@ } @Override - final int internalCompareTo(Instruction other) { - return Integer.compare(AAAAAAAA, ((Format30t) other).AAAAAAAA); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visitInt(AAAAAAAA, ((Format30t) other).AAAAAAAA); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format31c.java b/src/main/java/com/android/tools/r8/code/Format31c.java index 672057b..6664662 100644 --- a/src/main/java/com/android/tools/r8/code/Format31c.java +++ b/src/main/java/com/android/tools/r8/code/Format31c.java
@@ -13,6 +13,8 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; import java.util.function.BiPredicate; @@ -21,6 +23,10 @@ public final short AA; public DexString BBBBBBBB; + private static void specify(StructuralSpecification<Format31c, ?> spec) { + spec.withInt(i -> i.AA).withItem(i -> i.BBBBBBBB); + } + // vAA | op | string@BBBBlo | string@#+BBBBhi Format31c(int high, BytecodeStream stream, DexString[] map) { super(stream); @@ -51,10 +57,8 @@ } @Override - final int internalCompareTo(Instruction other) { - Format31c o = (Format31c) other; - int diff = Short.compare(AA, o.AA); - return diff != 0 ? diff : BBBBBBBB.compareTo(o.BBBBBBBB); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format31c) other, Format31c::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format31i.java b/src/main/java/com/android/tools/r8/code/Format31i.java index 3d0d27e..c2fa1b1 100644 --- a/src/main/java/com/android/tools/r8/code/Format31i.java +++ b/src/main/java/com/android/tools/r8/code/Format31i.java
@@ -10,7 +10,8 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; abstract class Format31i extends Base3Format { @@ -18,6 +19,10 @@ public final short AA; public final int BBBBBBBB; + private static void specify(StructuralSpecification<Format31i, ?> spec) { + spec.withInt(i -> i.AA).withInt(i -> i.BBBBBBBB); + } + // vAA | op | #+BBBBlo | #+BBBBhi /*package*/ Format31i(int high, BytecodeStream stream) { super(stream); @@ -48,9 +53,8 @@ } @Override - final int internalCompareTo(Instruction other) { - Format31i o = (Format31i) other; - return ComparatorUtils.compareInts(AA, o.AA, BBBBBBBB, o.BBBBBBBB); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format31i) other, Format31i::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format31t.java b/src/main/java/com/android/tools/r8/code/Format31t.java index 8e36d79..f85aca6 100644 --- a/src/main/java/com/android/tools/r8/code/Format31t.java +++ b/src/main/java/com/android/tools/r8/code/Format31t.java
@@ -10,7 +10,8 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; public abstract class Format31t extends Base3Format { @@ -18,6 +19,10 @@ public final short AA; protected /* offset */ int BBBBBBBB; + private static void specify(StructuralSpecification<Format31t, ?> spec) { + spec.withInt(i -> i.AA).withInt(i -> i.BBBBBBBB); + } + // vAA | op | +BBBBlo | +BBBBhi Format31t(int high, BytecodeStream stream) { super(stream); @@ -63,9 +68,8 @@ } @Override - final int internalCompareTo(Instruction other) { - Format31t o = (Format31t) other; - return ComparatorUtils.compareInts(AA, o.AA, BBBBBBBB, o.BBBBBBBB); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format31t) other, Format31t::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format32x.java b/src/main/java/com/android/tools/r8/code/Format32x.java index a5360db..c637383 100644 --- a/src/main/java/com/android/tools/r8/code/Format32x.java +++ b/src/main/java/com/android/tools/r8/code/Format32x.java
@@ -11,7 +11,8 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; abstract class Format32x extends Base3Format { @@ -19,6 +20,10 @@ public final int AAAA; public final int BBBB; + private static void specify(StructuralSpecification<Format32x, ?> spec) { + spec.withInt(i -> i.AAAA).withInt(i -> i.BBBB); + } + // øø | op | AAAA | BBBB Format32x(int high, BytecodeStream stream) { super(stream); @@ -51,9 +56,8 @@ } @Override - final int internalCompareTo(Instruction other) { - Format32x o = (Format32x) other; - return ComparatorUtils.compareInts(AAAA, o.AAAA, BBBB, o.BBBB); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format32x) other, Format32x::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format35c.java b/src/main/java/com/android/tools/r8/code/Format35c.java index 8e446db..e5d2489 100644 --- a/src/main/java/com/android/tools/r8/code/Format35c.java +++ b/src/main/java/com/android/tools/r8/code/Format35c.java
@@ -6,10 +6,12 @@ import com.android.tools.r8.dex.Constants; import com.android.tools.r8.graph.IndexedDexItem; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralItem; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.util.function.BiPredicate; -public abstract class Format35c<T extends IndexedDexItem> extends Base3Format { +public abstract class Format35c<T extends IndexedDexItem & StructuralItem<T>> extends Base3Format { public final byte A; public final byte C; @@ -19,6 +21,17 @@ public final byte G; public T BBBB; + private static <T extends IndexedDexItem & StructuralItem<T>> void specify( + StructuralSpecification<Format35c<T>, ?> spec) { + spec.withInt(i -> i.A) + .withInt(i -> i.C) + .withInt(i -> i.D) + .withInt(i -> i.E) + .withInt(i -> i.F) + .withInt(i -> i.G) + .withItem(i -> i.BBBB); + } + // A | G | op | BBBB | F | E | D | C Format35c(int high, BytecodeStream stream, T[] map) { super(stream); @@ -55,22 +68,12 @@ | G) ^ getClass().hashCode(); } + @SuppressWarnings("unchecked") @Override - final int internalCompareTo(Instruction other) { - Format35c<?> o = (Format35c<?>) other; - int diff = - ComparatorUtils.compareInts( - A, o.A, - C, o.C, - D, o.D, - E, o.E, - F, o.F, - G, o.G); - return diff != 0 ? diff : internalCompareBBBB(o); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format35c<T>) other, Format35c::specify); } - abstract int internalCompareBBBB(Format35c<?> other); - private void appendRegisterArguments(StringBuilder builder, String separator) { builder.append("{ "); int[] values = new int[]{C, D, E, F, G};
diff --git a/src/main/java/com/android/tools/r8/code/Format3rc.java b/src/main/java/com/android/tools/r8/code/Format3rc.java index 9102ecb..0ceeb4a 100644 --- a/src/main/java/com/android/tools/r8/code/Format3rc.java +++ b/src/main/java/com/android/tools/r8/code/Format3rc.java
@@ -6,15 +6,22 @@ import com.android.tools.r8.dex.Constants; import com.android.tools.r8.graph.IndexedDexItem; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralItem; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.util.function.BiPredicate; -public abstract class Format3rc<T extends IndexedDexItem> extends Base3Format { +public abstract class Format3rc<T extends IndexedDexItem & StructuralItem<T>> extends Base3Format { public final short AA; public final char CCCC; public T BBBB; + private static <T extends IndexedDexItem & StructuralItem<T>> void specify( + StructuralSpecification<Format3rc<T>, ?> spec) { + spec.withInt(i -> i.AA).withInt(i -> i.CCCC).withItem(i -> i.BBBB); + } + // AA | op | [meth|type]@BBBBB | CCCC Format3rc(int high, BytecodeStream stream, T[] map) { super(stream); @@ -40,15 +47,12 @@ return ((CCCC << 24) | (BBBB.hashCode() << 4) | AA) ^ getClass().hashCode(); } + @SuppressWarnings("unchecked") @Override - final int internalCompareTo(Instruction other) { - Format3rc<?> o = (Format3rc<?>) other; - int diff = ComparatorUtils.compareInts(AA, o.AA, CCCC, o.CCCC); - return diff != 0 ? diff : internalCompareBBBB(o); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format3rc<T>) other, Format3rc::specify); } - abstract int internalCompareBBBB(Format3rc<?> other); - private void appendRegisterRange(StringBuilder builder) { int firstRegister = CCCC; builder.append("{ ");
diff --git a/src/main/java/com/android/tools/r8/code/Format45cc.java b/src/main/java/com/android/tools/r8/code/Format45cc.java index bb0e1c7..1542015 100644 --- a/src/main/java/com/android/tools/r8/code/Format45cc.java +++ b/src/main/java/com/android/tools/r8/code/Format45cc.java
@@ -16,7 +16,8 @@ import com.android.tools.r8.ir.code.Invoke.Type; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; /** Format45cc for instructions of size 4, with 5 registers and 2 constant pool index. */ @@ -31,6 +32,17 @@ public DexMethod BBBB; public DexProto HHHH; + private static void specify(StructuralSpecification<Format45cc, ?> spec) { + spec.withInt(i -> i.A) + .withInt(i -> i.C) + .withInt(i -> i.D) + .withInt(i -> i.E) + .withInt(i -> i.F) + .withInt(i -> i.G) + .withItem(i -> i.BBBB) + .withItem(i -> i.HHHH); + } + Format45cc(int high, BytecodeStream stream, DexMethod[] methodMap, DexProto[] protoMap) { super(stream); G = (byte) (high & 0xf); @@ -77,21 +89,8 @@ } @Override - final int internalCompareTo(Instruction other) { - Format45cc o = (Format45cc) other; - int diff = - ComparatorUtils.compareInts( - A, o.A, - C, o.C, - D, o.D, - E, o.E, - F, o.F, - G, o.G); - if (diff != 0) { - return diff; - } - int bDiff = BBBB.compareTo(o.BBBB); - return bDiff != 0 ? bDiff : HHHH.compareTo(o.HHHH); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format45cc) other, Format45cc::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format4rcc.java b/src/main/java/com/android/tools/r8/code/Format4rcc.java index f29357d..4da5b40 100644 --- a/src/main/java/com/android/tools/r8/code/Format4rcc.java +++ b/src/main/java/com/android/tools/r8/code/Format4rcc.java
@@ -15,8 +15,9 @@ import com.android.tools.r8.ir.code.Invoke.Type; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; -import java.util.Comparator; import java.util.function.BiPredicate; /** Format4rcc for instructions of size 4, with a range of registers and 2 constant pool index. */ @@ -27,6 +28,10 @@ public DexMethod BBBB; public DexProto HHHH; + private static void specify(StructuralSpecification<Format4rcc, ?> spec) { + spec.withInt(i -> i.AA).withInt(i -> i.CCCC).withItem(i -> i.BBBB).withItem(i -> i.HHHH); + } + // AA | op | [meth]@BBBB | CCCC | [proto]@HHHH Format4rcc(int high, BytecodeStream stream, DexMethod[] methodMap, DexProto[] protoMap) { super(stream); @@ -70,12 +75,8 @@ } @Override - final int internalCompareTo(Instruction other) { - return Comparator.comparingInt((Format4rcc i) -> i.AA) - .thenComparingInt(i -> i.CCCC) - .thenComparing(i -> i.BBBB) - .thenComparing(i -> i.HHHH) - .compare(this, (Format4rcc) other); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format4rcc) other, Format4rcc::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Format51l.java b/src/main/java/com/android/tools/r8/code/Format51l.java index 6c583c4..1417bf8 100644 --- a/src/main/java/com/android/tools/r8/code/Format51l.java +++ b/src/main/java/com/android/tools/r8/code/Format51l.java
@@ -10,14 +10,19 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; -import java.util.Comparator; abstract class Format51l extends Base5Format { public final short AA; public final long BBBBBBBBBBBBBBBB; + private static void specify(StructuralSpecification<Format51l, ?> spec) { + spec.withInt(i -> i.AA).withLong(i -> i.BBBBBBBBBBBBBBBB); + } + // AA | op | BBBB | BBBB | BBBB | BBBB Format51l(int high, BytecodeStream stream) { super(stream); @@ -48,10 +53,8 @@ } @Override - final int internalCompareTo(Instruction other) { - return Comparator.comparingInt((Format51l i) -> i.AA) - .thenComparingLong(i -> i.BBBBBBBBBBBBBBBB) - .compare(this, (Format51l) other); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (Format51l) other, Format51l::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Instruction.java b/src/main/java/com/android/tools/r8/code/Instruction.java index 52aa90d..977dafd 100644 --- a/src/main/java/com/android/tools/r8/code/Instruction.java +++ b/src/main/java/com/android/tools/r8/code/Instruction.java
@@ -6,6 +6,7 @@ import com.android.tools.r8.cf.code.CfInstruction; import com.android.tools.r8.dex.IndexedItemCollection; import com.android.tools.r8.errors.InternalCompilerError; +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.DexMethod; @@ -19,10 +20,15 @@ import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.Equatable; +import com.android.tools.r8.utils.structural.HashingVisitor; +import com.android.tools.r8.utils.structural.StructuralItem; +import com.android.tools.r8.utils.structural.StructuralMapping; import java.nio.ShortBuffer; import java.util.function.BiPredicate; -public abstract class Instruction implements CfOrDexInstruction, Comparable<Instruction> { +public abstract class Instruction implements CfOrDexInstruction, StructuralItem<Instruction> { public static final Instruction[] EMPTY_ARRAY = {}; public final static int[] NO_TARGETS = null; @@ -294,26 +300,39 @@ @Override public final boolean equals(Object other) { - return other instanceof Instruction && compareTo((Instruction) other) == 0; + return Equatable.equalsImpl(this, other); } @Override public abstract int hashCode(); + @Override + public Instruction self() { + return this; + } + + @Override + public StructuralMapping<Instruction> getStructuralMapping() { + throw new Unreachable(); + } + int getCompareToId() { return getOpcode(); } // Abstract compare-to called only if the opcode/compare-id of the instruction matches. - abstract int internalCompareTo(Instruction other); + abstract int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor); @Override - public final int compareTo(Instruction other) { - if (this == other) { - return 0; - } - int opcodeDiff = Integer.compare(getCompareToId(), other.getCompareToId()); - return opcodeDiff != 0 ? opcodeDiff : internalCompareTo(other); + public final int acceptCompareTo(Instruction other, CompareToVisitor visitor) { + int opcodeDiff = visitor.visitInt(getCompareToId(), other.getCompareToId()); + return opcodeDiff != 0 ? opcodeDiff : internalAcceptCompareTo(other, visitor); + } + + @Override + public final void acceptHashing(HashingVisitor visitor) { + // Rather than traverse the full instruction, the compare ID will likely give a reasonable hash. + visitor.visitInt(getCompareToId()); } public abstract String getName();
diff --git a/src/main/java/com/android/tools/r8/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/code/InvokeCustom.java index fb6660a..1355f13 100644 --- a/src/main/java/com/android/tools/r8/code/InvokeCustom.java +++ b/src/main/java/com/android/tools/r8/code/InvokeCustom.java
@@ -44,11 +44,6 @@ } @Override - int internalCompareBBBB(Format35c<?> other) { - return BBBB.compareTo((DexCallSite) other.BBBB); - } - - @Override public void collectIndexedItems( IndexedItemCollection indexedItems, ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java b/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java index 71e8399..2d09e4f 100644 --- a/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java +++ b/src/main/java/com/android/tools/r8/code/InvokeCustomRange.java
@@ -44,11 +44,6 @@ } @Override - int internalCompareBBBB(Format3rc<?> other) { - return BBBB.compareTo((DexCallSite) other.BBBB); - } - - @Override public void collectIndexedItems( IndexedItemCollection indexedItems, ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/code/InvokeMethod.java index ec8e6c4..9e5d8f2 100644 --- a/src/main/java/com/android/tools/r8/code/InvokeMethod.java +++ b/src/main/java/com/android/tools/r8/code/InvokeMethod.java
@@ -42,11 +42,6 @@ public abstract Invoke.Type getInvokeType(); @Override - int internalCompareBBBB(Format35c<?> other) { - return BBBB.compareTo((DexMethod) other.BBBB); - } - - @Override public void write( ShortBuffer dest, ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/code/InvokeMethodRange.java b/src/main/java/com/android/tools/r8/code/InvokeMethodRange.java index 19c2bf1..ba248e0 100644 --- a/src/main/java/com/android/tools/r8/code/InvokeMethodRange.java +++ b/src/main/java/com/android/tools/r8/code/InvokeMethodRange.java
@@ -42,11 +42,6 @@ public abstract Type getInvokeType(); @Override - int internalCompareBBBB(Format3rc<?> other) { - return BBBB.compareTo((DexMethod) other.BBBB); - } - - @Override public void write( ShortBuffer dest, ProgramMethod context,
diff --git a/src/main/java/com/android/tools/r8/code/NewInstance.java b/src/main/java/com/android/tools/r8/code/NewInstance.java index 0b90d1a..44d2613 100644 --- a/src/main/java/com/android/tools/r8/code/NewInstance.java +++ b/src/main/java/com/android/tools/r8/code/NewInstance.java
@@ -12,6 +12,7 @@ import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.ir.conversion.IRBuilder; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; public class NewInstance extends Format21c<DexType> { @@ -44,8 +45,8 @@ } @Override - int internalCompareBBBB(Format21c<?> other) { - return BBBB.compareTo((DexType) other.BBBB); + void internalSubSpecify(StructuralSpecification<Format21c<DexType>, ?> spec) { + spec.withItem(i -> i.BBBB); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/Nop.java b/src/main/java/com/android/tools/r8/code/Nop.java index ae56ec8..1e46cfc 100644 --- a/src/main/java/com/android/tools/r8/code/Nop.java +++ b/src/main/java/com/android/tools/r8/code/Nop.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.code; import com.android.tools.r8.ir.conversion.IRBuilder; +import com.android.tools.r8.utils.structural.CompareToVisitor; public class Nop extends Format10x { @@ -33,7 +34,7 @@ // Notice that this must be overridden by the "Nop" subtypes! @Override - int internalCompareTo(Instruction other) { + int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { return DexCompareHelper.compareIdUniquelyDeterminesEquality(this, other); }
diff --git a/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java b/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java index 4ea070c..3d297bb 100644 --- a/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java +++ b/src/main/java/com/android/tools/r8/code/PackedSwitchPayload.java
@@ -8,11 +8,11 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; import java.util.Arrays; -import java.util.Comparator; public class PackedSwitchPayload extends SwitchPayload { @@ -20,6 +20,10 @@ public final int first_key; public final /* offset */ int[] targets; + private static void specify(StructuralSpecification<PackedSwitchPayload, ?> spec) { + spec.withInt(i -> i.size).withInt(i -> i.first_key).withIntArray(i -> i.targets); + } + public PackedSwitchPayload(int high, BytecodeStream stream) { super(high, stream); size = read16BitValue(stream); @@ -58,11 +62,8 @@ } @Override - final int internalCompareTo(Instruction other) { - return Comparator.comparingInt((PackedSwitchPayload i) -> i.size) - .thenComparingInt(i -> first_key) - .thenComparing(i -> i.targets, ComparatorUtils::compareIntArray) - .compare(this, (PackedSwitchPayload) other); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (PackedSwitchPayload) other, PackedSwitchPayload::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/code/ReturnVoid.java b/src/main/java/com/android/tools/r8/code/ReturnVoid.java index 68cfb51..32d75f8 100644 --- a/src/main/java/com/android/tools/r8/code/ReturnVoid.java +++ b/src/main/java/com/android/tools/r8/code/ReturnVoid.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.code; import com.android.tools.r8.ir.conversion.IRBuilder; +import com.android.tools.r8.utils.structural.CompareToVisitor; public class ReturnVoid extends Format10x { @@ -33,7 +34,7 @@ } @Override - final int internalCompareTo(Instruction other) { + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { return DexCompareHelper.compareIdUniquelyDeterminesEquality(this, other); }
diff --git a/src/main/java/com/android/tools/r8/code/SgetOrSput.java b/src/main/java/com/android/tools/r8/code/SgetOrSput.java index 4524f8e..499d679 100644 --- a/src/main/java/com/android/tools/r8/code/SgetOrSput.java +++ b/src/main/java/com/android/tools/r8/code/SgetOrSput.java
@@ -9,6 +9,7 @@ import com.android.tools.r8.graph.ObjectToOffsetMapping; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; abstract class SgetOrSput extends Format21c<DexField> { @@ -49,7 +50,7 @@ } @Override - int internalCompareBBBB(Format21c<?> other) { - return BBBB.compareTo((DexField) other.BBBB); + void internalSubSpecify(StructuralSpecification<Format21c<DexField>, ?> spec) { + spec.withItem(i -> i.BBBB); } }
diff --git a/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java b/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java index 1a1b1e9..9dac193e 100644 --- a/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java +++ b/src/main/java/com/android/tools/r8/code/SparseSwitchPayload.java
@@ -8,11 +8,11 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.naming.ClassNameMapper; -import com.android.tools.r8.utils.ComparatorUtils; import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.StructuralSpecification; import java.nio.ShortBuffer; import java.util.Arrays; -import java.util.Comparator; public class SparseSwitchPayload extends SwitchPayload { @@ -20,6 +20,10 @@ public final int[] keys; public final /* offset */ int[] targets; + private static void specify(StructuralSpecification<SparseSwitchPayload, ?> spec) { + spec.withInt(i -> i.size).withIntArray(i -> i.keys).withIntArray(i -> i.targets); + } + public SparseSwitchPayload(int high, BytecodeStream stream) { super(high, stream); size = read16BitValue(stream); @@ -64,11 +68,8 @@ } @Override - final int internalCompareTo(Instruction other) { - return Comparator.comparingInt((SparseSwitchPayload i) -> i.size) - .thenComparing(i -> i.keys, ComparatorUtils::compareIntArray) - .thenComparing(i -> i.targets, ComparatorUtils::compareIntArray) - .compare(this, (SparseSwitchPayload) other); + final int internalAcceptCompareTo(Instruction other, CompareToVisitor visitor) { + return visitor.visit(this, (SparseSwitchPayload) other, SparseSwitchPayload::specify); } @Override
diff --git a/src/main/java/com/android/tools/r8/dex/Constants.java b/src/main/java/com/android/tools/r8/dex/Constants.java index e3801e6..dfa8667 100644 --- a/src/main/java/com/android/tools/r8/dex/Constants.java +++ b/src/main/java/com/android/tools/r8/dex/Constants.java
@@ -140,6 +140,7 @@ public static final String JAVA_LANG_OBJECT_NAME = "java/lang/Object"; public static final String INSTANCE_INITIALIZER_NAME = "<init>"; public static final String CLASS_INITIALIZER_NAME = "<clinit>"; + public static final String TEMPORARY_INSTANCE_INITIALIZER_PREFIX = "$r8$constructor"; public static final int MAX_NON_JUMBO_INDEX = U16BIT_MAX;
diff --git a/src/main/java/com/android/tools/r8/errors/ExperimentalClassFileVersionDiagnostic.java b/src/main/java/com/android/tools/r8/errors/ExperimentalClassFileVersionDiagnostic.java new file mode 100644 index 0000000..953afb2 --- /dev/null +++ b/src/main/java/com/android/tools/r8/errors/ExperimentalClassFileVersionDiagnostic.java
@@ -0,0 +1,35 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.errors; + +import com.android.tools.r8.Diagnostic; +import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.position.Position; + +public class ExperimentalClassFileVersionDiagnostic implements Diagnostic { + + private final String message; + private final Origin origin; + + public ExperimentalClassFileVersionDiagnostic(Origin origin, String message) { + this.origin = origin; + this.message = message; + } + + @Override + public Origin getOrigin() { + return origin; + } + + @Override + public Position getPosition() { + return Position.UNKNOWN; + } + + @Override + public String getDiagnosticMessage() { + return message; + } +}
diff --git a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java index b6588df..ec95ade 100644 --- a/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java +++ b/src/main/java/com/android/tools/r8/features/ClassToFeatureSplitMap.java
@@ -16,6 +16,7 @@ import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.ProgramDefinition; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.graph.PrunedItems; import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.Reporter; import com.google.common.collect.Sets; @@ -135,11 +136,11 @@ return rewrittenClassToFeatureSplitMap; } - public ClassToFeatureSplitMap withoutPrunedClasses(Set<DexType> prunedClasses) { + public ClassToFeatureSplitMap withoutPrunedItems(PrunedItems prunedItems) { ClassToFeatureSplitMap classToFeatureSplitMapAfterPruning = new ClassToFeatureSplitMap(); classToFeatureSplitMap.forEach( (type, featureSplit) -> { - if (!prunedClasses.contains(type)) { + if (!prunedItems.getRemovedClasses().contains(type)) { classToFeatureSplitMapAfterPruning.classToFeatureSplitMap.put(type, featureSplit); } });
diff --git a/src/main/java/com/android/tools/r8/graph/AppServices.java b/src/main/java/com/android/tools/r8/graph/AppServices.java index 7fe273a..c0c3976 100644 --- a/src/main/java/com/android/tools/r8/graph/AppServices.java +++ b/src/main/java/com/android/tools/r8/graph/AppServices.java
@@ -26,7 +26,6 @@ import java.io.IOException; import java.nio.charset.Charset; import java.util.ArrayList; -import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -139,11 +138,11 @@ return new AppServices(appView, rewrittenFeatureMappings.build()); } - public AppServices prunedCopy(Collection<DexType> removedClasses) { + public AppServices prunedCopy(PrunedItems prunedItems) { ImmutableMap.Builder<DexType, Map<FeatureSplit, List<DexType>>> rewrittenServicesBuilder = ImmutableMap.builder(); for (Entry<DexType, Map<FeatureSplit, List<DexType>>> entry : services.entrySet()) { - if (removedClasses.contains(entry.getKey())) { + if (prunedItems.getRemovedClasses().contains(entry.getKey())) { continue; } ImmutableMap.Builder<FeatureSplit, List<DexType>> prunedFeatureSplitImpls = @@ -152,7 +151,7 @@ ImmutableList.Builder<DexType> rewrittenServiceImplementationTypesBuilder = ImmutableList.builder(); for (DexType serviceImplementationType : featureSplitEntry.getValue()) { - if (!removedClasses.contains(serviceImplementationType)) { + if (!prunedItems.getRemovedClasses().contains(serviceImplementationType)) { rewrittenServiceImplementationTypesBuilder.add(serviceImplementationType); } }
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java index ce2d937..8e0800b 100644 --- a/src/main/java/com/android/tools/r8/graph/AppView.java +++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -22,6 +22,7 @@ import com.android.tools.r8.ir.conversion.MethodProcessingId; import com.android.tools.r8.ir.desugar.PrefixRewritingMapper; import com.android.tools.r8.ir.optimize.CallSiteOptimizationInfoPropagator; +import com.android.tools.r8.ir.optimize.enums.EnumDataMap; import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory; import com.android.tools.r8.ir.optimize.library.LibraryMemberOptimizer; import com.android.tools.r8.ir.optimize.library.LibraryMethodSideEffectModelCollection; @@ -38,8 +39,6 @@ import com.android.tools.r8.utils.ThrowingConsumer; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableSet; -import java.util.Collection; -import java.util.Collections; import java.util.IdentityHashMap; import java.util.Map; import java.util.Set; @@ -87,7 +86,7 @@ private HorizontallyMergedClasses horizontallyMergedClasses; private StaticallyMergedClasses staticallyMergedClasses; private VerticallyMergedClasses verticallyMergedClasses; - private EnumValueInfoMapCollection unboxedEnums = EnumValueInfoMapCollection.empty(); + private EnumDataMap unboxedEnums = EnumDataMap.empty(); // TODO(b/169115389): Remove private Set<DexMethod> cfByteCodePassThrough = ImmutableSet.of(); @@ -95,7 +94,7 @@ // When input has been (partially) desugared these are the classes which has been library // desugared. This information is populated in the IR converter. - private Set<DexProgramClass> alreadyLibraryDesugared = null; + private Set<DexType> alreadyLibraryDesugared = null; private AppView( T appInfo, @@ -512,18 +511,18 @@ testing().verticallyMergedClassesConsumer.accept(dexItemFactory(), verticallyMergedClasses); } - public EnumValueInfoMapCollection unboxedEnums() { + public EnumDataMap unboxedEnums() { return unboxedEnums; } - public void setUnboxedEnums(EnumValueInfoMapCollection unboxedEnums) { + public void setUnboxedEnums(EnumDataMap unboxedEnums) { assert this.unboxedEnums.isEmpty(); this.unboxedEnums = unboxedEnums; testing().unboxedEnumsConsumer.accept(dexItemFactory(), unboxedEnums); } public boolean validateUnboxedEnumsHaveBeenPruned() { - for (DexType unboxedEnum : unboxedEnums.enumSet()) { + for (DexType unboxedEnum : unboxedEnums.getUnboxedEnums()) { assert appInfo.definitionForWithoutExistenceAssert(unboxedEnum) == null : "Enum " + unboxedEnum + " has been unboxed but is still in the program."; assert appInfo().withLiveness().wasPruned(unboxedEnum) @@ -569,35 +568,17 @@ return !cfByteCodePassThrough.isEmpty(); } - public void removePrunedClasses( - DirectMappedDexApplication prunedApp, Set<DexType> removedClasses) { - removePrunedClasses(prunedApp, removedClasses, Collections.emptySet()); + public void pruneItems(PrunedItems prunedItems) { + pruneItems(prunedItems, withLiveness()); } - public void removePrunedClasses( - DirectMappedDexApplication prunedApp, - Set<DexType> removedClasses, - Collection<DexMethod> methodsToKeepForConfigurationDebugging) { - assert enableWholeProgramOptimizations(); - assert appInfo().hasLiveness(); - removePrunedClasses( - prunedApp, removedClasses, methodsToKeepForConfigurationDebugging, withLiveness()); - } - - private static void removePrunedClasses( - DirectMappedDexApplication prunedApp, - Set<DexType> removedClasses, - Collection<DexMethod> methodsToKeepForConfigurationDebugging, - AppView<AppInfoWithLiveness> appView) { - if (removedClasses.isEmpty() && !appView.options().configurationDebugging) { - assert appView.appInfo.app() == prunedApp; + private static void pruneItems(PrunedItems prunedItems, AppView<AppInfoWithLiveness> appView) { + if (!prunedItems.hasRemovedClasses() && !appView.options().configurationDebugging) { + assert appView.appInfo().app() == prunedItems.getPrunedApp(); return; } - appView.setAppInfo( - appView - .appInfo() - .prunedCopyFrom(prunedApp, removedClasses, methodsToKeepForConfigurationDebugging)); - appView.setAppServices(appView.appServices().prunedCopy(removedClasses)); + appView.setAppInfo(appView.appInfo().prunedCopyFrom(prunedItems)); + appView.setAppServices(appView.appServices().prunedCopy(prunedItems)); } public void rewriteWithLens(NonIdentityGraphLens lens) { @@ -661,7 +642,7 @@ }); } - public void setAlreadyLibraryDesugared(Set<DexProgramClass> alreadyLibraryDesugared) { + public void setAlreadyLibraryDesugared(Set<DexType> alreadyLibraryDesugared) { assert this.alreadyLibraryDesugared == null; this.alreadyLibraryDesugared = alreadyLibraryDesugared; } @@ -671,6 +652,6 @@ return false; } assert alreadyLibraryDesugared != null; - return alreadyLibraryDesugared.contains(clazz); + return alreadyLibraryDesugared.contains(clazz.getType()); } }
diff --git a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java index 887f10d..559d2f2 100644 --- a/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java +++ b/src/main/java/com/android/tools/r8/graph/AppliedGraphLens.java
@@ -6,7 +6,8 @@ import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens; import com.android.tools.r8.utils.MapUtils; -import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap; +import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableList; @@ -25,8 +26,8 @@ */ public final class AppliedGraphLens extends NonIdentityGraphLens { - private final BidirectionalManyToOneMap<DexType, DexType> renamedTypeNames = - new BidirectionalManyToOneMap<>(); + private final MutableBidirectionalManyToOneMap<DexType, DexType> renamedTypeNames = + new BidirectionalManyToOneHashMap<>(); private final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create(); private final BiMap<DexMethod, DexMethod> originalMethodSignatures = HashBiMap.create(); @@ -102,11 +103,8 @@ @Override public Iterable<DexType> getOriginalTypes(DexType type) { - Set<DexType> originalTypes = renamedTypeNames.getKeysOrNull(type); - if (originalTypes == null) { - return ImmutableList.of(type); - } - return originalTypes; + Set<DexType> originalTypes = renamedTypeNames.getKeys(type); + return originalTypes.isEmpty() ? ImmutableList.of(type) : originalTypes; } @Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java index fbd5e7a..719e2d6 100644 --- a/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java +++ b/src/main/java/com/android/tools/r8/graph/DexClassAndMember.java
@@ -39,6 +39,10 @@ return definition; } + public DexString getName() { + return getReference().getName(); + } + public R getReference() { return definition.getReference(); }
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 ab974b3..2e42ccf 100644 --- a/src/main/java/com/android/tools/r8/graph/DexCode.java +++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -3,8 +3,6 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.graph; -import static com.android.tools.r8.utils.ComparatorUtils.arrayComparator; - import com.android.tools.r8.code.Instruction; import com.android.tools.r8.code.ReturnVoid; import com.android.tools.r8.code.SwitchPayload; @@ -22,13 +20,16 @@ import com.android.tools.r8.ir.conversion.MethodProcessor; import com.android.tools.r8.naming.ClassNameMapper; import com.android.tools.r8.origin.Origin; -import com.android.tools.r8.utils.ComparatorUtils; import com.android.tools.r8.utils.StringUtils; +import com.android.tools.r8.utils.structural.Equatable; +import com.android.tools.r8.utils.structural.HashCodeVisitor; +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.base.Strings; import it.unimi.dsi.fastutil.ints.Int2IntMap; import java.util.Arrays; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -36,7 +37,7 @@ import java.util.Set; // DexCode corresponds to code item in dalvik/dex-format.html -public class DexCode extends Code implements Comparable<DexCode> { +public class DexCode extends Code implements StructuralItem<DexCode> { static final String FAKE_THIS_PREFIX = "_"; static final String FAKE_THIS_SUFFIX = "this"; @@ -52,6 +53,16 @@ private DexDebugInfo debugInfo; private DexDebugInfoForWriting debugInfoForWriting; + private static void specify(StructuralSpecification<DexCode, ?> spec) { + spec.withInt(c -> c.registerSize) + .withInt(c -> c.incomingRegisterSize) + .withInt(c -> c.outgoingRegisterSize) + .withItemArray(c -> c.tries) + .withItemArray(c -> c.handlers) + .withNullableItem(c -> c.debugInfo) + .withItemArray(c -> c.instructions); + } + public DexCode( int registerSize, int insSize, @@ -73,6 +84,16 @@ hashCode(); // Cache the hash code eagerly. } + @Override + public DexCode self() { + return this; + } + + @Override + public StructuralMapping<DexCode> getStructuralMapping() { + return DexCode::specify; + } + public DexCode withoutThisParameter() { // Note that we assume the original code has a register associated with 'this' // argument of the (former) instance method. We also assume (but do not check) @@ -183,26 +204,8 @@ } @Override - public int compareTo(DexCode other) { - if (this == other) { - return 0; - } - int diff = - Comparator.comparingInt((DexCode c) -> c.incomingRegisterSize) - .thenComparingInt(c -> c.registerSize) - .thenComparingInt(c -> c.outgoingRegisterSize) - .thenComparing(c -> c.tries, arrayComparator()) - .thenComparing(c -> c.handlers, arrayComparator()) - .thenComparing(c -> c.debugInfo, Comparator.nullsFirst(DexDebugInfo::compareTo)) - .thenComparing((DexCode c) -> c.instructions, arrayComparator()) - .compare(this, other); - assert (diff == 0) == (0 == toString().compareTo(other.toString())); - return diff; - } - - @Override public boolean computeEquals(Object other) { - return other instanceof DexCode && compareTo((DexCode) other) == 0; + return Equatable.equalsImpl(this, other); } @Override @@ -453,7 +456,7 @@ return last.getOffset() + last.getSize(); } - public static class Try extends DexItem implements Comparable<Try> { + public static class Try extends DexItem implements StructuralItem<Try> { public static final int NO_INDEX = -1; @@ -462,6 +465,13 @@ public /* offset */ int instructionCount; public int handlerIndex; + private static void specify(StructuralSpecification<Try, ?> spec) { + // The handler offset is the offset given by the dex input and does not determine the item. + spec.withInt(t -> t.startAddress) + .withInt(t -> t.instructionCount) + .withInt(t -> t.handlerIndex); + } + public Try(int startAddress, int instructionCount, int handlerOffset) { this.startAddress = startAddress; this.instructionCount = instructionCount; @@ -469,6 +479,16 @@ this.handlerIndex = NO_INDEX; } + @Override + public Try self() { + return this; + } + + @Override + public StructuralMapping<Try> getStructuralMapping() { + return Try::specify; + } + public void setHandlerIndex(Int2IntMap map) { handlerIndex = map.get(handlerOffset); } @@ -480,18 +500,7 @@ @Override public boolean equals(Object other) { - return other instanceof Try && compareTo((Try) other) == 0; - } - - @Override - public int compareTo(Try other) { - if (this == other) { - return 0; - } - return ComparatorUtils.compareInts( - startAddress, other.startAddress, - instructionCount, other.instructionCount, - handlerIndex, other.handlerIndex); + return Equatable.equalsImpl(this, other); } @Override @@ -512,36 +521,40 @@ } - public static class TryHandler extends DexItem implements Comparable<TryHandler> { + public static class TryHandler extends DexItem implements StructuralItem<TryHandler> { public static final int NO_HANDLER = -1; public final TypeAddrPair[] pairs; public final /* offset */ int catchAllAddr; + private static void specify(StructuralSpecification<TryHandler, ?> spec) { + spec.withInt(h -> h.catchAllAddr).withItemArray(h -> h.pairs); + } + public TryHandler(TypeAddrPair[] pairs, int catchAllAddr) { this.pairs = pairs; this.catchAllAddr = catchAllAddr; } @Override + public TryHandler self() { + return this; + } + + @Override + public StructuralMapping<TryHandler> getStructuralMapping() { + return TryHandler::specify; + } + + @Override public int hashCode() { - return catchAllAddr + Arrays.hashCode(pairs) * 7; + return HashCodeVisitor.run(this); } @Override public boolean equals(Object other) { - return other instanceof TryHandler && compareTo((TryHandler) other) == 0; - } - - @Override - public int compareTo(TryHandler other) { - if (this == other) { - return 0; - } - return Comparator.comparingInt((TryHandler h) -> h.catchAllAddr) - .thenComparing(h -> h.pairs, arrayComparator()) - .compare(this, other); + return Equatable.equalsImpl(this, other); } public void collectIndexedItems(IndexedItemCollection indexedItems, GraphLens graphLens) { @@ -576,16 +589,30 @@ return builder.toString(); } - public static class TypeAddrPair extends DexItem implements Comparable<TypeAddrPair> { + public static class TypeAddrPair extends DexItem implements StructuralItem<TypeAddrPair> { private final DexType type; public final /* offset */ int addr; + private static void specify(StructuralSpecification<TypeAddrPair, ?> spec) { + spec.withItem(p -> p.type).withInt(p -> p.addr); + } + public TypeAddrPair(DexType type, int addr) { this.type = type; this.addr = addr; } + @Override + public TypeAddrPair self() { + return this; + } + + @Override + public StructuralMapping<TypeAddrPair> getStructuralMapping() { + return TypeAddrPair::specify; + } + public DexType getType() { return type; } @@ -612,17 +639,7 @@ @Override public boolean equals(Object other) { - return other instanceof TypeAddrPair && compareTo((TypeAddrPair) other) == 0; - } - - @Override - public int compareTo(TypeAddrPair other) { - if (this == other) { - return 0; - } - return Comparator.comparingInt((TypeAddrPair p) -> p.addr) - .thenComparing(p -> p.type) - .compare(this, other); + return Equatable.equalsImpl(this, other); } } }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java index 16842b0..9dba226 100644 --- a/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java +++ b/src/main/java/com/android/tools/r8/graph/DexDebugEvent.java
@@ -7,11 +7,16 @@ import com.android.tools.r8.dex.DebugBytecodeWriter; import com.android.tools.r8.dex.IndexedItemCollection; import com.android.tools.r8.dex.MixedSectionCollection; +import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.ir.code.Position; -import java.util.Comparator; +import com.android.tools.r8.utils.structural.CompareToVisitor; +import com.android.tools.r8.utils.structural.HashingVisitor; +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 java.util.Objects; -public abstract class DexDebugEvent extends DexItem implements Comparable<DexDebugEvent> { +public abstract class DexDebugEvent extends DexItem implements StructuralItem<DexDebugEvent> { // Compare ID(s) for virtual debug events. private static final int DBG_SET_INLINE_FRAME_COMPARE_ID = Constants.DBG_LAST_SPECIAL + 1; @@ -41,15 +46,30 @@ abstract int getCompareToId(); - abstract int internalCompareTo(DexDebugEvent other); + abstract int internalAcceptCompareTo(DexDebugEvent other, CompareToVisitor visitor); + + abstract void internalAcceptHashing(HashingVisitor visitor); @Override - public final int compareTo(DexDebugEvent other) { - if (this == other) { - return 0; - } - int diff = Integer.compare(getCompareToId(), other.getCompareToId()); - return diff != 0 ? diff : internalCompareTo(other); + public DexDebugEvent self() { + return this; + } + + @Override + public StructuralMapping<DexDebugEvent> getStructuralMapping() { + throw new Unreachable(); + } + + @Override + public final int acceptCompareTo(DexDebugEvent other, CompareToVisitor visitor) { + int diff = visitor.visitInt(getCompareToId(), other.getCompareToId()); + return diff != 0 ? diff : internalAcceptCompareTo(other, visitor); + } + + @Override + public final void acceptHashing(HashingVisitor visitor) { + visitor.visitInt(getCompareToId()); + internalAcceptHashing(visitor); } public abstract void writeOn(DebugBytecodeWriter writer, ObjectToOffsetMapping mapping); @@ -102,8 +122,13 @@ } @Override - int internalCompareTo(DexDebugEvent other) { - return Integer.compare(delta, ((AdvancePC) other).delta); + int internalAcceptCompareTo(DexDebugEvent other, CompareToVisitor visitor) { + return visitor.visitInt(delta, ((AdvancePC) other).delta); + } + + @Override + void internalAcceptHashing(HashingVisitor visitor) { + visitor.visitInt(delta); } } @@ -139,10 +164,15 @@ } @Override - int internalCompareTo(DexDebugEvent other) { + int internalAcceptCompareTo(DexDebugEvent other, CompareToVisitor visitor) { assert other instanceof SetPrologueEnd; return 0; } + + @Override + void internalAcceptHashing(HashingVisitor visitor) { + // Nothing to hash as the ID has already been hashed. + } } @@ -177,10 +207,15 @@ } @Override - int internalCompareTo(DexDebugEvent other) { + int internalAcceptCompareTo(DexDebugEvent other, CompareToVisitor visitor) { assert other instanceof SetEpilogueBegin; return 0; } + + @Override + void internalAcceptHashing(HashingVisitor visitor) { + // Nothing to hash as the ID has already been hashed. + } } public static class AdvanceLine extends DexDebugEvent { @@ -219,8 +254,13 @@ } @Override - int internalCompareTo(DexDebugEvent other) { - return Integer.compare(delta, ((AdvanceLine) other).delta); + int internalAcceptCompareTo(DexDebugEvent other, CompareToVisitor visitor) { + return visitor.visitInt(delta, ((AdvanceLine) other).delta); + } + + @Override + void internalAcceptHashing(HashingVisitor visitor) { + visitor.visitInt(delta); } } @@ -231,6 +271,13 @@ final DexType type; final DexString signature; + private static void spec(StructuralSpecification<StartLocal, ?> spec) { + spec.withInt(e -> e.registerNum) + .withItem(e -> e.name) + .withItem(e -> e.type) + .withNullableItem(e -> e.signature); + } + public StartLocal( int registerNum, DexString name, @@ -298,12 +345,13 @@ } @Override - int internalCompareTo(DexDebugEvent other) { - return Comparator.comparingInt((StartLocal e) -> e.registerNum) - .thenComparing(e -> e.name) - .thenComparing(e -> e.type) - .thenComparing(e -> e.signature, Comparator.nullsFirst(DexString::compareTo)) - .compare(this, (StartLocal) other); + int internalAcceptCompareTo(DexDebugEvent other, CompareToVisitor visitor) { + return visitor.visit(this, (StartLocal) other, StartLocal::spec); + } + + @Override + void internalAcceptHashing(HashingVisitor visitor) { + visitor.visit(this, StartLocal::spec); } } @@ -343,8 +391,13 @@ } @Override - int internalCompareTo(DexDebugEvent other) { - return Integer.compare(registerNum, ((EndLocal) other).registerNum); + int internalAcceptCompareTo(DexDebugEvent other, CompareToVisitor visitor) { + return visitor.visitInt(registerNum, ((EndLocal) other).registerNum); + } + + @Override + void internalAcceptHashing(HashingVisitor visitor) { + visitor.visitInt(registerNum); } } @@ -384,8 +437,13 @@ } @Override - int internalCompareTo(DexDebugEvent other) { - return Integer.compare(registerNum, ((RestartLocal) other).registerNum); + int internalAcceptCompareTo(DexDebugEvent other, CompareToVisitor visitor) { + return visitor.visitInt(registerNum, ((RestartLocal) other).registerNum); + } + + @Override + void internalAcceptHashing(HashingVisitor visitor) { + visitor.visitInt(registerNum); } } @@ -430,8 +488,13 @@ } @Override - int internalCompareTo(DexDebugEvent other) { - return fileName.compareTo(((SetFile) other).fileName); + int internalAcceptCompareTo(DexDebugEvent other, CompareToVisitor visitor) { + return fileName.acceptCompareTo(((SetFile) other).fileName, visitor); + } + + @Override + void internalAcceptHashing(HashingVisitor visitor) { + fileName.acceptHashing(visitor); } } @@ -440,6 +503,10 @@ final DexMethod callee; final Position caller; + private static void specify(StructuralSpecification<SetInlineFrame, ?> spec) { + spec.withItem(e -> e.callee).withNullableItem(e -> e.caller); + } + SetInlineFrame(DexMethod callee, Position caller) { assert callee != null; this.callee = callee; @@ -472,10 +539,13 @@ } @Override - int internalCompareTo(DexDebugEvent other) { - return Comparator.comparing((SetInlineFrame e) -> e.callee, DexMethod::compareTo) - .thenComparing(e -> e.caller, Comparator.nullsFirst(Position::compareTo)) - .compare(this, (SetInlineFrame) other); + int internalAcceptCompareTo(DexDebugEvent other, CompareToVisitor visitor) { + return visitor.visit(this, (SetInlineFrame) other, SetInlineFrame::specify); + } + + @Override + void internalAcceptHashing(HashingVisitor visitor) { + visitor.visit(this, SetInlineFrame::specify); } @Override @@ -540,8 +610,13 @@ } @Override - int internalCompareTo(DexDebugEvent other) { - return Integer.compare(value, ((Default) other).value); + int internalAcceptCompareTo(DexDebugEvent other, CompareToVisitor visitor) { + return visitor.visitInt(value, ((Default) other).value); + } + + @Override + void internalAcceptHashing(HashingVisitor visitor) { + visitor.visitInt(value); } } }
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java index 5bfbd8d..75588f6 100644 --- a/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java +++ b/src/main/java/com/android/tools/r8/graph/DexDebugInfo.java
@@ -5,17 +5,25 @@ import com.android.tools.r8.dex.IndexedItemCollection; import com.android.tools.r8.dex.MixedSectionCollection; -import com.android.tools.r8.utils.ComparatorUtils; +import com.android.tools.r8.utils.structural.Equatable; +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 java.util.Arrays; -import java.util.Comparator; import java.util.List; -public class DexDebugInfo extends CachedHashValueDexItem implements Comparable<DexDebugInfo> { +public class DexDebugInfo extends CachedHashValueDexItem implements StructuralItem<DexDebugInfo> { public final int startLine; public final DexString[] parameters; public DexDebugEvent[] events; + private static void specify(StructuralSpecification<DexDebugInfo, ?> spec) { + spec.withInt(d -> d.startLine) + .withItemArrayAllowingNullMembers(d -> d.parameters) + .withItemArray(d -> d.events); + } + public DexDebugInfo(int startLine, DexString[] parameters, DexDebugEvent[] events) { assert startLine >= 0; this.startLine = startLine; @@ -26,6 +34,16 @@ hashCode(); } + @Override + public DexDebugInfo self() { + return this; + } + + @Override + public StructuralMapping<DexDebugInfo> getStructuralMapping() { + return DexDebugInfo::specify; + } + public List<DexDebugEntry> computeEntries(DexMethod method) { DexDebugEntryBuilder builder = new DexDebugEntryBuilder(startLine, method); for (DexDebugEvent event : events) { @@ -43,20 +61,7 @@ @Override public final boolean computeEquals(Object other) { - return other instanceof DexDebugInfo && compareTo((DexDebugInfo) other) == 0; - } - - @Override - public final int compareTo(DexDebugInfo other) { - if (this == other) { - return 0; - } - return Comparator.comparingInt((DexDebugInfo i) -> i.startLine) - .thenComparing( - i -> i.parameters, - ComparatorUtils.arrayComparator(Comparator.nullsFirst(DexString::compareTo))) - .thenComparing(i -> i.events, ComparatorUtils.arrayComparator()) - .compare(this, other); + return Equatable.equalsImpl(this, other); } public void collectIndexedItems(IndexedItemCollection indexedItems, GraphLens graphLens) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java index 7d807e9..bcbc160 100644 --- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -514,6 +514,24 @@ return accessFlags.isConstructor() && !accessFlags.isStatic(); } + /** + * Returns true for (private instance) methods that have been created as a result of class merging + * and will be force-inlined into an instance initializer on the enclosing class. + */ + public boolean willBeInlinedIntoInstanceInitializer(DexItemFactory dexItemFactory) { + checkIfObsolete(); + if (getName().startsWith(dexItemFactory.temporaryConstructorMethodPrefix)) { + assert isPrivate(); + assert !isStatic(); + return true; + } + return false; + } + + public boolean isOrWillBeInlinedIntoInstanceInitializer(DexItemFactory dexItemFactory) { + return isInstanceInitializer() || willBeInlinedIntoInstanceInitializer(dexItemFactory); + } + public boolean isDefaultInitializer() { checkIfObsolete(); return isInstanceInitializer() && method.proto.parameters.isEmpty();
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 dac5b16..a93000e 100644 --- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java +++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -188,7 +188,6 @@ public final DexString convertMethodName = createString("convert"); public final DexString wrapperFieldName = createString("wrappedValue"); - public final DexString initMethodName = createString("<init>"); public final DexString getClassMethodName = createString("getClass"); public final DexString finalizeMethodName = createString("finalize"); @@ -290,6 +289,8 @@ public final DexString constructorMethodName = createString(Constants.INSTANCE_INITIALIZER_NAME); public final DexString classConstructorMethodName = createString(Constants.CLASS_INITIALIZER_NAME); + public final DexString temporaryConstructorMethodPrefix = + createString(Constants.TEMPORARY_INSTANCE_INITIALIZER_PREFIX); public final DexString thisName = createString("this"); public final DexString enumValuesFieldName = createString("$VALUES"); @@ -1134,6 +1135,8 @@ public final DexMethod requireNonNull; public final DexMethod requireNonNullWithMessage; public final DexMethod requireNonNullWithMessageSupplier; + public final DexMethod toStringWithObject = + createMethod(objectsType, createProto(stringType, objectType), "toString"); private ObjectsMethods() { DexString requireNonNullMethodName = createString("requireNonNull"); @@ -1357,7 +1360,7 @@ public final DexMethod initWithMessage = createMethod( - illegalArgumentExceptionType, createProto(voidType, stringType), initMethodName); + illegalArgumentExceptionType, createProto(voidType, stringType), constructorMethodName); } /** @@ -1548,6 +1551,8 @@ public final DexMethod toString; private final Set<DexMethod> appendMethods; + private final Set<DexMethod> appendPrimitiveMethods; + private StringBuildingMethods(DexType receiver) { DexString append = createString("append"); @@ -1566,7 +1571,6 @@ appendObject = createMethod(receiver, createProto(receiver, objectType), append); appendString = createMethod(receiver, createProto(receiver, stringType), append); appendStringBuffer = createMethod(receiver, createProto(receiver, stringBufferType), append); - charSequenceConstructor = createMethod(receiver, createProto(voidType, charSequenceType), constructorMethodName); defaultConstructor = createMethod(receiver, createProto(voidType), constructorMethodName); @@ -1591,6 +1595,9 @@ appendObject, appendString, appendStringBuffer); + appendPrimitiveMethods = + ImmutableSet.of( + appendBoolean, appendChar, appendInt, appendDouble, appendFloat, appendLong); constructorMethods = ImmutableSet.of( charSequenceConstructor, defaultConstructor, intConstructor, stringConstructor); @@ -1602,12 +1609,29 @@ return appendMethods.contains(method); } + public boolean isAppendObjectMethod(DexMethod method) { + return method == appendObject; + } + + public boolean isAppendPrimitiveMethod(DexMethod method) { + return appendPrimitiveMethods.contains(method); + } + + public boolean isAppendStringMethod(DexMethod method) { + return method == appendString; + } + + public boolean isConstructorMethod(DexMethod method) { + return constructorMethods.contains(method); + } + public boolean constructorInvokeIsSideEffectFree(InvokeMethod invoke) { DexMethod invokedMethod = invoke.getInvokedMethod(); if (invokedMethod == charSequenceConstructor) { - // NullPointerException - if seq is null. - Value seqValue = invoke.inValues().get(1); - return !seqValue.getType().isNullable(); + // Performs callbacks on the given CharSequence, which may have side effects. + TypeElement charSequenceType = invoke.getArgument(1).getType(); + return charSequenceType.isClassType() + && charSequenceType.asClassType().getClassType() == stringType; } if (invokedMethod == defaultConstructor) { @@ -1926,6 +1950,10 @@ holder); } + public DexMethod createClassInitializer(DexType holder) { + return createMethod(holder, createProto(voidType), classConstructorMethodName); + } + public DexMethod createInstanceInitializerWithFreshProto( DexMethod method, List<DexType> extraTypes, Predicate<DexMethod> isFresh) { assert method.isInstanceInitializer(this);
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java index c91ffdf..8ab4935 100644 --- a/src/main/java/com/android/tools/r8/graph/DexMethod.java +++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -58,6 +58,10 @@ return proto.parameters; } + public DexProto getProto() { + return proto; + } + public DexType getReturnType() { return proto.returnType; }
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 3e92e74..0faef1b 100644 --- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java +++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -756,4 +756,8 @@ public long getChecksum() { return checksumSupplier.getChecksum(this); } + + public ChecksumSupplier getChecksumSupplier() { + return checksumSupplier; + } }
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 8a59614..77976f1 100644 --- a/src/main/java/com/android/tools/r8/graph/DexReference.java +++ b/src/main/java/com/android/tools/r8/graph/DexReference.java
@@ -73,19 +73,4 @@ assert isDexMethod(); return 3; } - - public int referenceCompareTo(DexReference o) { - int typeDiff = referenceTypeOrder() - o.referenceTypeOrder(); - if (typeDiff != 0) { - return typeDiff; - } - if (isDexType()) { - return asDexType().compareTo(o.asDexType()); - } - if (isDexField()) { - return asDexField().compareTo(o.asDexField()); - } - assert isDexMethod(); - return asDexMethod().compareTo(o.asDexMethod()); - } }
diff --git a/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java b/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java deleted file mode 100644 index 362ff7c..0000000 --- a/src/main/java/com/android/tools/r8/graph/EnumValueInfoMapCollection.java +++ /dev/null
@@ -1,139 +0,0 @@ -// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -package com.android.tools.r8.graph; - -import com.google.common.collect.ImmutableMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Set; -import java.util.function.BiConsumer; - -public class EnumValueInfoMapCollection { - - public static EnumValueInfoMapCollection empty() { - return new EnumValueInfoMapCollection(ImmutableMap.of()); - } - - private final Map<DexType, EnumValueInfoMap> maps; - - private EnumValueInfoMapCollection(Map<DexType, EnumValueInfoMap> maps) { - this.maps = maps; - } - - public EnumValueInfoMap getEnumValueInfoMap(DexType type) { - return maps.get(type); - } - - public boolean isEmpty() { - return maps.isEmpty(); - } - - public boolean containsEnum(DexType type) { - return maps.containsKey(type); - } - - public Set<DexType> enumSet() { - return maps.keySet(); - } - - public EnumValueInfoMapCollection rewrittenWithLens(GraphLens lens) { - Builder builder = builder(); - maps.forEach( - (type, map) -> { - DexType dexType = lens.lookupType(type); - // Enum unboxing may have changed the type to int type. - // Do not keep the map for such enums. - if (!dexType.isPrimitiveType()) { - builder.put(dexType, map.rewrittenWithLens(lens)); - } - }); - return builder.build(); - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - - private ImmutableMap.Builder<DexType, EnumValueInfoMap> builder; - - public Builder put(DexType type, EnumValueInfoMap map) { - if (builder == null) { - builder = ImmutableMap.builder(); - } - builder.put(type, map); - return this; - } - - public EnumValueInfoMapCollection build() { - if (builder == null) { - return empty(); - } - return new EnumValueInfoMapCollection(builder.build()); - } - } - - public static final class EnumValueInfoMap { - - private final LinkedHashMap<DexField, EnumValueInfo> map; - - public EnumValueInfoMap(LinkedHashMap<DexField, EnumValueInfo> map) { - this.map = map; - } - - public Set<DexField> enumValues() { - return map.keySet(); - } - - public int size() { - return map.size(); - } - - public boolean hasEnumValueInfo(DexField field) { - return map.containsKey(field); - } - - public EnumValueInfo getEnumValueInfo(DexField field) { - return map.get(field); - } - - public void forEach(BiConsumer<DexField, EnumValueInfo> consumer) { - map.forEach(consumer); - } - - EnumValueInfoMap rewrittenWithLens(GraphLens lens) { - LinkedHashMap<DexField, EnumValueInfo> rewritten = new LinkedHashMap<>(); - map.forEach( - (field, valueInfo) -> - rewritten.put(lens.lookupField(field), valueInfo.rewrittenWithLens(lens))); - return new EnumValueInfoMap(rewritten); - } - } - - public static final class EnumValueInfo { - - // The anonymous subtype of this specific value or the enum type. - public final DexType type; - public final int ordinal; - - public EnumValueInfo(DexType type, int ordinal) { - this.type = type; - this.ordinal = ordinal; - } - - public int convertToInt() { - return ordinal + 1; - } - - EnumValueInfo rewrittenWithLens(GraphLens lens) { - DexType newType = lens.lookupType(type); - if (type == newType) { - return this; - } - return new EnumValueInfo(newType, ordinal); - } - } -}
diff --git a/src/main/java/com/android/tools/r8/graph/GraphLens.java b/src/main/java/com/android/tools/r8/graph/GraphLens.java index 5a10844..0e3d784 100644 --- a/src/main/java/com/android/tools/r8/graph/GraphLens.java +++ b/src/main/java/com/android/tools/r8/graph/GraphLens.java
@@ -4,9 +4,11 @@ package com.android.tools.r8.graph; import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; +import static com.android.tools.r8.horizontalclassmerging.ClassMerger.CLASS_ID_FIELD_NAME; +import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX; +import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_INSTANCE_FIELD_NAME; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.horizontalclassmerging.ClassMerger; import com.android.tools.r8.ir.code.Invoke.Type; import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; import com.android.tools.r8.ir.desugar.InterfaceProcessor.InterfaceProcessorNestedGraphLens; @@ -15,10 +17,12 @@ import com.android.tools.r8.utils.IterableUtils; import com.android.tools.r8.utils.SetUtils; import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap; +import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap; +import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap; import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneRepresentativeMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap; import com.android.tools.r8.utils.collections.ProgramMethodSet; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; @@ -32,6 +36,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.function.Predicate; +import java.util.stream.Collectors; /** * A GraphLens implements a virtual view on top of the graph, used to delay global rewrites until @@ -65,6 +70,10 @@ return reference; } + public R getRewrittenReference(BidirectionalManyToOneRepresentativeMap<R, R> rewritings) { + return rewritings.getOrDefault(reference, reference); + } + public R getRewrittenReference(Map<R, R> rewritings) { return rewritings.getOrDefault(reference, reference); } @@ -77,6 +86,11 @@ return reboundReference; } + public R getRewrittenReboundReference( + BidirectionalManyToOneRepresentativeMap<R, R> rewritings) { + return rewritings.getOrDefault(reboundReference, reboundReference); + } + public R getRewrittenReboundReference(Map<R, R> rewritings) { return rewritings.getOrDefault(reboundReference, reboundReference); } @@ -228,10 +242,10 @@ protected final Map<DexType, DexType> typeMap = new IdentityHashMap<>(); protected final Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>(); - protected final Map<DexField, DexField> fieldMap = new IdentityHashMap<>(); + protected final MutableBidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap = + new BidirectionalManyToOneRepresentativeHashMap<>(); - protected final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create(); - protected final BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures = + protected final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures = new BidirectionalOneToOneHashMap<>(); public void map(DexType from, DexType to) { @@ -248,13 +262,6 @@ methodMap.put(from, to); } - public void map(DexField from, DexField to) { - if (from == to) { - return; - } - fieldMap.put(from, to); - } - public void move(DexMethod from, DexMethod to) { if (from == to) { return; @@ -268,7 +275,6 @@ return; } fieldMap.put(from, to); - originalFieldSignatures.put(to, from); } public GraphLens build(DexItemFactory dexItemFactory) { @@ -283,7 +289,6 @@ typeMap, methodMap, fieldMap, - originalFieldSignatures, originalMethodSignatures, previousLens, dexItemFactory); @@ -611,21 +616,14 @@ continue; } for (DexEncodedField field : clazz.fields()) { - // The field $r8$clinitField may be synthesized by R8 in order to trigger the initialization - // of the enclosing class. It is not present in the input, and therefore we do not require - // that it can be mapped back to the original program. - if (field.field.match(dexItemFactory.objectMembers.clinitField)) { - continue; - } - - // TODO(b/167947782): Should be a general check to see if the field is D8/R8 synthesized. - if (field.getReference().name.toSourceString().equals(ClassMerger.CLASS_ID_FIELD_NAME)) { - continue; - } - - DexField originalField = getOriginalFieldSignature(field.field); + // Fields synthesized by R8 are not present in the input, and therefore we do not require + // that they can be mapped back to the original program. + DexField originalField = getOriginalFieldSignature(field.getReference()); assert originalFields.contains(originalField) - : "Unable to map field `" + field.field.toSourceString() + "` back to original program"; + || isD8R8SynthesizedField(originalField, dexItemFactory) + : "Unable to map field `" + + field.getReference().toSourceString() + + "` back to original program"; } for (DexEncodedMethod method : clazz.methods()) { if (method.isD8R8Synthesized()) { @@ -640,6 +638,22 @@ return true; } + private boolean isD8R8SynthesizedField(DexField field, DexItemFactory dexItemFactory) { + // TODO(b/167947782): Should be a general check to see if the field is D8/R8 synthesized + // instead of relying on field names. + if (field.match(dexItemFactory.objectMembers.clinitField)) { + return true; + } + if (field.getName().toSourceString().equals(CLASS_ID_FIELD_NAME)) { + return true; + } + if (field.getHolderType().toSourceString().contains(LAMBDA_CLASS_NAME_PREFIX) + && field.getName().toSourceString().equals(LAMBDA_INSTANCE_FIELD_NAME)) { + return true; + } + return false; + } + public abstract static class NonIdentityGraphLens extends GraphLens { private final DexItemFactory dexItemFactory; @@ -963,11 +977,10 @@ protected final Map<DexType, DexType> typeMap; protected final Map<DexMethod, DexMethod> methodMap; - protected final Map<DexField, DexField> fieldMap; + protected final BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap; - // Maps that store the original signature of fields and methods that have been affected, for - // example, by vertical class merging. Needed to generate a correct Proguard map in the end. - protected final BiMap<DexField, DexField> originalFieldSignatures; + // Map that store the original signature of methods that have been affected, for example, by + // vertical class merging. Needed to generate a correct Proguard map in the end. protected BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod> originalMethodSignatures; @@ -980,8 +993,7 @@ public NestedGraphLens( Map<DexType, DexType> typeMap, Map<DexMethod, DexMethod> methodMap, - Map<DexField, DexField> fieldMap, - BiMap<DexField, DexField> originalFieldSignatures, + BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap, BidirectionalManyToManyRepresentativeMap<DexMethod, DexMethod> originalMethodSignatures, GraphLens previousLens, DexItemFactory dexItemFactory) { @@ -993,7 +1005,6 @@ this.typeMap = typeMap.isEmpty() ? null : typeMap; this.methodMap = methodMap; this.fieldMap = fieldMap; - this.originalFieldSignatures = originalFieldSignatures; this.originalMethodSignatures = originalMethodSignatures; this.dexItemFactory = dexItemFactory; } @@ -1022,10 +1033,7 @@ @Override public DexField getOriginalFieldSignature(DexField field) { - DexField originalField = - originalFieldSignatures != null - ? originalFieldSignatures.getOrDefault(field, field) - : field; + DexField originalField = fieldMap.getRepresentativeKeyOrDefault(field, field); return getPrevious().getOriginalFieldSignature(originalField); } @@ -1038,9 +1046,7 @@ @Override public DexField getRenamedFieldSignature(DexField originalField) { DexField renamedField = getPrevious().getRenamedFieldSignature(originalField); - return originalFieldSignatures != null - ? originalFieldSignatures.inverse().getOrDefault(renamedField, renamedField) - : renamedField; + return fieldMap.getOrDefault(renamedField, renamedField); } @Override @@ -1221,10 +1227,15 @@ builder.append(entry.getKey().toSourceString()).append(" -> "); builder.append(entry.getValue().toSourceString()).append(System.lineSeparator()); } - for (Map.Entry<DexField, DexField> entry : fieldMap.entrySet()) { - builder.append(entry.getKey().toSourceString()).append(" -> "); - builder.append(entry.getValue().toSourceString()).append(System.lineSeparator()); - } + fieldMap.forEachManyToOneMapping( + (keys, value) -> { + builder.append( + keys.stream() + .map(DexField::toSourceString) + .collect(Collectors.joining("," + System.lineSeparator()))); + builder.append(" -> "); + builder.append(value.toSourceString()).append(System.lineSeparator()); + }); builder.append(getPrevious().toString()); return builder.toString(); }
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 0902a45..fabdde7 100644 --- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java +++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -57,12 +57,12 @@ import java.util.function.Consumer; import java.util.zip.CRC32; import org.objectweb.asm.AnnotationVisitor; -import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.RecordComponentVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.TypePath; @@ -300,6 +300,17 @@ } @Override + public RecordComponentVisitor visitRecordComponent( + String name, String descriptor, String signature) { + throw new CompilationError("Records are not supported", origin); + } + + @Override + public void visitPermittedSubclass(String permittedSubclass) { + throw new CompilationError("Sealed classes are not supported", origin); + } + + @Override public void visit( int rawVersion, int access, @@ -311,6 +322,9 @@ if (InternalOptions.SUPPORTED_CF_VERSION.isLessThan(version)) { throw new CompilationError("Unsupported class file version: " + version, origin); } + if (version.isGreaterThanOrEqualTo(InternalOptions.EXPERIMENTAL_CF_VERSION)) { + application.options.warningExperimentalClassFileVersion(origin); + } this.deprecated = AsmUtils.isDeprecated(access); accessFlags = ClassAccessFlags.fromCfAccessFlags(cleanAccessFlags(access)); type = application.getTypeFromName(name); @@ -400,11 +414,6 @@ } @Override - public void visitAttribute(Attribute attr) { - // Unknown attribute must only be ignored - } - - @Override public void visitEnd() { if (defaultAnnotations != null) { addAnnotation(DexAnnotation.createAnnotationDefaultAnnotation(
diff --git a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java index 6731b5e..3387ff2 100644 --- a/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java +++ b/src/main/java/com/android/tools/r8/graph/ObjectAllocationInfoCollectionImpl.java
@@ -46,7 +46,7 @@ */ final Set<DexProgramClass> interfacesWithUnknownSubtypeHierarchy = Sets.newIdentityHashSet(); - /** Map of types directly implemented by lambdas to those lambdas. */ + /** Map of types directly implemented by lambdas to those types. */ final Map<DexType, List<LambdaDescriptor>> instantiatedLambdas = new IdentityHashMap<>(); /** @@ -215,6 +215,40 @@ return instantiatedLambdas.keySet(); } + public void removeAllocationsForPrunedItems(PrunedItems prunedItems) { + Set<DexType> removedClasses = prunedItems.getRemovedClasses(); + if (removedClasses.isEmpty()) { + return; + } + classesWithAllocationSiteTracking + .entrySet() + .removeIf(entry -> removedClasses.contains(entry.getKey().getType())); + classesWithoutAllocationSiteTracking.removeIf( + clazz -> removedClasses.contains(clazz.getType())); + boolean removed = + interfacesWithUnknownSubtypeHierarchy.removeIf( + iface -> removedClasses.contains(iface.getType())); + assert !removed : "Unexpected removal of an interface marking an unknown hierarchy."; + removedClasses.forEach(instantiatedLambdas::remove); + } + + public boolean verifyAllocatedTypesAreLive( + Set<DexType> liveTypes, DexDefinitionSupplier definitions) { + for (DexProgramClass clazz : classesWithAllocationSiteTracking.keySet()) { + assert liveTypes.contains(clazz.getType()); + } + for (DexProgramClass clazz : classesWithoutAllocationSiteTracking) { + assert liveTypes.contains(clazz.getType()); + } + for (DexProgramClass iface : interfacesWithUnknownSubtypeHierarchy) { + assert liveTypes.contains(iface.getType()); + } + for (DexType iface : instantiatedLambdas.keySet()) { + assert definitions.definitionFor(iface).isNotProgramClass() || liveTypes.contains(iface); + } + return true; + } + public static class Builder extends ObjectAllocationInfoCollectionImpl { private static class Data {
diff --git a/src/main/java/com/android/tools/r8/graph/PrunedItems.java b/src/main/java/com/android/tools/r8/graph/PrunedItems.java new file mode 100644 index 0000000..2df596c --- /dev/null +++ b/src/main/java/com/android/tools/r8/graph/PrunedItems.java
@@ -0,0 +1,92 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.graph; + +import com.google.common.collect.Sets; +import java.util.Collection; +import java.util.Set; + +public class PrunedItems { + + private final DexApplication prunedApp; + private final Set<DexReference> additionalPinnedItems; + private final Set<DexType> noLongerSyntheticItems; + private final Set<DexType> removedClasses; + + private PrunedItems( + DexApplication prunedApp, + Set<DexReference> additionalPinnedItems, + Set<DexType> noLongerSyntheticItems, + Set<DexType> removedClasses) { + this.prunedApp = prunedApp; + this.additionalPinnedItems = additionalPinnedItems; + this.noLongerSyntheticItems = noLongerSyntheticItems; + this.removedClasses = removedClasses; + } + + public static Builder builder() { + return new Builder(); + } + + public static PrunedItems empty(DexApplication application) { + return new Builder().setPrunedApp(application).build(); + } + + public DexApplication getPrunedApp() { + return prunedApp; + } + + public Set<? extends DexReference> getAdditionalPinnedItems() { + return additionalPinnedItems; + } + + public Set<DexType> getNoLongerSyntheticItems() { + return noLongerSyntheticItems; + } + + public boolean hasRemovedClasses() { + return !removedClasses.isEmpty(); + } + + public Set<DexType> getRemovedClasses() { + return removedClasses; + } + + public static class Builder { + + private DexApplication prunedApp; + + private final Set<DexReference> additionalPinnedItems = Sets.newIdentityHashSet(); + private final Set<DexType> noLongerSyntheticItems = Sets.newIdentityHashSet(); + private final Set<DexType> removedClasses = Sets.newIdentityHashSet(); + + public Builder setPrunedApp(DexApplication prunedApp) { + this.prunedApp = prunedApp; + return this; + } + + public Builder addAdditionalPinnedItems( + Collection<? extends DexReference> additionalPinnedItems) { + this.additionalPinnedItems.addAll(additionalPinnedItems); + return this; + } + + public Builder addNoLongerSyntheticItems(Set<DexType> noLongerSyntheticItems) { + this.noLongerSyntheticItems.addAll(noLongerSyntheticItems); + return this; + } + + public Builder addRemovedClasses(Set<DexType> removedClasses) { + this.noLongerSyntheticItems.addAll(removedClasses); + this.removedClasses.addAll(removedClasses); + return this; + } + + public PrunedItems build() { + return new PrunedItems( + prunedApp, additionalPinnedItems, noLongerSyntheticItems, removedClasses); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java index c8fc0a8..add54d6 100644 --- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java +++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -329,14 +329,16 @@ } } - private static final RewrittenPrototypeDescription none = new RewrittenPrototypeDescription(); + private static final RewrittenPrototypeDescription NONE = new RewrittenPrototypeDescription(); private final List<ExtraParameter> extraParameters; private final ArgumentInfoCollection argumentInfoCollection; private final RewrittenTypeInfo rewrittenReturnInfo; private RewrittenPrototypeDescription() { - this(Collections.emptyList(), null, ArgumentInfoCollection.empty()); + this.extraParameters = Collections.emptyList(); + this.rewrittenReturnInfo = null; + this.argumentInfoCollection = ArgumentInfoCollection.empty(); } private RewrittenPrototypeDescription( @@ -347,6 +349,16 @@ this.extraParameters = extraParameters; this.rewrittenReturnInfo = rewrittenReturnInfo; this.argumentInfoCollection = argumentsInfo; + assert !isEmpty(); + } + + private static RewrittenPrototypeDescription create( + List<ExtraParameter> extraParameters, + RewrittenTypeInfo rewrittenReturnInfo, + ArgumentInfoCollection argumentsInfo) { + return extraParameters.isEmpty() && rewrittenReturnInfo == null && argumentsInfo.isEmpty() + ? none() + : new RewrittenPrototypeDescription(extraParameters, rewrittenReturnInfo, argumentsInfo); } public static RewrittenPrototypeDescription createForUninstantiatedTypes( @@ -356,18 +368,16 @@ DexType returnType = method.proto.returnType; RewrittenTypeInfo returnInfo = returnType.isAlwaysNull(appView) ? RewrittenTypeInfo.toVoid(returnType, appView) : null; - return new RewrittenPrototypeDescription( - Collections.emptyList(), returnInfo, removedArgumentsInfo); + return create(Collections.emptyList(), returnInfo, removedArgumentsInfo); } public static RewrittenPrototypeDescription createForRewrittenTypes( RewrittenTypeInfo returnInfo, ArgumentInfoCollection rewrittenArgumentsInfo) { - return new RewrittenPrototypeDescription( - Collections.emptyList(), returnInfo, rewrittenArgumentsInfo); + return create(Collections.emptyList(), returnInfo, rewrittenArgumentsInfo); } public static RewrittenPrototypeDescription none() { - return none; + return NONE; } public boolean isEmpty() {
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java index 0b5bc41..3a9f618 100644 --- a/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java +++ b/src/main/java/com/android/tools/r8/graph/classmerging/HorizontallyMergedLambdaClasses.java
@@ -8,7 +8,9 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.ir.optimize.lambda.LambdaGroup; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap; import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap; import java.util.Collections; import java.util.Map; import java.util.Set; @@ -19,8 +21,10 @@ private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses; public HorizontallyMergedLambdaClasses(Map<DexType, LambdaGroup> lambdas) { - this.mergedClasses = new BidirectionalManyToOneMap<>(); + MutableBidirectionalManyToOneMap<DexType, DexType> mergedClasses = + new BidirectionalManyToOneHashMap<>(); lambdas.forEach((lambda, group) -> mergedClasses.put(lambda, group.getGroupClassType())); + this.mergedClasses = mergedClasses; } public static HorizontallyMergedLambdaClasses empty() { @@ -29,7 +33,7 @@ @Override public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) { - mergedClasses.forEach(consumer); + mergedClasses.forEachManyToOneMapping(consumer); } @Override @@ -38,6 +42,11 @@ } @Override + public boolean isMergeTarget(DexType type) { + return mergedClasses.containsValue(type); + } + + @Override public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) { for (DexType source : mergedClasses.keySet()) { assert appView.appInfo().wasPruned(source)
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/MergedClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/MergedClasses.java index 25879f1..ae77e17 100644 --- a/src/main/java/com/android/tools/r8/graph/classmerging/MergedClasses.java +++ b/src/main/java/com/android/tools/r8/graph/classmerging/MergedClasses.java
@@ -17,6 +17,8 @@ boolean hasBeenMergedIntoDifferentType(DexType type); + boolean isMergeTarget(DexType type); + boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView); /**
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/MergedClassesCollection.java b/src/main/java/com/android/tools/r8/graph/classmerging/MergedClassesCollection.java index 5875506..33bee51 100644 --- a/src/main/java/com/android/tools/r8/graph/classmerging/MergedClassesCollection.java +++ b/src/main/java/com/android/tools/r8/graph/classmerging/MergedClassesCollection.java
@@ -38,6 +38,16 @@ } @Override + public boolean isMergeTarget(DexType type) { + for (MergedClasses mergedClasses : collection) { + if (mergedClasses.isMergeTarget(type)) { + return true; + } + } + return false; + } + + @Override public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) { for (MergedClasses mergedClasses : collection) { assert mergedClasses.verifyAllSourcesPruned(appView);
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/StaticallyMergedClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/StaticallyMergedClasses.java index 79e4ef7..9aabf18 100644 --- a/src/main/java/com/android/tools/r8/graph/classmerging/StaticallyMergedClasses.java +++ b/src/main/java/com/android/tools/r8/graph/classmerging/StaticallyMergedClasses.java
@@ -8,7 +8,10 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap; import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap; +import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap; import java.util.Set; import java.util.function.BiConsumer; @@ -21,7 +24,7 @@ } public static StaticallyMergedClasses empty() { - return new StaticallyMergedClasses(BidirectionalManyToOneMap.empty()); + return new StaticallyMergedClasses(new EmptyBidirectionalOneToOneMap<>()); } public static Builder builder() { @@ -30,7 +33,7 @@ @Override public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) { - mergedClasses.forEach(consumer); + mergedClasses.forEachManyToOneMapping(consumer); } @Override @@ -39,14 +42,21 @@ } @Override + public boolean isMergeTarget(DexType type) { + // Intentionally returns false since static class merging technically doesn't merge any classes, + // it only moves static members. + return false; + } + + @Override public boolean verifyAllSourcesPruned(AppView<AppInfoWithLiveness> appView) { return true; } public static class Builder { - private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses = - new BidirectionalManyToOneMap<>(); + private final MutableBidirectionalManyToOneMap<DexType, DexType> mergedClasses = + new BidirectionalManyToOneHashMap<>(); private Builder() {}
diff --git a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java index 6ba32e9..3b59401 100644 --- a/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java +++ b/src/main/java/com/android/tools/r8/graph/classmerging/VerticallyMergedClasses.java
@@ -8,6 +8,7 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap; +import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap; import java.util.Collection; import java.util.Map; import java.util.Set; @@ -22,12 +23,12 @@ } public static VerticallyMergedClasses empty() { - return new VerticallyMergedClasses(BidirectionalManyToOneMap.empty()); + return new VerticallyMergedClasses(new EmptyBidirectionalOneToOneMap<>()); } @Override public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) { - mergedClasses.forEach(consumer); + mergedClasses.forEachManyToOneMapping(consumer); } public Map<DexType, DexType> getForwardMap() { @@ -55,7 +56,8 @@ return mergedClasses.isEmpty(); } - public boolean isTarget(DexType type) { + @Override + public boolean isMergeTarget(DexType type) { return !getSourcesFor(type).isEmpty(); }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInitializerSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInitializerSynthesizedCode.java new file mode 100644 index 0000000..c919e63 --- /dev/null +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInitializerSynthesizedCode.java
@@ -0,0 +1,100 @@ +// 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.horizontalclassmerging; + +import static com.android.tools.r8.utils.ConsumerUtils.apply; +import static java.lang.Integer.max; + +import com.android.tools.r8.cf.CfVersion; +import com.android.tools.r8.cf.code.CfGoto; +import com.android.tools.r8.cf.code.CfInstruction; +import com.android.tools.r8.cf.code.CfLabel; +import com.android.tools.r8.cf.code.CfReturnVoid; +import com.android.tools.r8.graph.CfCode; +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.utils.CfVersionUtils; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class ClassInitializerSynthesizedCode { + private final List<DexEncodedMethod> staticClassInitializers; + private int maxStack = 0; + private int maxLocals = 0; + + private ClassInitializerSynthesizedCode(List<DexEncodedMethod> staticClassInitializers) { + this.staticClassInitializers = staticClassInitializers; + } + + public boolean isEmpty() { + return staticClassInitializers.isEmpty(); + } + + private void addCfCode(List<CfInstruction> newInstructions, DexEncodedMethod method) { + CfCode code = method.getCode().asCfCode(); + maxStack = max(maxStack, code.getMaxStack()); + maxLocals = max(maxLocals, code.getMaxLocals()); + + CfLabel endLabel = new CfLabel(); + boolean requiresLabel = false; + int index = 1; + for (CfInstruction instruction : code.getInstructions()) { + if (instruction.isReturn()) { + if (code.getInstructions().size() != index) { + newInstructions.add(new CfGoto(endLabel)); + requiresLabel = true; + } + } else { + newInstructions.add(instruction); + } + + index++; + } + if (requiresLabel) { + newInstructions.add(endLabel); + } + } + + public CfCode synthesizeCode(DexType originalHolder) { + return new CfCode( + originalHolder, + maxStack, + maxLocals, + buildInstructions(), + Collections.emptyList(), + Collections.emptyList()); + } + + private List<CfInstruction> buildInstructions() { + List<CfInstruction> newInstructions = new ArrayList<>(); + staticClassInitializers.forEach(apply(this::addCfCode, newInstructions)); + newInstructions.add(new CfReturnVoid()); + return newInstructions; + } + + public DexEncodedMethod getFirst() { + return staticClassInitializers.iterator().next(); + } + + public CfVersion getCfVersion() { + return CfVersionUtils.max(staticClassInitializers); + } + + public static class Builder { + private final List<DexEncodedMethod> staticClassInitializers = new ArrayList<>(); + + public void add(DexEncodedMethod method) { + assert method.isClassInitializer(); + assert method.hasCode(); + assert method.getCode().isCfCode(); + staticClassInitializers.add(method); + } + + public ClassInitializerSynthesizedCode build() { + return new ClassInitializerSynthesizedCode(staticClassInitializers); + } + } +}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java index 26ed21c..96282ee 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
@@ -5,11 +5,12 @@ package com.android.tools.r8.horizontalclassmerging; import com.android.tools.r8.graph.DexEncodedField; -import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerGraphLens.Builder; +import com.android.tools.r8.utils.IterableUtils; import com.android.tools.r8.utils.ListUtils; +import com.google.common.collect.Iterables; import java.util.ArrayList; import java.util.Collection; import java.util.IdentityHashMap; @@ -50,20 +51,19 @@ } } - private void mergeField(DexEncodedField oldField, DexEncodedField newField) { - if (newField.isFinal() && !oldField.isFinal()) { + private void fixAccessFlags(DexEncodedField newField, Collection<DexEncodedField> oldFields) { + if (newField.isFinal() && Iterables.any(oldFields, oldField -> !oldField.isFinal())) { newField.getAccessFlags().demoteFromFinal(); } - lensBuilder.moveField(oldField.field, newField.field); } - private void mergeFields(DexEncodedField newField, Collection<DexEncodedField> oldFields) { - DexField newFieldReference = newField.getReference(); - - lensBuilder.moveField(newFieldReference, newFieldReference); - lensBuilder.setRepresentativeField(newFieldReference, newFieldReference); - - oldFields.forEach(oldField -> mergeField(oldField, newField)); + private void mergeFields(DexEncodedField newField, List<DexEncodedField> oldFields) { + fixAccessFlags(newField, oldFields); + lensBuilder.recordNewFieldSignature( + Iterables.transform( + IterableUtils.append(oldFields, newField), DexEncodedField::getReference), + newField.getReference(), + newField.getReference()); } public void merge() {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java index 04b4b68..950cf9a 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassMerger.java
@@ -6,8 +6,10 @@ import static com.google.common.base.Predicates.not; +import com.android.tools.r8.cf.CfVersion; import com.android.tools.r8.dex.Constants; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.DexAnnotationSet; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; @@ -19,6 +21,9 @@ import com.android.tools.r8.graph.DexTypeList; import com.android.tools.r8.graph.FieldAccessFlags; import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature; +import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature; +import com.android.tools.r8.graph.MethodAccessFlags; +import com.android.tools.r8.graph.ParameterAnnotationsList; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier; @@ -46,8 +51,10 @@ public static final String CLASS_ID_FIELD_NAME = "$r8$classId"; + private final AppView<AppInfoWithLiveness> appView; private final MergeGroup group; private final DexItemFactory dexItemFactory; + private final ClassInitializerSynthesizedCode classInitializerSynthesizedCode; private final HorizontalClassMergerGraphLens.Builder lensBuilder; private final HorizontallyMergedClasses.Builder mergedClassesBuilder; private final FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder; @@ -66,7 +73,9 @@ FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder, MergeGroup group, Collection<VirtualMethodMerger> virtualMethodMergers, - Collection<ConstructorMerger> constructorMergers) { + Collection<ConstructorMerger> constructorMergers, + ClassInitializerSynthesizedCode classInitializerSynthesizedCode) { + this.appView = appView; this.lensBuilder = lensBuilder; this.mergedClassesBuilder = mergedClassesBuilder; this.fieldAccessChangesBuilder = fieldAccessChangesBuilder; @@ -75,6 +84,7 @@ this.constructorMergers = constructorMergers; this.dexItemFactory = appView.dexItemFactory(); + this.classInitializerSynthesizedCode = classInitializerSynthesizedCode; this.classStaticFieldsMerger = new ClassStaticFieldsMerger(appView, lensBuilder, group); this.classInstanceFieldsMerger = new ClassInstanceFieldsMerger(lensBuilder, group); @@ -91,18 +101,54 @@ } void mergeDirectMethods(SyntheticArgumentClass syntheticArgumentClass) { + mergeStaticClassInitializers(); mergeDirectMethods(group.getTarget()); group.forEachSource(this::mergeDirectMethods); mergeConstructors(syntheticArgumentClass); } + void mergeStaticClassInitializers() { + if (classInitializerSynthesizedCode.isEmpty()) { + return; + } + + DexMethod newClinit = dexItemFactory.createClassInitializer(group.getTarget().getType()); + + CfCode code = classInitializerSynthesizedCode.synthesizeCode(group.getTarget().getType()); + if (!group.getTarget().hasClassInitializer()) { + classMethodsBuilder.addDirectMethod( + new DexEncodedMethod( + newClinit, + MethodAccessFlags.fromSharedAccessFlags( + Constants.ACC_SYNTHETIC | Constants.ACC_STATIC, true), + MethodTypeSignature.noSignature(), + DexAnnotationSet.empty(), + ParameterAnnotationsList.empty(), + code, + true, + classInitializerSynthesizedCode.getCfVersion())); + } else { + DexEncodedMethod clinit = group.getTarget().getClassInitializer(); + clinit.setCode(code, appView); + CfVersion cfVersion = classInitializerSynthesizedCode.getCfVersion(); + if (cfVersion != null) { + clinit.upgradeClassFileVersion(cfVersion); + } else { + assert appView.options().isGeneratingDex(); + } + classMethodsBuilder.addDirectMethod(clinit); + } + } + void mergeDirectMethods(DexProgramClass toMerge) { toMerge.forEachProgramDirectMethod( method -> { DexEncodedMethod definition = method.getDefinition(); - assert !definition.isClassInitializer(); - - if (!definition.isInstanceInitializer()) { + if (definition.isClassInitializer()) { + lensBuilder.moveMethod( + method.getReference(), + dexItemFactory.createClassInitializer(group.getTarget().getType())); + } else if (!definition.isInstanceInitializer()) { DexMethod newMethod = method.getReference().withHolder(group.getTarget().getType(), dexItemFactory); if (!classMethodsBuilder.isFresh(newMethod)) { @@ -114,7 +160,6 @@ } } }); - // Clear the members of the class to be merged since they have now been moved to the target. toMerge.getMethodCollection().clearDirectMethods(); } @@ -217,6 +262,8 @@ public static class Builder { private final AppView<AppInfoWithLiveness> appView; private final MergeGroup group; + private final ClassInitializerSynthesizedCode.Builder classInitializerSynthesizedCodeBuilder = + new ClassInitializerSynthesizedCode.Builder(); private final Map<DexProto, ConstructorMerger.Builder> constructorMergerBuilders = new LinkedHashMap<>(); private final List<ConstructorMerger.Builder> unmergedConstructorBuilders = new ArrayList<>(); @@ -242,20 +289,17 @@ } private void setupForMethodMerging(DexProgramClass toMerge) { - toMerge.forEachProgramDirectMethod( - method -> { - DexEncodedMethod definition = method.getDefinition(); - assert !definition.isClassInitializer(); - if (definition.isInstanceInitializer()) { - addConstructor(method); - } - }); + if (toMerge.hasClassInitializer()) { + classInitializerSynthesizedCodeBuilder.add(toMerge.getClassInitializer()); + } + toMerge.forEachProgramDirectMethodMatching( + DexEncodedMethod::isInstanceInitializer, this::addConstructor); toMerge.forEachProgramVirtualMethod(this::addVirtualMethod); } private void addConstructor(ProgramMethod method) { assert method.getDefinition().isInstanceInitializer(); - if (appView.options().enableHorizontalClassMergingConstructorMerging) { + if (appView.options().horizontalClassMergerOptions().isConstructorMergingEnabled()) { constructorMergerBuilders .computeIfAbsent( method.getDefinition().getProto(), ignore -> new ConstructorMerger.Builder(appView)) @@ -276,7 +320,7 @@ } private Collection<ConstructorMerger.Builder> getConstructorMergerBuilders() { - return appView.options().enableHorizontalClassMergingConstructorMerging + return appView.options().horizontalClassMergerOptions().isConstructorMergingEnabled() ? constructorMergerBuilders.values() : unmergedConstructorBuilders; } @@ -313,7 +357,8 @@ fieldAccessChangesBuilder, group, virtualMethodMergers, - constructorMergers); + constructorMergers, + classInitializerSynthesizedCodeBuilder.build()); } } }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java index 7bc85d1..80180f6 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassStaticFieldsMerger.java
@@ -33,7 +33,7 @@ this.dexItemFactory = appView.dexItemFactory(); } - private final boolean isFresh(DexField fieldReference) { + private boolean isFresh(DexField fieldReference) { return !targetFields.containsKey(fieldReference); } @@ -48,7 +48,7 @@ field = field.toTypeSubstitutedField(newFieldReference); targetFields.put(newFieldReference, field); - lensBuilder.moveField(oldFieldReference, newFieldReference); + lensBuilder.recordNewFieldSignature(oldFieldReference, newFieldReference); } public void addFields(DexProgramClass toMerge) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java index d2cd444..01d5771 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ConstructorMerger.java
@@ -4,6 +4,8 @@ package com.android.tools.r8.horizontalclassmerging; +import static com.android.tools.r8.dex.Constants.TEMPORARY_INSTANCE_INITIALIZER_PREFIX; + import com.android.tools.r8.cf.CfVersion; import com.android.tools.r8.dex.Constants; import com.android.tools.r8.graph.AppView; @@ -28,7 +30,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.LinkedList; import java.util.List; public class ConstructorMerger { @@ -111,7 +112,7 @@ ClassMethodsBuilder classMethodsBuilder, DexEncodedMethod constructor) { DexMethod method = dexItemFactory.createFreshMethodName( - "constructor", + TEMPORARY_INSTANCE_INITIALIZER_PREFIX, constructor.getHolderType(), constructor.proto(), group.getTarget().getType(), @@ -152,7 +153,7 @@ } DexMethod movedConstructor = moveConstructor(classMethodsBuilder, constructor); lensBuilder.mapMethod(movedConstructor, movedConstructor); - lensBuilder.mapMethodInverse(constructor.method, movedConstructor); + lensBuilder.recordNewMethodSignature(constructor.getReference(), movedConstructor); typeConstructorClassMap.put( classIdentifiers.getInt(constructor.getHolderType()), movedConstructor); } @@ -165,13 +166,29 @@ classMethodsBuilder::isFresh); int extraNulls = newConstructorReference.getArity() - methodReferenceTemplate.getArity(); - DexMethod representativeConstructorReference = constructors.iterator().next().method; + DexEncodedMethod representative = constructors.iterator().next(); + DexMethod originalConstructorReference = + appView.graphLens().getOriginalMethodSignature(representative.getReference()); + + // Create a special original method signature for the synthesized constructor that did not exist + // prior to horizontal class merging. Otherwise we might accidentally think that the synthesized + // constructor corresponds to the previous <init>() method on the target class, which could have + // unintended side-effects such as leading to unused argument removal being applied to the + // synthesized constructor all-though it by construction doesn't have any unused arguments. + DexMethod bridgeConstructorReference = + dexItemFactory.createFreshMethodName( + "$r8$init$bridge", + null, + originalConstructorReference.getProto(), + originalConstructorReference.getHolderType(), + classMethodsBuilder::isFresh); + ConstructorEntryPointSynthesizedCode synthesizedCode = new ConstructorEntryPointSynthesizedCode( typeConstructorClassMap, newConstructorReference, group.getClassIdField(), - appView.graphLens().getOriginalMethodSignature(representativeConstructorReference)); + bridgeConstructorReference); DexEncodedMethod newConstructor = new DexEncodedMethod( newConstructorReference, @@ -183,34 +200,20 @@ true, classFileVersion); - if (isTrivialMerge()) { - // The constructor does not require the additional argument, just map it like a regular - // method. - DexEncodedMethod oldConstructor = constructors.iterator().next(); - if (extraNulls > 0) { - List<ExtraParameter> extraParameters = new LinkedList<>(); - extraParameters.addAll(Collections.nCopies(extraNulls, new ExtraUnusedNullParameter())); - lensBuilder.moveMergedConstructor( - oldConstructor.method, newConstructorReference, extraParameters); - } else { - lensBuilder.moveMethod(oldConstructor.method, newConstructorReference); - } - } else { - // Map each old constructor to the newly synthesized constructor in the graph lens. - for (DexEncodedMethod oldConstructor : constructors) { + // Map each old constructor to the newly synthesized constructor in the graph lens. + for (DexEncodedMethod oldConstructor : constructors) { + List<ExtraParameter> extraParameters = new ArrayList<>(); + if (constructors.size() > 1) { int classIdentifier = classIdentifiers.getInt(oldConstructor.getHolderType()); - - List<ExtraParameter> extraParameters = new LinkedList<>(); extraParameters.add(new ExtraConstantIntParameter(classIdentifier)); - extraParameters.addAll(Collections.nCopies(extraNulls, new ExtraUnusedNullParameter())); - - lensBuilder.moveMergedConstructor( - oldConstructor.method, newConstructorReference, extraParameters); } + extraParameters.addAll(Collections.nCopies(extraNulls, new ExtraUnusedNullParameter())); + lensBuilder.mapMergedConstructor( + oldConstructor.getReference(), newConstructorReference, extraParameters); } - // Map the first constructor to the newly synthesized constructor. - lensBuilder.recordExtraOriginalSignature( - representativeConstructorReference, newConstructorReference); + + // Add a mapping from a synthetic name to the synthetic constructor. + lensBuilder.recordNewMethodSignature(bridgeConstructorReference, newConstructorReference); classMethodsBuilder.addDirectMethod(newConstructor);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java index 0821a4c..5983618 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -15,6 +15,7 @@ import com.android.tools.r8.horizontalclassmerging.policies.IgnoreSynthetics; import com.android.tools.r8.horizontalclassmerging.policies.LimitGroups; import com.android.tools.r8.horizontalclassmerging.policies.NoAnnotations; +import com.android.tools.r8.horizontalclassmerging.policies.NoClassInitializerWithObservableSideEffects; import com.android.tools.r8.horizontalclassmerging.policies.NoClassesOrMembersWithAnnotations; import com.android.tools.r8.horizontalclassmerging.policies.NoDirectRuntimeTypeChecks; import com.android.tools.r8.horizontalclassmerging.policies.NoEnums; @@ -26,7 +27,6 @@ import com.android.tools.r8.horizontalclassmerging.policies.NoKotlinMetadata; import com.android.tools.r8.horizontalclassmerging.policies.NoNativeMethods; import com.android.tools.r8.horizontalclassmerging.policies.NoServiceLoaders; -import com.android.tools.r8.horizontalclassmerging.policies.NoStaticClassInitializer; import com.android.tools.r8.horizontalclassmerging.policies.NotMatchedByNoHorizontalClassMerging; import com.android.tools.r8.horizontalclassmerging.policies.NotVerticallyMergedIntoSubtype; import com.android.tools.r8.horizontalclassmerging.policies.PreserveMethodCharacteristics; @@ -64,11 +64,9 @@ MergeGroup initialGroup = new MergeGroup(appView.appInfo().classesWithDeterministicOrder()); // Run the policies on all program classes to produce a final grouping. + List<Policy> policies = getPolicies(mainDexTracingResult, runtimeTypeCheckInfo); Collection<MergeGroup> groups = - new SimplePolicyExecutor() - .run( - Collections.singletonList(initialGroup), - getPolicies(mainDexTracingResult, runtimeTypeCheckInfo)); + new SimplePolicyExecutor().run(Collections.singletonList(initialGroup), policies); // If there are no groups, then end horizontal class merging. if (groups.isEmpty()) { @@ -116,7 +114,7 @@ new IgnoreSynthetics(appView), new NoClassesOrMembersWithAnnotations(), new NoInnerClasses(), - new NoStaticClassInitializer(), + new NoClassInitializerWithObservableSideEffects(), new NoNativeMethods(), new NoKeepRules(appView), new NoKotlinMetadata(),
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java index 16f853e..3adad2e 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerGraphLens.java
@@ -8,89 +8,51 @@ import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.GraphLens.NestedGraphLens; -import com.android.tools.r8.graph.RewrittenPrototypeDescription; import com.android.tools.r8.ir.conversion.ExtraParameter; import com.android.tools.r8.utils.IterableUtils; -import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; -import com.google.common.collect.BiMap; +import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap; +import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeHashMap; +import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap; +import com.android.tools.r8.utils.collections.BidirectionalOneToManyRepresentativeHashMap; +import com.android.tools.r8.utils.collections.BidirectionalOneToManyRepresentativeMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneRepresentativeMap; +import com.google.common.collect.Iterables; +import com.google.common.collect.Streams; import java.util.ArrayList; -import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Function; public class HorizontalClassMergerGraphLens extends NestedGraphLens { private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters; - private final Map<DexMethod, DexMethod> extraOriginalMethodSignatures; private final HorizontallyMergedClasses mergedClasses; - private final Map<DexField, DexField> extraOriginalFieldSignatures; private HorizontalClassMergerGraphLens( AppView<?> appView, HorizontallyMergedClasses mergedClasses, Map<DexMethod, List<ExtraParameter>> methodExtraParameters, - Map<DexField, DexField> fieldMap, + BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap, Map<DexMethod, DexMethod> methodMap, - BiMap<DexField, DexField> originalFieldSignatures, - BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures, - Map<DexMethod, DexMethod> extraOriginalMethodSignatures, - Map<DexField, DexField> extraOriginalFieldSignatures, - GraphLens previousLens) { + BidirectionalOneToManyRepresentativeMap<DexMethod, DexMethod> originalMethodSignatures) { super( mergedClasses.getForwardMap(), methodMap, fieldMap, - originalFieldSignatures, originalMethodSignatures, - previousLens, + appView.graphLens(), appView.dexItemFactory()); this.methodExtraParameters = methodExtraParameters; - this.extraOriginalFieldSignatures = extraOriginalFieldSignatures; - this.extraOriginalMethodSignatures = extraOriginalMethodSignatures; this.mergedClasses = mergedClasses; } - private boolean isSynthesizedByHorizontalClassMerging(DexMethod method) { - return methodExtraParameters.containsKey(method); - } - @Override protected Iterable<DexType> internalGetOriginalTypes(DexType previous) { return IterableUtils.prependSingleton(previous, mergedClasses.getSourcesFor(previous)); } - @Override - public RewrittenPrototypeDescription lookupPrototypeChangesForMethodDefinition(DexMethod method) { - if (isSynthesizedByHorizontalClassMerging(method)) { - // If we are processing the call site, the arguments should be removed. - return RewrittenPrototypeDescription.none(); - } - return super.lookupPrototypeChangesForMethodDefinition(method); - } - - @Override - public DexMethod getOriginalMethodSignature(DexMethod method) { - DexMethod originalConstructor = extraOriginalMethodSignatures.get(method); - if (originalConstructor == null) { - return super.getOriginalMethodSignature(method); - } - return getPrevious().getOriginalMethodSignature(originalConstructor); - } - - @Override - public DexField getOriginalFieldSignature(DexField field) { - DexField originalField = extraOriginalFieldSignatures.get(field); - if (originalField == null) { - return super.getOriginalFieldSignature(field); - } - return getPrevious().getOriginalFieldSignature(originalField); - } - /** * If an overloaded constructor is requested, add the constructor id as a parameter to the * constructor. Otherwise return the lookup on the underlying graph lens. @@ -111,87 +73,121 @@ } public static class Builder { - private ManyToOneMap<DexField, DexField> fieldMap = new ManyToOneMap<>(); - private ManyToOneMap<DexMethod, DexMethod> methodMap = new ManyToOneMap<>(); + + private final MutableBidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap = + new BidirectionalManyToOneRepresentativeHashMap<>(); + private final BidirectionalManyToOneHashMap<DexMethod, DexMethod> methodMap = + new BidirectionalManyToOneHashMap<>(); + private final BidirectionalOneToManyRepresentativeHashMap<DexMethod, DexMethod> + originalMethodSignatures = new BidirectionalOneToManyRepresentativeHashMap<>(); private final Map<DexMethod, List<ExtraParameter>> methodExtraParameters = new IdentityHashMap<>(); + private final BidirectionalManyToOneHashMap<DexMethod, DexMethod> pendingMethodMapUpdates = + new BidirectionalManyToOneHashMap<>(); + private final BidirectionalOneToManyRepresentativeHashMap<DexMethod, DexMethod> + pendingOriginalMethodSignatureUpdates = new BidirectionalOneToManyRepresentativeHashMap<>(); + Builder() {} - public HorizontalClassMergerGraphLens build( + HorizontalClassMergerGraphLens build( AppView<?> appView, HorizontallyMergedClasses mergedClasses) { - ManyToOneInverseMap<DexMethod, DexMethod> inverseMethodMap = - methodMap.inverse( - group -> { - // Every group should have a representative. Fail in debug mode. - assert false; - return group.iterator().next(); - }); - ManyToOneInverseMap<DexField, DexField> inverseFieldMap = - fieldMap.inverse( - group -> { - // Every group should have a representative. Fail in debug mode. - assert false; - return group.iterator().next(); - }); - + assert pendingMethodMapUpdates.isEmpty(); + assert pendingOriginalMethodSignatureUpdates.isEmpty(); return new HorizontalClassMergerGraphLens( appView, mergedClasses, methodExtraParameters, - fieldMap.getForwardMap(), + fieldMap, methodMap.getForwardMap(), - inverseFieldMap.getBiMap().getForwardBacking(), - inverseMethodMap.getBiMap(), - inverseMethodMap.getExtraMap(), - inverseFieldMap.getExtraMap(), - appView.graphLens()); + originalMethodSignatures); } - public void remapMethods(BiMap<DexMethod, DexMethod> remapMethods) { - methodMap = methodMap.remap(remapMethods, Function.identity(), Function.identity()); + void recordNewFieldSignature(DexField oldFieldSignature, DexField newFieldSignature) { + fieldMap.put(oldFieldSignature, newFieldSignature); } - public void remapFields(BiMap<DexField, DexField> remapFields) { - fieldMap = fieldMap.remap(remapFields, Function.identity(), Function.identity()); + void recordNewFieldSignature( + Iterable<DexField> oldFieldSignatures, + DexField newFieldSignature, + DexField representative) { + assert Streams.stream(oldFieldSignatures) + .anyMatch(oldFieldSignature -> oldFieldSignature != newFieldSignature); + assert Streams.stream(oldFieldSignatures).noneMatch(fieldMap::containsValue); + assert Iterables.contains(oldFieldSignatures, representative); + for (DexField oldFieldSignature : oldFieldSignatures) { + recordNewFieldSignature(oldFieldSignature, newFieldSignature); + } + fieldMap.setRepresentative(newFieldSignature, representative); } - public Builder moveField(DexField from, DexField to) { - fieldMap.put(from, to); - fieldMap.putInverse(from, to); - return this; + void fixupField(DexField oldFieldSignature, DexField newFieldSignature) { + Set<DexField> originalFieldSignatures = fieldMap.removeValue(oldFieldSignature); + if (originalFieldSignatures.isEmpty()) { + fieldMap.put(oldFieldSignature, newFieldSignature); + } else if (originalFieldSignatures.size() == 1) { + fieldMap.put(originalFieldSignatures.iterator().next(), newFieldSignature); + } else { + for (DexField originalFieldSignature : originalFieldSignatures) { + fieldMap.put(originalFieldSignature, newFieldSignature); + } + DexField representative = fieldMap.removeRepresentativeFor(oldFieldSignature); + assert representative != null; + fieldMap.setRepresentative(newFieldSignature, representative); + } } - public Builder setRepresentativeField(DexField from, DexField to) { - fieldMap.setRepresentative(from, to); - return this; + void mapMethod(DexMethod oldMethodSignature, DexMethod newMethodSignature) { + methodMap.put(oldMethodSignature, newMethodSignature); } - /** Unidirectional mapping from one method to another. */ - public Builder recordExtraOriginalSignature(DexMethod from, DexMethod to) { - methodMap.setRepresentative(from, to); - - return this; - } - - /** Unidirectional mapping from one method to another. */ - public Builder mapMethod(DexMethod from, DexMethod to) { - methodMap.put(from, to); - - return this; - } - - /** Unidirectional mapping from one method to another. */ - public Builder mapMethodInverse(DexMethod from, DexMethod to) { - methodMap.putInverse(from, to); - - return this; - } - - public Builder moveMethod(DexMethod from, DexMethod to) { + void moveMethod(DexMethod from, DexMethod to) { mapMethod(from, to); - mapMethodInverse(from, to); - return this; + recordNewMethodSignature(from, to); + } + + void recordNewMethodSignature(DexMethod oldMethodSignature, DexMethod newMethodSignature) { + originalMethodSignatures.put(newMethodSignature, oldMethodSignature); + } + + void fixupMethod(DexMethod oldMethodSignature, DexMethod newMethodSignature) { + fixupMethodMap(oldMethodSignature, newMethodSignature); + fixupOriginalMethodSignatures(oldMethodSignature, newMethodSignature); + } + + private void fixupMethodMap(DexMethod oldMethodSignature, DexMethod newMethodSignature) { + Set<DexMethod> originalMethodSignatures = methodMap.getKeys(oldMethodSignature); + if (originalMethodSignatures.isEmpty()) { + pendingMethodMapUpdates.put(oldMethodSignature, newMethodSignature); + } else { + for (DexMethod originalMethodSignature : originalMethodSignatures) { + pendingMethodMapUpdates.put(originalMethodSignature, newMethodSignature); + } + } + } + + private void fixupOriginalMethodSignatures( + DexMethod oldMethodSignature, DexMethod newMethodSignature) { + Set<DexMethod> oldMethodSignatures = originalMethodSignatures.getValues(oldMethodSignature); + if (oldMethodSignatures.isEmpty()) { + pendingOriginalMethodSignatureUpdates.put(newMethodSignature, oldMethodSignature); + } else { + for (DexMethod originalMethodSignature : oldMethodSignatures) { + pendingOriginalMethodSignatureUpdates.put(newMethodSignature, originalMethodSignature); + } + } + } + + void commitPendingUpdates() { + // Commit pending method map updates. + methodMap.removeAll(pendingMethodMapUpdates.keySet()); + pendingMethodMapUpdates.forEachManyToOneMapping(methodMap::put); + pendingMethodMapUpdates.clear(); + + // Commit pending original method signatures updates. + originalMethodSignatures.removeAll(pendingOriginalMethodSignatureUpdates.keySet()); + pendingOriginalMethodSignatureUpdates.forEachOneToManyMapping(originalMethodSignatures::put); + pendingOriginalMethodSignatureUpdates.clear(); } /** @@ -199,24 +195,27 @@ * where many constructors are merged into a single constructor. The synthesized constructor * therefore does not have a unique reverse constructor. */ - public Builder moveMergedConstructor( - DexMethod from, DexMethod to, List<ExtraParameter> extraParameters) { - moveMethod(from, to); - methodExtraParameters.put(from, extraParameters); - return this; + void mapMergedConstructor(DexMethod from, DexMethod to, List<ExtraParameter> extraParameters) { + mapMethod(from, to); + if (extraParameters.size() > 0) { + methodExtraParameters.put(from, extraParameters); + } } - public Builder addExtraParameters(DexMethod to, List<ExtraParameter> extraParameters) { - Set<DexMethod> mapsFrom = methodMap.lookupReverse(to); - if (mapsFrom == null) { - mapsFrom = Collections.singleton(to); + void addExtraParameters(DexMethod methodSignature, List<ExtraParameter> extraParameters) { + Set<DexMethod> originalMethodSignatures = methodMap.getKeys(methodSignature); + if (originalMethodSignatures.isEmpty()) { + methodExtraParameters + .computeIfAbsent(methodSignature, ignore -> new ArrayList<>(extraParameters.size())) + .addAll(extraParameters); + } else { + for (DexMethod originalMethodSignature : originalMethodSignatures) { + methodExtraParameters + .computeIfAbsent( + originalMethodSignature, ignore -> new ArrayList<>(extraParameters.size())) + .addAll(extraParameters); + } } - mapsFrom.forEach( - originalFrom -> - methodExtraParameters - .computeIfAbsent(originalFrom, ignore -> new ArrayList<>(extraParameters.size())) - .addAll(extraParameters)); - return this; } } }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java index 4d6d93f..93f9d3f 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontallyMergedClasses.java
@@ -8,7 +8,10 @@ import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.classmerging.MergedClasses; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap; import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap; +import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; @@ -22,12 +25,12 @@ } public static HorizontallyMergedClasses empty() { - return new HorizontallyMergedClasses(new BidirectionalManyToOneMap<>()); + return new HorizontallyMergedClasses(new EmptyBidirectionalOneToOneMap<>()); } @Override public void forEachMergeGroup(BiConsumer<Set<DexType>, DexType> consumer) { - mergedClasses.forEach(consumer); + mergedClasses.forEachManyToOneMapping(consumer); } public DexType getMergeTargetOrDefault(DexType type) { @@ -42,13 +45,18 @@ return mergedClasses.getKeys(type); } - @Override - public boolean hasBeenMergedIntoDifferentType(DexType type) { - return mergedClasses.hasKey(type); + public Set<DexType> getTargets() { + return mergedClasses.values(); } + @Override + public boolean hasBeenMergedIntoDifferentType(DexType type) { + return mergedClasses.containsKey(type); + } + + @Override public boolean isMergeTarget(DexType type) { - return mergedClasses.hasValue(type); + return mergedClasses.containsValue(type); } public boolean hasBeenMergedOrIsMergeTarget(DexType type) { @@ -71,8 +79,8 @@ } public static class Builder { - private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses = - new BidirectionalManyToOneMap<>(); + private final MutableBidirectionalManyToOneMap<DexType, DexType> mergedClasses = + new BidirectionalManyToOneHashMap<>(); public HorizontallyMergedClasses build() { return new HorizontallyMergedClasses(mergedClasses);
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java deleted file mode 100644 index 973162f..0000000 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneInverseMap.java +++ /dev/null
@@ -1,27 +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.horizontalclassmerging; - -import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; -import java.util.Map; - -/** The inverse of a {@link ManyToOneMap} used for generating graph lens maps. */ -public class ManyToOneInverseMap<K, V> { - private final BidirectionalOneToOneHashMap<V, K> biMap; - private final Map<V, K> extraMap; - - ManyToOneInverseMap(BidirectionalOneToOneHashMap<V, K> biMap, Map<V, K> extraMap) { - this.biMap = biMap; - this.extraMap = extraMap; - } - - public BidirectionalOneToOneHashMap<V, K> getBiMap() { - return biMap; - } - - public Map<V, K> getExtraMap() { - return extraMap; - } -}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java deleted file mode 100644 index 5204021..0000000 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ManyToOneMap.java +++ /dev/null
@@ -1,121 +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.horizontalclassmerging; - -import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import java.util.HashMap; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.function.Function; - -/** - * This mapping class is used to track method mappings for horizontal class merging. Essentially it - * is a bidirectional many to one map, but with support for having unidirectional mappings and with - * support for remapping the values to new values using {@link ManyToOneMap#remap(BiMap, Function, - * Function)}. It also supports generating an inverse mapping {@link ManyToOneInverseMap} that can - * be used by the graph lens using {@link ManyToOneMap#inverse(Function)}. The inverse map is a - * bidirectional one to one map with additional non-bidirectional representative entries. - */ -public class ManyToOneMap<K, V> { - private final Map<K, V> forwardMap = new IdentityHashMap<>(); - private final Map<V, Set<K>> inverseMap = new IdentityHashMap<>(); - private final Map<V, K> representativeMap = new IdentityHashMap<>(); - - public Map<K, V> getForwardMap() { - return forwardMap; - } - - public Set<K> lookupReverse(V to) { - return inverseMap.get(to); - } - - public V put(K from, V to) { - return forwardMap.put(from, to); - } - - public void putInverse(K from, V to) { - inverseMap.computeIfAbsent(to, ignore -> new HashSet<>()).add(from); - } - - public K setRepresentative(K from, V to) { - putInverse(from, to); - return representativeMap.put(to, from); - } - - public ManyToOneInverseMap<K, V> inverse(Function<Set<K>, K> pickRepresentative) { - BidirectionalOneToOneHashMap<V, K> biMap = new BidirectionalOneToOneHashMap<>(); - Map<V, K> extraMap = new HashMap<>(); - for (Entry<V, Set<K>> entry : inverseMap.entrySet()) { - K representative = representativeMap.get(entry.getKey()); - if (entry.getValue().size() == 1) { - K singleton = entry.getValue().iterator().next(); - assert representative == null || singleton == representative; - if (representative == null) { - biMap.put(entry.getKey(), singleton); - } else { - extraMap.put(entry.getKey(), singleton); - } - } else { - if (representative == null) { - representative = pickRepresentative.apply(entry.getValue()); - } else { - assert representative == entry.getKey() || entry.getValue().contains(representative); - } - extraMap.put(entry.getKey(), representative); - } - } - - return new ManyToOneInverseMap<>(biMap, extraMap); - } - - public <NewV> ManyToOneMap<K, NewV> remap( - BiMap<V, NewV> biMap, Function<V, NewV> notInBiMap, Function<V, K> notInForwardMap) { - ManyToOneMap<K, NewV> newMap = new ManyToOneMap<>(); - - // All entries that should be remapped and are already in the forward and/or inverse mappings - // should only be remapped in the directions they are already mapped in. - BiMap<V, NewV> biMapCopy = HashBiMap.create(biMap); - for (Entry<V, Set<K>> entry : inverseMap.entrySet()) { - NewV to = biMapCopy.remove(entry.getKey()); - if (to == null) { - to = biMap.getOrDefault(entry.getKey(), notInBiMap.apply(entry.getKey())); - } - newMap.inverseMap.put(to, entry.getValue()); - } - for (Entry<K, V> entry : forwardMap.entrySet()) { - NewV newTo = biMapCopy.remove(entry.getValue()); - if (newTo == null) { - newTo = biMap.getOrDefault(entry.getValue(), notInBiMap.apply(entry.getValue())); - } - newMap.forwardMap.put(entry.getKey(), newTo); - } - - // All new entries should be mapped in both directions. - for (Entry<V, NewV> entry : biMapCopy.entrySet()) { - newMap.forwardMap.put(notInForwardMap.apply(entry.getKey()), entry.getValue()); - newMap - .inverseMap - .computeIfAbsent(entry.getValue(), ignore -> new HashSet<>()) - .add(notInForwardMap.apply(entry.getKey())); - } - - // Representatives are always in the inverse mapping, so they should always be remapped as new - // representatives. - for (Entry<V, K> entry : representativeMap.entrySet()) { - NewV newTo = biMap.get(entry.getKey()); - if (newTo == null) { - newTo = notInBiMap.apply(entry.getKey()); - } - newMap.representativeMap.put(newTo, entry.getValue()); - } - - return newMap; - } -}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/Policy.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/Policy.java index f6b86a3..4642229 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/Policy.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/Policy.java
@@ -12,6 +12,8 @@ /** Counter keeping track of how many classes this policy has removed. For debugging only. */ public int numberOfRemovedClasses; + public void clear() {} + public boolean shouldSkipPolicy() { return false; }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java index b2fdb3d..14b249a 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SimplePolicyExecutor.java
@@ -70,6 +70,8 @@ linkedGroups = applyMultiClassPolicy((MultiClassPolicy) policy, linkedGroups); } + policy.clear(); + // Any policy should not return any trivial groups. assert linkedGroups.stream().allMatch(group -> group.size() >= 2); }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java index b6e1f53..c3aa3c0 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
@@ -107,7 +107,9 @@ boolean requiresMainDex = appView.appInfo().getMainDexClasses().containsAnyOf(mergeClasses); List<DexType> syntheticArgumentTypes = new ArrayList<>(); - for (int i = 0; i < appView.options().horizontalClassMergingSyntheticArgumentCount; i++) { + for (int i = 0; + i < appView.options().horizontalClassMergerOptions().getSyntheticArgumentCount(); + i++) { syntheticArgumentTypes.add( synthesizeClass(appView, appBuilder, context, requiresMainDex, i)); }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java index da61076..0f85054 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/TreeFixer.java
@@ -45,8 +45,6 @@ private final FieldAccessInfoCollectionModifier.Builder fieldAccessChangesBuilder; private final AppView<AppInfoWithLiveness> appView; private final DexItemFactory dexItemFactory; - private final BiMap<DexMethod, DexMethod> movedMethods = HashBiMap.create(); - private final BiMap<DexField, DexField> movedFields = HashBiMap.create(); private final SyntheticArgumentClass syntheticArgumentClass; private final BiMap<DexMethodSignature, DexMethodSignature> reservedInterfaceSignatures = HashBiMap.create(); @@ -128,10 +126,6 @@ for (DexProgramClass root : subtypingForrest.getProgramRoots()) { subtypingForrest.traverseNodeDepthFirst(root, HashBiMap.create(), this::fixupProgramClass); } - - lensBuilder.remapMethods(movedMethods); - lensBuilder.remapFields(movedFields); - HorizontalClassMergerGraphLens lens = lensBuilder.build(appView, mergedClasses); fieldAccessChangesBuilder.build(this::fixupMethodReference).modify(appView); new AnnotationFixer(lens).run(appView.appInfo().classes()); @@ -164,6 +158,8 @@ fixupFields(clazz.staticFields(), clazz::setStaticField); fixupFields(clazz.instanceFields(), clazz::setInstanceField); + lensBuilder.commitPendingUpdates(); + return remappedClassVirtualMethods; } @@ -173,7 +169,7 @@ // Don't process this method if it does not refer to a merge class type. boolean referencesMergeClass = Iterables.any( - originalMethodReference.proto.getBaseTypes(dexItemFactory), + originalMethodReference.getProto().getBaseTypes(dexItemFactory), mergedClasses::hasBeenMergedOrIsMergeTarget); if (!referencesMergeClass) { return method; @@ -200,8 +196,7 @@ DexMethod newMethodReference = newMethodSignature.withHolder(originalMethodReference, dexItemFactory); - movedMethods.put(originalMethodReference, newMethodReference); - + lensBuilder.fixupMethod(originalMethodReference, newMethodReference); return method.toTypeSubstitutedMethod(newMethodReference); } @@ -217,17 +212,17 @@ iface.getMethodCollection().replaceVirtualMethods(this::fixupVirtualInterfaceMethod); fixupFields(iface.staticFields(), iface::setStaticField); fixupFields(iface.instanceFields(), iface::setInstanceField); + lensBuilder.commitPendingUpdates(); } private DexEncodedMethod fixupProgramMethod( DexMethod newMethodReference, DexEncodedMethod method) { DexMethod originalMethodReference = method.getReference(); - if (newMethodReference == originalMethodReference) { return method; } - movedMethods.put(originalMethodReference, newMethodReference); + lensBuilder.fixupMethod(originalMethodReference, newMethodReference); DexEncodedMethod newMethod = method.toTypeSubstitutedMethod(newMethodReference); if (newMethod.isNonPrivateVirtualMethod()) { @@ -365,26 +360,26 @@ Set<DexField> existingFields = Sets.newIdentityHashSet(); for (int i = 0; i < fields.size(); i++) { - DexEncodedField encodedField = fields.get(i); - DexField field = encodedField.field; - DexField newField = fixupFieldReference(field); + DexEncodedField oldField = fields.get(i); + DexField oldFieldReference = oldField.getReference(); + DexField newFieldReference = fixupFieldReference(oldFieldReference); // Rename the field if it already exists. - if (!existingFields.add(newField)) { - DexField template = newField; - newField = + if (!existingFields.add(newFieldReference)) { + DexField template = newFieldReference; + newFieldReference = dexItemFactory.createFreshMember( tryName -> Optional.of(template.withName(tryName, dexItemFactory)) .filter(tryMethod -> !existingFields.contains(tryMethod)), - newField.name.toSourceString()); - boolean added = existingFields.add(newField); + newFieldReference.name.toSourceString()); + boolean added = existingFields.add(newFieldReference); assert added; } - if (newField != encodedField.field) { - movedFields.put(field, newField); - setter.setField(i, encodedField.toTypeSubstitutedField(newField)); + if (newFieldReference != oldFieldReference) { + lensBuilder.fixupField(oldFieldReference, newFieldReference); + setter.setField(i, oldField.toTypeSubstitutedField(newFieldReference)); } } }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodEntryPointSynthesizedCode.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodEntryPointSynthesizedCode.java index d6690a9..7428b92 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodEntryPointSynthesizedCode.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodEntryPointSynthesizedCode.java
@@ -13,7 +13,6 @@ public class VirtualMethodEntryPointSynthesizedCode extends SynthesizedCode { private final Int2ReferenceSortedMap<DexMethod> mappedMethods; - private final DexField classIdField; public VirtualMethodEntryPointSynthesizedCode( Int2ReferenceSortedMap<DexMethod> mappedMethods, @@ -27,7 +26,6 @@ mappedMethods, classIdField, superMethod, method, position, originalMethod)); this.mappedMethods = mappedMethods; - this.classIdField = classIdField; } @Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java index fe3a898..9a44de7 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -229,8 +229,7 @@ classFileVersion = Ordered.maxIgnoreNull(classFileVersion, methodVersion); } DexMethod newMethod = moveMethod(classMethodsBuilder, method); - lensBuilder.mapMethod(newMethod, newMethod); - lensBuilder.mapMethodInverse(method.getReference(), newMethod); + lensBuilder.recordNewMethodSignature(method.getReference(), newMethod); classIdToMethodMap.put(classIdentifiers.getInt(method.getHolderType()), newMethod); if (representative == null) { representative = method; @@ -274,16 +273,14 @@ // Map each old non-abstract method to the newly synthesized method in the graph lens. for (ProgramMethod oldMethod : methods) { - if (oldMethod.getDefinition().isAbstract()) { - lensBuilder.mapMethod(oldMethod.getReference(), newMethodReference); - } else { - lensBuilder.moveMethod(oldMethod.getReference(), newMethodReference); - } + lensBuilder.mapMethod(oldMethod.getReference(), newMethodReference); } - lensBuilder.recordExtraOriginalSignature(bridgeMethodReference, newMethodReference); + + // Add a mapping from a synthetic name to the synthetic merged method. + lensBuilder.recordNewMethodSignature(bridgeMethodReference, newMethodReference); classMethodsBuilder.addVirtualMethod(newMethod); - fieldAccessChangesBuilder.fieldReadByMethod(group.getClassIdField(), newMethod.method); + fieldAccessChangesBuilder.fieldReadByMethod(group.getClassIdField(), newMethodReference); } }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/IgnoreSynthetics.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/IgnoreSynthetics.java index 78875fd..e45e2d3 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/IgnoreSynthetics.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/IgnoreSynthetics.java
@@ -19,6 +19,10 @@ @Override public boolean canMerge(DexProgramClass program) { - return !appView.getSyntheticItems().isSyntheticClass(program); + if (appView.getSyntheticItems().isSyntheticClass(program)) { + return appView.options().horizontalClassMergerOptions().isJavaLambdaMergingEnabled() + && appView.getSyntheticItems().isLegacySyntheticClass(program); + } + return true; } }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java index f2800aa..33de8f3 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/LimitGroups.java
@@ -18,7 +18,7 @@ private final int maxGroupSize; public LimitGroups(AppView<AppInfoWithLiveness> appView) { - maxGroupSize = appView.options().horizontalClassMergingMaxGroupSize; + maxGroupSize = appView.options().horizontalClassMergerOptions().getMaxGroupSize(); assert maxGroupSize >= 2; }
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerWithObservableSideEffects.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerWithObservableSideEffects.java new file mode 100644 index 0000000..f10d78c --- /dev/null +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoClassInitializerWithObservableSideEffects.java
@@ -0,0 +1,31 @@ +// 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.horizontalclassmerging.policies; + +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy; + +/** + * Prevent merging of classes with static initializers, as merging these causes side effects. It is + * okay for superclasses to have static initializers as all classes are expected to have the same + * super class. + */ +public class NoClassInitializerWithObservableSideEffects extends SingleClassPolicy { + + @Override + public boolean canMerge(DexProgramClass program) { + if (!program.hasClassInitializer()) { + return true; + } + DexEncodedMethod clinit = program.getClassInitializer(); + return clinit.getOptimizationInfo().classInitializerMayBePostponed() || isKotlinLambda(program); + } + + private boolean isKotlinLambda(DexProgramClass program) { + return program.getKotlinInfo().isSyntheticClass() + && program.getKotlinInfo().asSyntheticClass().isLambda(); + } +}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java index 2da0177..c8a0aab 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoEnums.java
@@ -22,8 +22,19 @@ } @Override + public void clear() { + cache.clear(); + } + + @Override public boolean canMerge(DexProgramClass program) { - return !program.isEnum() && !isEnumSubtype(program); + if (program.isEnum()) { + return false; + } + if (isEnumSubtype(program)) { + return false; + } + return true; } private boolean isEnumSubtype(DexClass clazz) {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java index a137e88..dee7a4a 100644 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java +++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoKotlinLambdas.java
@@ -18,7 +18,7 @@ @Override public boolean shouldSkipPolicy() { - return appView.options().enableHorizontalClassMergingOfKotlinLambdas; + return appView.options().horizontalClassMergerOptions().isKotlinLambdaMergingEnabled(); } @Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoStaticClassInitializer.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoStaticClassInitializer.java deleted file mode 100644 index aeca6c0..0000000 --- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoStaticClassInitializer.java +++ /dev/null
@@ -1,20 +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.horizontalclassmerging.policies; - -import com.android.tools.r8.graph.DexProgramClass; -import com.android.tools.r8.horizontalclassmerging.SingleClassPolicy; - -/** - * Prevent merging of classes with static initializers, as merging these causes side effects. It is - * okay for superclasses to have static initializers as all classes are expected to have the same - * super class. - */ -public class NoStaticClassInitializer extends SingleClassPolicy { - @Override - public boolean canMerge(DexProgramClass program) { - return !program.hasClassInitializer(); - } -}
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 8e52af1..ee985c4 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
@@ -8,6 +8,7 @@ import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexValue; +import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.code.BasicBlock; import com.android.tools.r8.ir.code.DominatorTree; @@ -86,10 +87,16 @@ return null; } + boolean isStaticFieldValueAnalysis() { + return false; + } + StaticFieldValueAnalysis asStaticFieldValueAnalysis() { return null; } + abstract boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field); + abstract boolean isSubjectToOptimization(DexEncodedField field); void recordFieldPut(DexEncodedField field, Instruction instruction) { @@ -118,9 +125,18 @@ if (instruction.isFieldPut()) { FieldInstruction fieldPut = instruction.asFieldInstruction(); DexField field = fieldPut.getField(); - DexEncodedField encodedField = appInfo.resolveField(field).getResolvedField(); - if (encodedField != null && isSubjectToOptimization(encodedField)) { - recordFieldPut(encodedField, fieldPut); + SuccessfulFieldResolutionResult fieldResolutionResult = + appInfo.resolveField(field).asSuccessfulResolution(); + if (fieldResolutionResult != null) { + DexEncodedField encodedField = fieldResolutionResult.getResolvedField(); + assert encodedField != null; + if (isSubjectToOptimization(encodedField)) { + recordFieldPut(encodedField, fieldPut); + } else if (isStaticFieldValueAnalysis() + && fieldResolutionResult.getResolvedHolder().isEnum() + && isSubjectToOptimizationIgnoringPinning(encodedField)) { + recordFieldPut(encodedField, fieldPut); + } } } else if (isInstanceFieldValueAnalysis() && instruction.isInvokeConstructor(appView.dexItemFactory())) { @@ -153,15 +169,15 @@ boolean priorReadsWillReadSameValue = !classInitializerDefaultsResult.hasStaticValue(field) && fieldPut.value().isZero(); if (!priorReadsWillReadSameValue && fieldMaybeReadBeforeInstruction(field, fieldPut)) { - if (!isInstanceFieldValueAnalysis()) { + // 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, fieldPut.value(), valueBeforePut); + .updateFieldOptimizationInfoWith2Values(field, fieldPut.value(), valueBeforePut); } continue; }
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 cc607ac..c4061ec 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
@@ -124,6 +124,11 @@ } @Override + boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field) { + throw new Unreachable("Used by static analysis only."); + } + + @Override void updateFieldOptimizationInfo(DexEncodedField field, FieldInstruction fieldPut, Value value) { if (fieldNeverWrittenBetweenInstancePutAndMethodExit(field, fieldPut.asInstancePut())) { recordInstanceFieldIsInitializedWithValue(field, value);
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 3ab1305..03455fb 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
@@ -14,6 +14,7 @@ import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.DexValue; import com.android.tools.r8.graph.DexValue.DexValueNull; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; @@ -42,12 +43,15 @@ public class StaticFieldValueAnalysis extends FieldValueAnalysis { + private final StaticFieldValues.Builder builder; + private StaticFieldValueAnalysis( AppView<AppInfoWithLiveness> appView, IRCode code, OptimizationFeedback feedback) { super(appView, code, feedback); + builder = StaticFieldValues.builder(code.context().getHolder()); } - public static void run( + public static StaticFieldValues run( AppView<?> appView, IRCode code, ClassInitializerDefaultsResult classInitializerDefaultsResult, @@ -57,9 +61,16 @@ assert appView.enableWholeProgramOptimizations(); assert code.context().getDefinition().isClassInitializer(); timing.begin("Analyze class initializer"); - new StaticFieldValueAnalysis(appView.withLiveness(), code, feedback) - .computeFieldOptimizationInfo(classInitializerDefaultsResult); + StaticFieldValues result = + new StaticFieldValueAnalysis(appView.withLiveness(), code, feedback) + .analyze(classInitializerDefaultsResult, code.context().getHolderType()); timing.end(); + return result; + } + + @Override + boolean isStaticFieldValueAnalysis() { + return true; } @Override @@ -67,6 +78,12 @@ return this; } + StaticFieldValues analyze( + ClassInitializerDefaultsResult classInitializerDefaultsResult, DexType holderType) { + computeFieldOptimizationInfo(classInitializerDefaultsResult); + return builder.build(); + } + @Override void computeFieldOptimizationInfo(ClassInitializerDefaultsResult classInitializerDefaultsResult) { super.computeFieldOptimizationInfo(classInitializerDefaultsResult); @@ -105,14 +122,32 @@ } @Override - void updateFieldOptimizationInfo(DexEncodedField field, FieldInstruction fieldPut, Value value) { - // Abstract value. - feedback.recordFieldHasAbstractValue(field, appView, getOrComputeAbstractValue(value, field)); - - setDynamicType(field, value, false); + boolean isSubjectToOptimizationIgnoringPinning(DexEncodedField field) { + return field.isStatic() + && field.getHolderType() == context.getHolderType() + && appView + .appInfo() + .isFieldOnlyWrittenInMethodIgnoringPinning(field, context.getDefinition()); } - private void setDynamicType(DexEncodedField field, Value value, boolean maybeNull) { + @Override + void updateFieldOptimizationInfo(DexEncodedField 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) { + builder.recordStaticField(field, abstractValue, appView.dexItemFactory()); + + // We cannot modify FieldOptimizationInfo of pinned fields. + if (appView.appInfo().isPinned(field)) { + return; + } + + // Abstract value. + feedback.recordFieldHasAbstractValue(field, appView, abstractValue); + // Dynamic upper bound type. TypeElement fieldType = TypeElement.fromDexType(field.field.type, Nullability.maybeNull(), appView); @@ -137,16 +172,16 @@ } public void updateFieldOptimizationInfoWith2Values( - DexEncodedField field, FieldInstruction fieldPut, Value valuePut, DexValue valueBeforePut) { + DexEncodedField 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) { return; } - feedback.recordFieldHasAbstractValue( - field, appView, NullOrAbstractValue.create(getOrComputeAbstractValue(valuePut, field))); - setDynamicType(field, valuePut, true); + AbstractValue abstractValue = + NullOrAbstractValue.create(getOrComputeAbstractValue(valuePut, field)); + updateFieldOptimizationInfo(field, valuePut, abstractValue, true); } private AbstractValue getOrComputeAbstractValue(Value value, DexEncodedField field) {
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 new file mode 100644 index 0000000..b2dcdcd --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldvalueanalysis/StaticFieldValues.java
@@ -0,0 +1,143 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.analysis.fieldvalueanalysis; + +import com.android.tools.r8.graph.DexEncodedField; +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexProgramClass; +import com.android.tools.r8.ir.analysis.value.AbstractValue; +import com.android.tools.r8.ir.analysis.value.ObjectState; +import com.google.common.collect.ImmutableMap; + +public abstract class StaticFieldValues { + + public boolean isEnumStaticFieldValues() { + return false; + } + + public EnumStaticFieldValues asEnumStaticFieldValues() { + return null; + } + + public static Builder builder(DexProgramClass clazz) { + return clazz.isEnum() ? EnumStaticFieldValues.builder() : EmptyStaticValues.builder(); + } + + public abstract static class Builder { + + public abstract void recordStaticField( + DexEncodedField staticField, AbstractValue value, DexItemFactory factory); + + public abstract StaticFieldValues build(); + } + + // All the abstract values stored here may match a pinned field, using them requires therefore + // to check the field is not pinned or prove it is no longer pinned. + public static class EnumStaticFieldValues extends StaticFieldValues { + private final ImmutableMap<DexField, AbstractValue> enumAbstractValues; + private final DexField valuesField; + private final AbstractValue valuesAbstractValue; + + public EnumStaticFieldValues( + ImmutableMap<DexField, AbstractValue> enumAbstractValues, + DexField valuesField, + AbstractValue valuesAbstractValue) { + this.enumAbstractValues = enumAbstractValues; + this.valuesField = valuesField; + this.valuesAbstractValue = valuesAbstractValue; + } + + static StaticFieldValues.Builder builder() { + return new Builder(); + } + + public static class Builder extends StaticFieldValues.Builder { + private final ImmutableMap.Builder<DexField, AbstractValue> enumAbstractValuesBuilder = + ImmutableMap.builder(); + private DexField valuesFields; + private AbstractValue valuesAbstractValue; + + Builder() {} + + @Override + public void recordStaticField( + DexEncodedField staticField, AbstractValue value, DexItemFactory factory) { + // TODO(b/166532388): Stop relying on the values name. + if (staticField.getName() == factory.enumValuesFieldName) { + valuesFields = staticField.field; + valuesAbstractValue = value; + } else if (staticField.isEnum()) { + enumAbstractValuesBuilder.put(staticField.field, value); + } + } + + @Override + public StaticFieldValues build() { + ImmutableMap<DexField, AbstractValue> enumAbstractValues = + enumAbstractValuesBuilder.build(); + if (valuesAbstractValue == null && enumAbstractValues.isEmpty()) { + return EmptyStaticValues.getInstance(); + } + return new EnumStaticFieldValues(enumAbstractValues, valuesFields, valuesAbstractValue); + } + } + + @Override + public boolean isEnumStaticFieldValues() { + return true; + } + + @Override + public EnumStaticFieldValues asEnumStaticFieldValues() { + return this; + } + + public ObjectState getObjectStateForPossiblyPinnedField(DexField field) { + AbstractValue fieldValue = enumAbstractValues.get(field); + if (fieldValue == null || fieldValue.isZero()) { + return null; + } + if (fieldValue.isSingleFieldValue()) { + return fieldValue.asSingleFieldValue().getState(); + } + assert fieldValue.isUnknown(); + return ObjectState.empty(); + } + + public AbstractValue getValuesAbstractValueForPossiblyPinnedField(DexField field) { + assert valuesField == field || valuesAbstractValue == null; + return valuesAbstractValue; + } + } + + public static class EmptyStaticValues extends StaticFieldValues { + private static EmptyStaticValues INSTANCE = new EmptyStaticValues(); + + private EmptyStaticValues() {} + + public static EmptyStaticValues getInstance() { + return INSTANCE; + } + + static StaticFieldValues.Builder builder() { + return new Builder(); + } + + public static class Builder extends StaticFieldValues.Builder { + + @Override + public void recordStaticField( + DexEncodedField staticField, AbstractValue value, DexItemFactory factory) { + // Do nothing. + } + + @Override + public StaticFieldValues build() { + return EmptyStaticValues.getInstance(); + } + } + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/EnumValuesObjectState.java b/src/main/java/com/android/tools/r8/ir/analysis/value/EnumValuesObjectState.java index f1ec89c..2d8ddc2 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/EnumValuesObjectState.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/EnumValuesObjectState.java
@@ -33,6 +33,10 @@ return state[ordinal]; } + public int getEnumValuesSize() { + return state.length; + } + @Override public boolean isEnumValuesObjectState() { return true;
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java index 8de1c91..911174b 100644 --- a/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java +++ b/src/main/java/com/android/tools/r8/ir/analysis/value/SingleFieldValue.java
@@ -12,7 +12,6 @@ import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap; import com.android.tools.r8.graph.FieldResolutionResult.SuccessfulFieldResolutionResult; import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.ProgramMethod; @@ -23,6 +22,7 @@ import com.android.tools.r8.ir.code.StaticGet; import com.android.tools.r8.ir.code.TypeAndLocalInfoSupplier; import com.android.tools.r8.ir.code.Value; +import com.android.tools.r8.ir.optimize.enums.EnumDataMap; import com.android.tools.r8.shaking.AppInfoWithLiveness; public abstract class SingleFieldValue extends SingleValue { @@ -107,12 +107,9 @@ public SingleValue rewrittenWithLens(AppView<AppInfoWithLiveness> appView, GraphLens lens) { AbstractValueFactory factory = appView.abstractValueFactory(); if (field.holder == field.type) { - EnumValueInfoMap unboxedEnumInfo = appView.unboxedEnums().getEnumValueInfoMap(field.type); - if (unboxedEnumInfo != null) { - // Return the ordinal of the unboxed enum. - assert unboxedEnumInfo.hasEnumValueInfo(field); - return factory.createSingleNumberValue( - unboxedEnumInfo.getEnumValueInfo(field).convertToInt()); + EnumDataMap enumDataMap = appView.unboxedEnums(); + if (enumDataMap.hasUnboxedValueFor(field)) { + return factory.createSingleNumberValue(enumDataMap.getUnboxedValue(field)); } } return factory.createSingleFieldValue(
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 a3f8421..54354f6 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
@@ -275,6 +275,18 @@ } @Override + public void replaceCurrentInstructionWithConstString( + AppView<?> appView, IRCode code, DexString value) { + if (current == null) { + throw new IllegalStateException(); + } + + // Replace the instruction by const-string. + ConstString constString = code.createStringConstant(appView, value, current.getLocalInfo()); + replaceCurrentInstruction(constString); + } + + @Override public void replaceCurrentInstructionWithStaticGet( AppView<?> appView, IRCode code, DexField field, Set<Value> affectedValues) { if (current == null) { @@ -451,6 +463,16 @@ return newBlock; } + @Override + public BasicBlock splitCopyCatchHandlers( + IRCode code, ListIterator<BasicBlock> blockIterator, InternalOptions options) { + BasicBlock splitBlock = split(code, blockIterator, false); + assert !block.hasCatchHandlers(); + assert splitBlock.hasCatchHandlers(); + block.copyCatchHandlers(code, blockIterator, splitBlock, options); + return splitBlock; + } + private boolean canThrow(IRCode code) { InstructionIterator iterator = code.instructionIterator(); while (iterator.hasNext()) {
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 cb33b18..d2b9ffb 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
@@ -53,6 +53,12 @@ } @Override + public void replaceCurrentInstructionWithConstString( + AppView<?> appView, IRCode code, DexString value) { + instructionIterator.replaceCurrentInstructionWithConstString(appView, code, value); + } + + @Override public void replaceCurrentInstructionWithStaticGet( AppView<?> appView, IRCode code, DexField field, Set<Value> affectedValues) { instructionIterator.replaceCurrentInstructionWithStaticGet( @@ -86,6 +92,12 @@ } @Override + public BasicBlock splitCopyCatchHandlers( + IRCode code, ListIterator<BasicBlock> blockIterator, InternalOptions options) { + throw new Unimplemented(); + } + + @Override public BasicBlock inlineInvoke( AppView<?> appView, IRCode code,
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 792018b..1acdb98 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
@@ -96,6 +96,14 @@ void replaceCurrentInstructionWithConstInt(IRCode code, int value); + void replaceCurrentInstructionWithConstString(AppView<?> appView, IRCode code, DexString value); + + default void replaceCurrentInstructionWithConstString( + AppView<?> appView, IRCode code, String value) { + replaceCurrentInstructionWithConstString( + appView, code, appView.dexItemFactory().createString(value)); + } + void replaceCurrentInstructionWithStaticGet( AppView<?> appView, IRCode code, DexField field, Set<Value> affectedValues); @@ -146,6 +154,9 @@ return split(code, null); } + BasicBlock splitCopyCatchHandlers( + IRCode code, ListIterator<BasicBlock> blockIterator, InternalOptions options); + /** * Split the block into three blocks. The first split is at the point of the {@link ListIterator} * cursor and the second split is <code>instructions</code> after the cursor. The existing
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 6daa9a4..9d7d69b 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
@@ -30,7 +30,9 @@ import com.android.tools.r8.ir.regalloc.RegisterAllocator; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.collections.ProgramMethodSet; +import com.google.common.collect.ImmutableList; import java.util.BitSet; +import java.util.Collections; import java.util.List; public abstract class InvokeMethod extends Invoke { @@ -238,4 +240,30 @@ } return false; } + + abstract static class Builder<B extends Builder<B, I>, I extends InvokeMethod> + extends BuilderBase<B, I> { + + protected DexMethod method; + protected List<Value> arguments = Collections.emptyList(); + + public B setArguments(List<Value> arguments) { + assert arguments != null; + this.arguments = arguments; + return self(); + } + + public B setSingleArgument(Value argument) { + return setArguments(ImmutableList.of(argument)); + } + + public B setMethod(DexMethod method) { + this.method = method; + return self(); + } + + public B setMethod(DexClassAndMethod method) { + return setMethod(method.getReference()); + } + } }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java index 3139d20..1b3aef3 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeStatic.java
@@ -27,8 +27,6 @@ import com.android.tools.r8.ir.optimize.InliningConstraints; import com.android.tools.r8.ir.optimize.inliner.WhyAreYouNotInliningReporter; import com.android.tools.r8.shaking.AppInfoWithLiveness; -import com.google.common.collect.ImmutableList; -import java.util.Collections; import java.util.List; public class InvokeStatic extends InvokeMethod { @@ -235,29 +233,7 @@ .classInitializationMayHaveSideEffectsInContext(appView, context); } - public static class Builder extends BuilderBase<Builder, InvokeStatic> { - - private DexMethod method; - private List<Value> arguments = Collections.emptyList(); - - public Builder setArguments(List<Value> arguments) { - assert arguments != null; - this.arguments = arguments; - return this; - } - - public Builder setSingleArgument(Value argument) { - return setArguments(ImmutableList.of(argument)); - } - - public Builder setMethod(DexMethod method) { - this.method = method; - return this; - } - - public Builder setMethod(DexClassAndMethod method) { - return setMethod(method.getReference()); - } + public static class Builder extends InvokeMethod.Builder<Builder, InvokeStatic> { @Override public InvokeStatic build() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java index d4f737e..e063b8f 100644 --- a/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java +++ b/src/main/java/com/android/tools/r8/ir/code/InvokeVirtual.java
@@ -33,6 +33,10 @@ super(target, result, arguments); } + public static Builder builder() { + return new Builder(); + } + @Override public boolean getInterfaceBit() { return false; @@ -167,4 +171,17 @@ return ClassInitializationAnalysis.InstructionUtils.forInvokeVirtual( this, clazz, context, appView, mode, assumption); } + + public static class Builder extends InvokeMethod.Builder<Builder, InvokeVirtual> { + + @Override + public InvokeVirtual build() { + return amend(new InvokeVirtual(method, outValue, arguments)); + } + + @Override + public Builder self() { + return this; + } + } }
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 889089f..e6009dd 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
@@ -73,6 +73,12 @@ } @Override + public void replaceCurrentInstructionWithConstString( + AppView<?> appView, IRCode code, DexString value) { + currentBlockIterator.replaceCurrentInstructionWithConstString(appView, code, value); + } + + @Override public void replaceCurrentInstructionWithStaticGet( AppView<?> appView, IRCode code, DexField field, Set<Value> affectedValues) { currentBlockIterator.replaceCurrentInstructionWithStaticGet( @@ -102,6 +108,12 @@ } @Override + public BasicBlock splitCopyCatchHandlers( + IRCode code, ListIterator<BasicBlock> blockIterator, InternalOptions options) { + return currentBlockIterator.splitCopyCatchHandlers(code, blockIterator, options); + } + + @Override public BasicBlock inlineInvoke( AppView<?> appView, IRCode code,
diff --git a/src/main/java/com/android/tools/r8/ir/code/Position.java b/src/main/java/com/android/tools/r8/ir/code/Position.java index ab28d55..0d054ef 100644 --- a/src/main/java/com/android/tools/r8/ir/code/Position.java +++ b/src/main/java/com/android/tools/r8/ir/code/Position.java
@@ -7,11 +7,13 @@ import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.ProgramMethod; +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.annotations.VisibleForTesting; -import java.util.Comparator; import java.util.Objects; -public class Position implements Comparable<Position> { +public class Position implements StructuralItem<Position> { // A no-position marker. Not having a position means the position is implicitly defined by the // context, e.g., the marker does not materialize anything concrete. @@ -36,6 +38,14 @@ public final DexMethod method; public final Position callerPosition; + private static void specify(StructuralSpecification<Position, ?> spec) { + spec.withInt(p -> p.line) + .withNullableItem(p -> p.file) + .withBool(p -> p.synthetic) + .withNullableItem(p -> p.method) + .withNullableItem(p -> p.callerPosition); + } + public Position(int line, DexString file, DexMethod method, Position callerPosition) { this(line, file, method, callerPosition, false); assert line >= 0; @@ -91,6 +101,16 @@ return position; } + @Override + public Position self() { + return this; + } + + @Override + public StructuralMapping<Position> getStructuralMapping() { + return Position::specify; + } + public boolean isNone() { return line == -1; } @@ -128,19 +148,6 @@ return result; } - @Override - public int compareTo(Position o) { - if (this == o) { - return 0; - } - return Comparator.comparingInt((Position p) -> p.line) - .thenComparing(p -> p.file, Comparator.nullsFirst(DexString::compareTo)) - .thenComparing(p -> p.synthetic) - .thenComparing(p -> p.method) - .thenComparing(p -> p.callerPosition, Comparator.nullsFirst(Position::compareTo)) - .compare(this, o); - } - private String toString(boolean forceMethod) { if (isNone()) { return "--";
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 2ef58cd..7861771 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
@@ -22,7 +22,6 @@ import com.android.tools.r8.graph.DexString; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.DexTypeList; -import com.android.tools.r8.graph.EnumValueInfoMapCollection; import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses; @@ -33,6 +32,7 @@ import com.android.tools.r8.ir.analysis.fieldaccess.TrivialFieldAccessReprocessor; import com.android.tools.r8.ir.analysis.fieldvalueanalysis.InstanceFieldValueAnalysis; import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValueAnalysis; +import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues; import com.android.tools.r8.ir.analysis.type.TypeElement; import com.android.tools.r8.ir.code.AlwaysMaterializingDefinition; import com.android.tools.r8.ir.code.AlwaysMaterializingUser; @@ -73,6 +73,7 @@ import com.android.tools.r8.ir.optimize.ReflectionOptimizer; import com.android.tools.r8.ir.optimize.ServiceLoaderRewriter; import com.android.tools.r8.ir.optimize.classinliner.ClassInliner; +import com.android.tools.r8.ir.optimize.enums.EnumDataMap; import com.android.tools.r8.ir.optimize.enums.EnumUnboxer; import com.android.tools.r8.ir.optimize.enums.EnumValueOptimizer; import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoCollector; @@ -487,13 +488,13 @@ // Classes which has already been through library desugaring will not go through IR // processing again. LibraryDesugaredChecker libraryDesugaredChecker = new LibraryDesugaredChecker(appView); - Set<DexProgramClass> alreadyLibraryDesugared = Sets.newConcurrentHashSet(); + Set<DexType> alreadyLibraryDesugared = Sets.newConcurrentHashSet(); ThreadUtils.processItems( application.classes(), clazz -> { if (libraryDesugaredChecker.isClassLibraryDesugared(clazz)) { if (appView.options().desugarSpecificOptions().allowAllDesugaredInput) { - alreadyLibraryDesugared.add(clazz); + alreadyLibraryDesugared.add(clazz.getType()); } else { throw new CompilationError( "Code for " @@ -752,7 +753,7 @@ if (enumUnboxer != null) { enumUnboxer.unboxEnums(postMethodProcessorBuilder, executorService, feedback); } else { - appView.setUnboxedEnums(EnumValueInfoMapCollection.empty()); + appView.setUnboxedEnums(EnumDataMap.empty()); } if (!options.debug) { new TrivialFieldAccessReprocessor(appView.withLiveness(), postMethodProcessorBuilder) @@ -1708,16 +1709,21 @@ } InstanceFieldInitializationInfoCollection instanceFieldInitializationInfos = null; + StaticFieldValues staticFieldValues = null; if (method.getDefinition().isInitializer()) { if (method.getDefinition().isClassInitializer()) { - StaticFieldValueAnalysis.run( - appView, code, classInitializerDefaultsResult, feedback, timing); + staticFieldValues = + StaticFieldValueAnalysis.run( + appView, code, classInitializerDefaultsResult, feedback, timing); } else { instanceFieldInitializationInfos = InstanceFieldValueAnalysis.run( appView, code, classInitializerDefaultsResult, feedback, timing); } } + if (enumUnboxer != null) { + enumUnboxer.recordEnumState(method.getHolder(), staticFieldValues); + } methodOptimizationInfoCollector.collectMethodOptimizationInfo( method, code, feedback, dynamicTypeOptimization, instanceFieldInitializationInfos, timing); }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java index 84fd203..1e3b81f 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -26,6 +26,7 @@ import com.android.tools.r8.ir.synthetic.SynthesizedCode; import com.android.tools.r8.position.MethodPosition; import com.android.tools.r8.utils.MethodSignatureEquivalence; +import com.android.tools.r8.utils.WorkList; import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.ImmutableList; @@ -421,8 +422,39 @@ if (clazz.isNotProgramClass()) { return; } + Set<DexType> filtered = new HashSet<>(emulatedInterfaces); + WorkList<DexType> workList = WorkList.newIdentityWorkList(); + for (DexType emulatedInterface : emulatedInterfaces) { + DexClass iface = appView.definitionFor(emulatedInterface); + if (iface != null) { + assert iface.isLibraryClass() + || appView.options().desugaredLibraryConfiguration.isLibraryCompilation(); + workList.addIfNotSeen(iface.getInterfaces()); + } + } + while (workList.hasNext()) { + DexType type = workList.next(); + filtered.remove(type); + DexClass iface = appView.definitionFor(type); + if (iface == null) { + continue; + } + workList.addIfNotSeen(iface.getInterfaces()); + } + + for (DexType emulatedInterface : emulatedInterfaces) { + DexClass s = appView.definitionFor(emulatedInterface); + if (s != null) { + s = appView.definitionFor(s.superType); + } + while (s != null && s.getType() != appView.dexItemFactory().objectType) { + filtered.remove(s.getType()); + s = appView.definitionFor(s.getSuperType()); + } + } + // We need to introduce them in deterministic order for deterministic compilation. - ArrayList<DexType> sortedEmulatedInterfaces = new ArrayList<>(emulatedInterfaces); + ArrayList<DexType> sortedEmulatedInterfaces = new ArrayList<>(filtered); Collections.sort(sortedEmulatedInterfaces); List<GenericSignature.ClassTypeSignature> extraInterfaceSignatures = new ArrayList<>(); for (DexType extraInterface : sortedEmulatedInterfaces) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java index 361ab4b..0159139 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/DesugaredLibraryWrapperSynthesizer.java
@@ -441,7 +441,7 @@ factory.createMethod( field.holder, factory.createProto(factory.voidType, field.type), - factory.initMethodName); + factory.constructorMethodName); return newSynthesizedMethod( method, Constants.ACC_PRIVATE | Constants.ACC_SYNTHETIC,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java index abe232f..d73a7c1 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceProcessor.java
@@ -49,8 +49,10 @@ import com.android.tools.r8.origin.SynthesizedOrigin; import com.android.tools.r8.utils.Pair; import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap; +import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap; import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; -import com.google.common.collect.BiMap; +import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap; import com.google.common.collect.ImmutableList; import java.util.ArrayDeque; import java.util.ArrayList; @@ -521,17 +523,15 @@ public InterfaceProcessorNestedGraphLens( Map<DexType, DexType> typeMap, Map<DexMethod, DexMethod> methodMap, - Map<DexField, DexField> fieldMap, - BiMap<DexField, DexField> originalFieldSignatures, - BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures, - BidirectionalOneToOneHashMap<DexMethod, DexMethod> extraOriginalMethodSignatures, + BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap, + BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures, + BidirectionalOneToOneMap<DexMethod, DexMethod> extraOriginalMethodSignatures, GraphLens previousLens, DexItemFactory dexItemFactory) { super( typeMap, methodMap, fieldMap, - originalFieldSignatures, originalMethodSignatures, previousLens, dexItemFactory); @@ -601,7 +601,7 @@ public static class Builder extends NestedGraphLens.Builder { - private final BidirectionalOneToOneHashMap<DexMethod, DexMethod> + private final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> extraOriginalMethodSignatures = new BidirectionalOneToOneHashMap<>(); public void recordCodeMovedToCompanionClass(DexMethod from, DexMethod to) { @@ -613,7 +613,7 @@ @Override public InterfaceProcessorNestedGraphLens build( DexItemFactory dexItemFactory, GraphLens previousLens) { - if (originalFieldSignatures.isEmpty() + if (fieldMap.isEmpty() && originalMethodSignatures.isEmpty() && extraOriginalMethodSignatures.isEmpty()) { return null; @@ -622,7 +622,6 @@ typeMap, methodMap, fieldMap, - originalFieldSignatures, originalMethodSignatures, extraOriginalMethodSignatures, previousLens,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java index c9e47c2..a50581e 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -33,6 +33,8 @@ import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult; import com.android.tools.r8.ir.code.Invoke; import com.android.tools.r8.ir.code.Invoke.Type; +import com.android.tools.r8.ir.optimize.info.OptimizationFeedback; +import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple; import com.android.tools.r8.ir.synthetic.ForwardMethodSourceCode; import com.android.tools.r8.ir.synthetic.SynthesizedCode; import com.android.tools.r8.origin.SynthesizedOrigin; @@ -65,6 +67,8 @@ */ public final class LambdaClass { + private static final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance(); + final AppView<?> appView; final LambdaRewriter rewriter; public final DexType type; @@ -293,6 +297,7 @@ ParameterAnnotationsList.empty(), LambdaClassConstructorSourceCode.build(this), true); + feedback.classInitializerMayBePostponed(methods[1]); } return methods; }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java index e4d6f65..f464fdd 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaRewriter.java
@@ -18,7 +18,6 @@ import com.android.tools.r8.graph.CfCode; import com.android.tools.r8.graph.DexApplication.Builder; import com.android.tools.r8.graph.DexCallSite; -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.DexMethod; @@ -42,10 +41,10 @@ import com.android.tools.r8.ir.code.ValueType; import com.android.tools.r8.ir.conversion.IRConverter; import com.android.tools.r8.utils.DescriptorUtils; -import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; +import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap; +import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap; import com.android.tools.r8.utils.collections.SortedProgramMethodSet; import com.google.common.base.Suppliers; -import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import java.util.ArrayList; @@ -73,7 +72,7 @@ public static final String LAMBDA_CLASS_NAME_PREFIX = "-$$Lambda$"; public static final String LAMBDA_GROUP_CLASS_NAME_PREFIX = "-$$LambdaGroup$"; static final String EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$"; - private static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE"; + public static final String LAMBDA_INSTANCE_FIELD_NAME = "INSTANCE"; private final AppView<?> appView; @@ -133,7 +132,7 @@ */ public int desugarLambdas(ProgramMethod method, AppInfoWithClassHierarchy appInfo) { return desugarLambdas( - method.getDefinition(), + method, callsite -> { LambdaDescriptor descriptor = LambdaDescriptor.tryInfer(callsite, appInfo, method); if (descriptor == null) { @@ -145,8 +144,8 @@ // Same as above, but where lambdas are always known to exist for the call sites. public static int desugarLambdas( - DexEncodedMethod method, Function<DexCallSite, LambdaClass> callSites) { - CfCode code = method.getCode().asCfCode(); + ProgramMethod method, Function<DexCallSite, LambdaClass> callSites) { + CfCode code = method.getDefinition().getCode().asCfCode(); List<CfInstruction> instructions = code.getInstructions(); Supplier<List<CfInstruction>> lazyNewInstructions = Suppliers.memoize(() -> new ArrayList<>(instructions)); @@ -439,16 +438,14 @@ LambdaRewriterLens( Map<DexType, DexType> typeMap, Map<DexMethod, DexMethod> methodMap, - Map<DexField, DexField> fieldMap, - BiMap<DexField, DexField> originalFieldSignatures, - BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures, + BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap, + BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures, GraphLens previousLens, DexItemFactory dexItemFactory) { super( typeMap, methodMap, fieldMap, - originalFieldSignatures, originalMethodSignatures, previousLens, dexItemFactory); @@ -479,7 +476,6 @@ typeMap, methodMap, fieldMap, - originalFieldSignatures, originalMethodSignatures, previousLens, dexItemFactory);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java index 8889ec2..12418b3 100644 --- a/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java +++ b/src/main/java/com/android/tools/r8/ir/desugar/NestedPrivateMethodLens.java
@@ -12,7 +12,7 @@ import com.android.tools.r8.graph.GraphLens.NestedGraphLens; import com.android.tools.r8.graph.RewrittenPrototypeDescription; import com.android.tools.r8.ir.code.Invoke.Type; -import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap; +import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap; import com.google.common.collect.ImmutableMap; import java.util.IdentityHashMap; import java.util.Map; @@ -34,9 +34,8 @@ super( ImmutableMap.of(), methodMap, - ImmutableMap.of(), - null, - BidirectionalManyToManyRepresentativeMap.empty(), + new EmptyBidirectionalOneToOneMap<>(), + new EmptyBidirectionalOneToOneMap<>(), previousLens, appView.dexItemFactory()); // No concurrent maps here, we do not want synchronization overhead.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java index d648bc1..f4232a2 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/Devirtualizer.java
@@ -26,6 +26,7 @@ import com.android.tools.r8.ir.code.InvokeVirtual; import com.android.tools.r8.ir.code.Value; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.InternalOptions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; @@ -47,9 +48,11 @@ public class Devirtualizer { private final AppView<AppInfoWithLiveness> appView; + private final InternalOptions options; public Devirtualizer(AppView<AppInfoWithLiveness> appView) { this.appView = appView; + this.options = appView.options(); } public void devirtualizeInvokeInterface(IRCode code) { @@ -115,6 +118,7 @@ } } } + continue; } if (current.isInvokeSuper()) { @@ -122,7 +126,7 @@ // Check if the instruction can be rewritten to invoke-super. This allows inlining of the // enclosing method into contexts outside the current class. - if (appView.options().testing.enableInvokeSuperToInvokeVirtualRewriting) { + if (options.testing.enableInvokeSuperToInvokeVirtualRewriting) { DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context); if (singleTarget != null) { DexMethod invokedMethod = invoke.getInvokedMethod(); @@ -225,7 +229,18 @@ if (castedReceiverCache.containsKey(receiver) && castedReceiverCache.get(receiver).containsKey(holderClass.getType())) { Value cachedReceiver = castedReceiverCache.get(receiver).get(holderClass.getType()); - if (dominatorTree.dominatedBy(block, cachedReceiver.definition.getBlock())) { + BasicBlock cachedReceiverBlock = cachedReceiver.definition.getBlock(); + BasicBlock dominatorBlock = null; + if (cachedReceiverBlock.hasCatchHandlers()) { + if (cachedReceiverBlock.hasUniqueNormalSuccessor()) { + dominatorBlock = cachedReceiverBlock.getUniqueNormalSuccessor(); + } else { + assert false; + } + } else { + dominatorBlock = cachedReceiverBlock; + } + if (dominatorBlock != null && dominatorTree.dominatedBy(block, dominatorBlock)) { newReceiver = cachedReceiver; } } @@ -245,16 +260,18 @@ // We need to add this checkcast *before* the devirtualized invoke-virtual. assert it.peekPrevious() == devirtualizedInvoke; it.previous(); - // If the current block has catch handlers, split the new checkcast on its own block. - // Because checkcast is also a throwing instr, we should split before adding it. - // Otherwise, catch handlers are bound to a block with checkcast, not invoke IR. + + // If the current block has catch handlers, then split the block before adding the new + // check-cast instruction. The catch handlers are copied to the split block to ensure + // that all throwing instructions are covered by a catch-all catch handler in case of + // monitor instructions (see also b/174167294). BasicBlock blockWithDevirtualizedInvoke = - block.hasCatchHandlers() ? it.split(code, blocks) : block; + block.hasCatchHandlers() + ? it.splitCopyCatchHandlers(code, blocks, options) + : block; if (blockWithDevirtualizedInvoke != block) { // If we split, add the new checkcast at the end of the currently visiting block. - it = block.listIterator(code, block.getInstructions().size()); - it.previous(); - it.add(checkCast); + block.listIterator(code, block.getInstructions().size() - 1).add(checkCast); // Update the dominator tree after the split. dominatorTree = new DominatorTree(code); // Restore the cursor. @@ -354,7 +371,11 @@ if (newResolutionResult == null || newResolutionResult .isAccessibleForVirtualDispatchFrom(context, appView.appInfo()) - .isPossiblyFalse()) { + .isPossiblyFalse() + || !newResolutionResult + .getResolvedMethod() + .getAccessFlags() + .isAtLeastAsVisibleAs(resolutionResult.getResolvedMethod().getAccessFlags())) { return target; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java index b26bb34..96c9234 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -625,7 +625,7 @@ shouldSynthesizeMonitorEnterExit && !target.getDefinition().isStatic(); if (shouldSynthesizeNullCheckForReceiver && !isSynthesizingNullCheckForReceiverUsingMonitorEnter) { - synthesizeNullCheckForReceiver(appView, code); + synthesizeNullCheckForReceiver(appView, code, invoke); } // Insert monitor-enter and monitor-exit instructions if the method is synchronized. @@ -763,11 +763,12 @@ assert !initClassBlock.hasCatchHandlers(); InstructionListIterator iterator = initClassBlock.listIterator(code); - iterator.setInsertionPosition(entryBlock.exit().getPosition()); + iterator.setInsertionPosition(invoke.getPosition()); iterator.add(new InitClass(code.createValue(TypeElement.getInt()), target.getHolderType())); } - private void synthesizeNullCheckForReceiver(AppView<?> appView, IRCode code) { + private void synthesizeNullCheckForReceiver( + AppView<?> appView, IRCode code, InvokeMethod invoke) { List<Value> arguments = code.collectArguments(); if (!arguments.isEmpty()) { Value receiver = arguments.get(0); @@ -782,7 +783,7 @@ assert !throwBlock.hasCatchHandlers(); InstructionListIterator iterator = throwBlock.listIterator(code); - iterator.setInsertionPosition(entryBlock.exit().getPosition()); + iterator.setInsertionPosition(invoke.getPosition()); if (appView.options().canUseRequireNonNull()) { DexMethod requireNonNullMethod = appView.dexItemFactory().objectsMethods.requireNonNull; iterator.add(new InvokeStatic(requireNonNullMethod, null, ImmutableList.of(receiver)));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java index 8ba58f1..615e5ae 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -311,11 +311,10 @@ // Insert the definition of the replacement. replacement.setPosition(position); if (block.hasCatchHandlers()) { - BasicBlock splitBlock = iterator.split(code, blocks, false); - splitBlock.listIterator(code).add(replacement); - assert !block.hasCatchHandlers(); - assert splitBlock.hasCatchHandlers(); - block.copyCatchHandlers(code, blocks, splitBlock, appView.options()); + iterator + .splitCopyCatchHandlers(code, blocks, appView.options()) + .listIterator(code) + .add(replacement); } else { iterator.add(replacement); } @@ -413,11 +412,10 @@ // Insert the definition of the replacement. replacement.setPosition(position); if (block.hasCatchHandlers()) { - BasicBlock splitBlock = iterator.split(code, blocks, false); - splitBlock.listIterator(code).add(replacement); - assert !block.hasCatchHandlers(); - assert splitBlock.hasCatchHandlers(); - block.copyCatchHandlers(code, blocks, splitBlock, appView.options()); + iterator + .splitCopyCatchHandlers(code, blocks, appView.options()) + .listIterator(code) + .add(replacement); } else { iterator.add(replacement); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java index 3745120..cabff87 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadElimination.java
@@ -344,7 +344,7 @@ HorizontallyMergedClasses horizontallyMergedClasses = appView.horizontallyMergedClasses(); assert verticallyMergedClasses != null; assert horizontallyMergedClasses != null; - assert verticallyMergedClasses.isTarget(method.getHolderType()) + assert verticallyMergedClasses.isMergeTarget(method.getHolderType()) || horizontallyMergedClasses.isMergeTarget(method.getHolderType()); assert appView .dexItemFactory()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java index 48002cc..c681eaa 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/UninstantiatedTypeOptimization.java
@@ -29,6 +29,9 @@ import com.android.tools.r8.utils.MethodSignatureEquivalence; import com.android.tools.r8.utils.Timing; import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; +import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap; +import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap; import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.BiMap; import com.google.common.collect.ImmutableMap; @@ -54,14 +57,13 @@ private final Map<DexMethod, ArgumentInfoCollection> removedArgumentsInfoPerMethod; UninstantiatedTypeOptimizationGraphLens( - BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMap, + BidirectionalOneToOneMap<DexMethod, DexMethod> methodMap, Map<DexMethod, ArgumentInfoCollection> removedArgumentsInfoPerMethod, AppView<?> appView) { super( ImmutableMap.of(), - methodMap, - ImmutableMap.of(), - null, + methodMap.getForwardMap(), + new EmptyBidirectionalOneToOneMap<>(), methodMap.getInverseOneToOneMap(), appView.graphLens(), appView.dexItemFactory()); @@ -129,7 +131,7 @@ } Map<Wrapper<DexMethod>, Set<DexType>> changedVirtualMethods = new HashMap<>(); - BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping = + MutableBidirectionalOneToOneMap<DexMethod, DexMethod> methodMapping = new BidirectionalOneToOneHashMap<>(); Map<DexMethod, ArgumentInfoCollection> removedArgumentsInfoPerMethod = new IdentityHashMap<>(); @@ -140,7 +142,7 @@ processClass( clazz, changedVirtualMethods, - methodMapping.getForwardBacking(), + methodMapping.getForwardMap(), methodPoolCollection, removedArgumentsInfoPerMethod));
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java index 16fb558..ec1b904 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/UnusedArgumentsCollector.java
@@ -7,7 +7,6 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.ArgumentUse; 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.DexMethod; import com.android.tools.r8.graph.DexProgramClass; @@ -27,9 +26,10 @@ import com.android.tools.r8.utils.ThreadUtils; import com.android.tools.r8.utils.Timing; import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; +import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap; +import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap; import com.google.common.base.Equivalence.Wrapper; -import com.google.common.collect.BiMap; -import com.google.common.collect.ImmutableBiMap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Streams; import java.util.BitSet; @@ -48,7 +48,7 @@ private final AppView<AppInfoWithLiveness> appView; private final MethodPoolCollection methodPoolCollection; - private final BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping = + private final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> methodMapping = new BidirectionalOneToOneHashMap<>(); private final Map<DexMethod, ArgumentInfoCollection> removedArguments = new IdentityHashMap<>(); @@ -57,19 +57,15 @@ private final Map<DexMethod, ArgumentInfoCollection> removedArguments; UnusedArgumentsGraphLens( - Map<DexType, DexType> typeMap, Map<DexMethod, DexMethod> methodMap, - Map<DexField, DexField> fieldMap, - BiMap<DexField, DexField> originalFieldSignatures, - BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures, + BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures, GraphLens previousLens, DexItemFactory dexItemFactory, Map<DexMethod, ArgumentInfoCollection> removedArguments) { super( - typeMap, + ImmutableMap.of(), methodMap, - fieldMap, - originalFieldSignatures, + new EmptyBidirectionalOneToOneMap<>(), originalMethodSignatures, previousLens, dexItemFactory); @@ -108,10 +104,7 @@ if (!methodMapping.isEmpty()) { return new UnusedArgumentsGraphLens( - ImmutableMap.of(), - methodMapping, - ImmutableMap.of(), - ImmutableBiMap.of(), + methodMapping.getForwardMap(), methodMapping.getInverseOneToOneMap(), appView.graphLens(), appView.dexItemFactory(),
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java index 247e2e3..1b55f13 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
@@ -19,6 +19,35 @@ public class UtilityMethodsForCodeOptimizations { + public static UtilityMethodForCodeOptimizations synthesizeToStringIfNotNullMethod( + AppView<?> appView, ProgramMethod context, MethodProcessingId methodProcessingId) { + InternalOptions options = appView.options(); + if (options.isGeneratingClassFiles()) { + // TODO(b/172194277): Allow synthetics when generating CF. + return null; + } + DexItemFactory dexItemFactory = appView.dexItemFactory(); + DexProto proto = dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.objectType); + SyntheticItems syntheticItems = appView.getSyntheticItems(); + ProgramMethod syntheticMethod = + syntheticItems.createMethod( + context, + dexItemFactory, + builder -> + builder + .setProto(proto) + .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic()) + .setCode(method -> getToStringIfNotNullCodeTemplate(method, options)), + methodProcessingId); + return new UtilityMethodForCodeOptimizations(syntheticMethod); + } + + private static CfCode getToStringIfNotNullCodeTemplate( + DexMethod method, InternalOptions options) { + return CfUtilityMethodsForCodeOptimizations + .CfUtilityMethodsForCodeOptimizationsTemplates_toStringIfNotNull(options, method); + } + public static UtilityMethodForCodeOptimizations synthesizeThrowClassCastExceptionIfNotNullMethod( AppView<?> appView, ProgramMethod context, MethodProcessingId methodProcessingId) { InternalOptions options = appView.options();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java new file mode 100644 index 0000000..07471e7 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumDataMap.java
@@ -0,0 +1,109 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize.enums; + +import com.android.tools.r8.graph.DexField; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldKnownData; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import java.util.Set; + +public class EnumDataMap { + private final ImmutableMap<DexType, EnumData> map; + + public static EnumDataMap empty() { + return new EnumDataMap(ImmutableMap.of()); + } + + public EnumDataMap(ImmutableMap<DexType, EnumData> map) { + this.map = map; + } + + public boolean isUnboxedEnum(DexType type) { + return map.containsKey(type); + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + public Set<DexType> getUnboxedEnums() { + return map.keySet(); + } + + public EnumInstanceFieldKnownData getInstanceFieldData( + DexType enumType, DexField enumInstanceField) { + assert map.containsKey(enumType); + return map.get(enumType).getInstanceFieldData(enumInstanceField); + } + + public boolean hasUnboxedValueFor(DexField enumStaticField) { + return isUnboxedEnum(enumStaticField.holder) + && map.get(enumStaticField.holder).hasUnboxedValueFor(enumStaticField); + } + + public int getUnboxedValue(DexField enumStaticField) { + assert map.containsKey(enumStaticField.holder); + return map.get(enumStaticField.holder).getUnboxedValue(enumStaticField); + } + + public int getValuesSize(DexType enumType) { + assert map.containsKey(enumType); + return map.get(enumType).getValuesSize(); + } + + public boolean matchesValuesField(DexField staticField) { + assert map.containsKey(staticField.holder); + return map.get(staticField.holder).matchesValuesField(staticField); + } + + public static class EnumData { + static final int INVALID_VALUES_SIZE = -1; + + // Map each enum instance field to the list of field known data. + final ImmutableMap<DexField, EnumInstanceFieldKnownData> instanceFieldMap; + // Map each enum instance (static field) to the unboxed integer value. + final ImmutableMap<DexField, Integer> unboxedValues; + // Fields matching the $VALUES content and type, usually one. + final ImmutableSet<DexField> valuesFields; + // Size of the $VALUES field, if the valuesFields set is empty, set to INVALID_VALUES_SIZE. + final int valuesSize; + + public EnumData( + ImmutableMap<DexField, EnumInstanceFieldKnownData> instanceFieldMap, + ImmutableMap<DexField, Integer> unboxedValues, + ImmutableSet<DexField> valuesFields, + int valuesSize) { + this.instanceFieldMap = instanceFieldMap; + this.unboxedValues = unboxedValues; + this.valuesFields = valuesFields; + this.valuesSize = valuesSize; + } + + public EnumInstanceFieldKnownData getInstanceFieldData(DexField enumInstanceField) { + assert instanceFieldMap.containsKey(enumInstanceField); + return instanceFieldMap.get(enumInstanceField); + } + + public int getUnboxedValue(DexField field) { + assert unboxedValues.containsKey(field); + return unboxedValues.get(field); + } + + public boolean hasUnboxedValueFor(DexField field) { + return unboxedValues.get(field) != null; + } + + public boolean matchesValuesField(DexField field) { + return valuesFields.contains(field); + } + + public int getValuesSize() { + assert valuesSize != INVALID_VALUES_SIZE; + return valuesSize; + } + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInstanceFieldData.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInstanceFieldData.java index 4f4dea9..69a5179 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInstanceFieldData.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInstanceFieldData.java
@@ -4,9 +4,9 @@ package com.android.tools.r8.ir.optimize.enums; -import com.android.tools.r8.graph.DexField; import com.android.tools.r8.ir.analysis.value.AbstractValue; -import java.util.Map; +import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap; +import java.util.function.BiConsumer; /* * My instances represent the values of an enum field for each of the enum instance. @@ -83,9 +83,9 @@ } public static class EnumInstanceFieldMappingData extends EnumInstanceFieldKnownData { - private final Map<DexField, AbstractValue> mapping; + private final ImmutableInt2ReferenceSortedMap<AbstractValue> mapping; - public EnumInstanceFieldMappingData(Map<DexField, AbstractValue> mapping) { + public EnumInstanceFieldMappingData(ImmutableInt2ReferenceSortedMap<AbstractValue> mapping) { this.mapping = mapping; } @@ -104,8 +104,12 @@ return this; } - public AbstractValue getData(DexField field) { - return mapping.get(field); + public AbstractValue getData(int unboxedEnumValue) { + return mapping.get(unboxedEnumValue); + } + + public void forEach(BiConsumer<? super Integer, ? super AbstractValue> consumer) { + mapping.forEach(consumer); } } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInstanceFieldDataMap.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInstanceFieldDataMap.java deleted file mode 100644 index f02e488..0000000 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumInstanceFieldDataMap.java +++ /dev/null
@@ -1,27 +0,0 @@ -// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -package com.android.tools.r8.ir.optimize.enums; - -import com.android.tools.r8.graph.DexField; -import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldKnownData; -import com.google.common.collect.ImmutableMap; - -public class EnumInstanceFieldDataMap { - private final ImmutableMap<DexType, ImmutableMap<DexField, EnumInstanceFieldKnownData>> - instanceFieldMap; - - public EnumInstanceFieldDataMap( - ImmutableMap<DexType, ImmutableMap<DexField, EnumInstanceFieldKnownData>> instanceFieldMap) { - this.instanceFieldMap = instanceFieldMap; - } - - public EnumInstanceFieldKnownData getInstanceFieldData( - DexType enumType, DexField enumInstanceField) { - assert instanceFieldMap.containsKey(enumType); - assert instanceFieldMap.get(enumType).containsKey(enumInstanceField); - return instanceFieldMap.get(enumType).get(enumInstanceField); - } -}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java index 2d46582..1ce0df3 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxer.java
@@ -20,7 +20,6 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.DirectMappedDexApplication; -import com.android.tools.r8.graph.EnumValueInfoMapCollection; import com.android.tools.r8.graph.FieldResolutionResult; import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.GraphLens.NestedGraphLens; @@ -28,10 +27,13 @@ import com.android.tools.r8.graph.ProgramPackageCollection; import com.android.tools.r8.graph.ResolutionResult; import com.android.tools.r8.graph.UseRegistry; +import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues; +import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues.EnumStaticFieldValues; import com.android.tools.r8.ir.analysis.type.ArrayTypeElement; 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.AbstractValue; +import com.android.tools.r8.ir.analysis.value.EnumValuesObjectState; import com.android.tools.r8.ir.analysis.value.ObjectState; import com.android.tools.r8.ir.code.ArrayPut; import com.android.tools.r8.ir.code.BasicBlock; @@ -51,6 +53,7 @@ import com.android.tools.r8.ir.conversion.IRConverter; import com.android.tools.r8.ir.conversion.PostMethodProcessor; import com.android.tools.r8.ir.optimize.Inliner.Constraint; +import com.android.tools.r8.ir.optimize.enums.EnumDataMap.EnumData; import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldKnownData; import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldMappingData; import com.android.tools.r8.ir.optimize.enums.EnumInstanceFieldData.EnumInstanceFieldOrdinalData; @@ -64,13 +67,16 @@ import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.Reporter; import com.android.tools.r8.utils.StringDiagnostic; +import com.android.tools.r8.utils.collections.ImmutableInt2ReferenceSortedMap; import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; +import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap; +import it.unimi.dsi.fastutil.ints.Int2ReferenceMap; import java.util.Arrays; -import java.util.IdentityHashMap; import java.util.Map; +import java.util.OptionalInt; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; @@ -86,6 +92,10 @@ private final EnumUnboxingCandidateInfoCollection enumUnboxingCandidatesInfo; private final ProgramPackageCollection enumsToUnboxWithPackageRequirement = ProgramPackageCollection.createEmpty(); + private final Map<DexType, EnumStaticFieldValues> staticFieldValuesMap = + new ConcurrentHashMap<>(); + + private final DexEncodedField ordinalField; private EnumUnboxingRewriter enumUnboxerRewriter; @@ -104,6 +114,18 @@ } assert !appView.options().debug; enumUnboxingCandidatesInfo = new EnumUnboxingCandidateAnalysis(appView, this).findCandidates(); + + ordinalField = + appView.appInfo().resolveField(factory.enumMembers.ordinalField).getResolvedField(); + if (ordinalField == null) { + // This can happen when compiling for non standard libraries, in that case, this effectively + // disables the enum unboxer. + enumUnboxingCandidatesInfo.clear(); + } + } + + public static int ordinalToUnboxedInt(int ordinal) { + return ordinal + 1; } private void markEnumAsUnboxable(Reason reason, DexProgramClass enumClass) { @@ -351,7 +373,7 @@ ExecutorService executorService, OptimizationFeedbackDelayed feedback) throws ExecutionException { - EnumInstanceFieldDataMap enumInstanceFieldDataMap = finishAnalysis(); + EnumDataMap enumDataMap = finishAnalysis(); // At this point the enum unboxing candidates are no longer candidates, they will all be // unboxed. We extract the now immutable enums to unbox information and clear the candidate // info. @@ -371,13 +393,12 @@ .synthesizeEnumUnboxingUtilityClasses( enumClassesToUnbox, enumsToUnboxWithPackageRequirement, appBuilder) .build(); - enumUnboxerRewriter = - new EnumUnboxingRewriter(appView, enumsToUnbox, enumInstanceFieldDataMap, relocator); + enumUnboxerRewriter = new EnumUnboxingRewriter(appView, enumDataMap, relocator); NestedGraphLens enumUnboxingLens = new EnumUnboxingTreeFixer(appView, enumsToUnbox, relocator, enumUnboxerRewriter) .fixupTypeReferences(); enumUnboxerRewriter.setEnumUnboxingLens(enumUnboxingLens); - appView.setUnboxedEnums(enumUnboxerRewriter.getEnumsToUnbox()); + appView.setUnboxedEnums(enumDataMap); GraphLens previousLens = appView.graphLens(); appView.rewriteWithLensAndApplication(enumUnboxingLens, appBuilder.build()); updateOptimizationInfos(executorService, feedback); @@ -426,34 +447,159 @@ keepInfo.mutate(mutator -> mutator.removeKeepInfoForPrunedItems(enumsToUnbox)); } - public EnumInstanceFieldDataMap finishAnalysis() { + public EnumDataMap finishAnalysis() { analyzeInitializers(); analyzeAccessibility(); - EnumInstanceFieldDataMap enumInstanceFieldDataMap = analyzeFields(); + EnumDataMap enumDataMap = analyzeEnumInstances(); + assert enumDataMap.getUnboxedEnums().size() == enumUnboxingCandidatesInfo.candidates().size(); if (debugLogEnabled) { reportEnumsAnalysis(); } - return enumInstanceFieldDataMap; + return enumDataMap; } - private EnumInstanceFieldDataMap analyzeFields() { - ImmutableMap.Builder<DexType, ImmutableMap<DexField, EnumInstanceFieldKnownData>> builder = - ImmutableMap.builder(); + private EnumDataMap analyzeEnumInstances() { + ImmutableMap.Builder<DexType, EnumData> builder = ImmutableMap.builder(); enumUnboxingCandidatesInfo.forEachCandidateAndRequiredInstanceFieldData( (enumClass, fields) -> { - ImmutableMap.Builder<DexField, EnumInstanceFieldKnownData> typeBuilder = - ImmutableMap.builder(); - for (DexField field : fields) { - EnumInstanceFieldData enumInstanceFieldData = computeEnumFieldData(field, enumClass); - if (enumInstanceFieldData.isUnknown()) { - markEnumAsUnboxable(Reason.MISSING_INSTANCE_FIELD_DATA, enumClass); - return; - } - typeBuilder.put(field, enumInstanceFieldData.asEnumFieldKnownData()); + EnumData data = buildData(enumClass, fields); + if (data == null) { + markEnumAsUnboxable(Reason.MISSING_INSTANCE_FIELD_DATA, enumClass); + return; } - builder.put(enumClass.type, typeBuilder.build()); + builder.put(enumClass.type, data); }); - return new EnumInstanceFieldDataMap(builder.build()); + staticFieldValuesMap.clear(); + return new EnumDataMap(builder.build()); + } + + private EnumData buildData(DexProgramClass enumClass, Set<DexField> fields) { + // This map holds all the accessible fields to their unboxed value, so we can remap the field + // read to the unboxed value. + ImmutableMap.Builder<DexField, Integer> unboxedValues = ImmutableMap.builder(); + // 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); + + // Step 1: We iterate over the field to find direct enum instance information and the values + // fields. + for (DexEncodedField staticField : enumClass.staticFields()) { + if (EnumUnboxingCandidateAnalysis.isEnumField(staticField, enumClass.type)) { + ObjectState enumState = + enumStaticFieldValues.getObjectStateForPossiblyPinnedField(staticField.field); + if (enumState != null) { + OptionalInt optionalOrdinal = getOrdinal(enumState); + if (!optionalOrdinal.isPresent()) { + return null; + } + int ordinal = optionalOrdinal.getAsInt(); + unboxedValues.put(staticField.field, ordinalToUnboxedInt(ordinal)); + ordinalToObjectState.put(ordinal, enumState); + } + } else if (EnumUnboxingCandidateAnalysis.matchesValuesField( + staticField, enumClass.type, factory)) { + AbstractValue valuesValue = + enumStaticFieldValues.getValuesAbstractValueForPossiblyPinnedField(staticField.field); + if (valuesValue == null || valuesValue.isZero()) { + // Unused field + continue; + } + if (valuesValue.isUnknown()) { + return null; + } + assert valuesValue.isSingleFieldValue(); + ObjectState valuesState = valuesValue.asSingleFieldValue().getState(); + if (!valuesState.isEnumValuesObjectState()) { + return null; + } + assert valuesContents == null + || valuesContents.equals(valuesState.asEnumValuesObjectState()); + valuesContents = valuesState.asEnumValuesObjectState(); + valuesField.add(staticField.field); + } + } + + // Step 2: We complete the information based on the values content, since some enum instances + // may be reachable only though the $VALUES field. + if (valuesContents != null) { + for (int ordinal = 0; ordinal < valuesContents.getEnumValuesSize(); ordinal++) { + if (!ordinalToObjectState.containsKey(ordinal)) { + ObjectState enumState = valuesContents.getObjectStateForOrdinal(ordinal); + if (enumState.isEmpty()) { + // If $VALUES is used, we need data for all enums, at least the ordinal. + return null; + } + assert getOrdinal(enumState).isPresent(); + assert getOrdinal(enumState).getAsInt() == ordinal; + ordinalToObjectState.put(ordinal, enumState); + } + } + } + + // The ordinalToObjectState map may have holes at this point, if some enum instances are never + // used ($VALUES unused or removed, and enum instance field unused or removed), it contains + // only data for reachable enum instance, that is what we're interested in. + ImmutableMap.Builder<DexField, EnumInstanceFieldKnownData> instanceFieldBuilder = + ImmutableMap.builder(); + for (DexField instanceField : fields) { + EnumInstanceFieldData fieldData = + computeEnumFieldData(instanceField, enumClass, ordinalToObjectState); + if (fieldData.isUnknown()) { + return null; + } + instanceFieldBuilder.put(instanceField, fieldData.asEnumFieldKnownData()); + } + + return new EnumData( + instanceFieldBuilder.build(), + unboxedValues.build(), + valuesField.build(), + valuesContents == null ? EnumData.INVALID_VALUES_SIZE : valuesContents.getEnumValuesSize()); + } + + private EnumInstanceFieldData computeEnumFieldData( + DexField instanceField, + DexProgramClass enumClass, + Int2ReferenceMap<ObjectState> ordinalToObjectState) { + DexEncodedField encodedInstanceField = + appView.appInfo().resolveFieldOn(enumClass, instanceField).getResolvedField(); + assert encodedInstanceField != null; + boolean canBeOrdinal = instanceField.type.isIntType(); + ImmutableInt2ReferenceSortedMap.Builder<AbstractValue> data = + ImmutableInt2ReferenceSortedMap.builder(); + for (Integer ordinal : ordinalToObjectState.keySet()) { + ObjectState state = ordinalToObjectState.get(ordinal); + AbstractValue fieldValue = state.getAbstractFieldValue(encodedInstanceField); + if (!(fieldValue.isSingleNumberValue() || fieldValue.isSingleStringValue())) { + return EnumInstanceFieldUnknownData.getInstance(); + } + data.put(ordinalToUnboxedInt(ordinal), fieldValue); + if (canBeOrdinal) { + assert fieldValue.isSingleNumberValue(); + int computedValue = fieldValue.asSingleNumberValue().getIntValue(); + if (computedValue != ordinal) { + canBeOrdinal = false; + } + } + } + if (canBeOrdinal) { + return new EnumInstanceFieldOrdinalData(); + } + return new EnumInstanceFieldMappingData(data.build()); + } + + private OptionalInt getOrdinal(ObjectState state) { + AbstractValue field = state.getAbstractFieldValue(ordinalField); + if (field.isSingleNumberValue()) { + return OptionalInt.of(field.asSingleNumberValue().getIntValue()); + } + return OptionalInt.empty(); } private void analyzeAccessibility() { @@ -494,6 +640,17 @@ return useRegistry.computeConstraint(method.asProgramMethod(appView)); } + public void recordEnumState(DexProgramClass clazz, StaticFieldValues staticFieldValues) { + if (staticFieldValues == null || !staticFieldValues.isEnumStaticFieldValues()) { + return; + } + assert clazz.isEnum(); + EnumStaticFieldValues enumStaticFieldValues = staticFieldValues.asEnumStaticFieldValues(); + if (getEnumUnboxingCandidateOrNull(clazz.type) != null) { + staticFieldValuesMap.put(clazz.type, enumStaticFieldValues); + } + } + private class EnumAccessibilityUseRegistry extends UseRegistry { private ProgramMethod context; @@ -929,89 +1086,6 @@ return Reason.OTHER_UNSUPPORTED_INSTRUCTION; } - private EnumInstanceFieldData computeEnumFieldData( - DexField instanceField, DexProgramClass enumClass) { - DexEncodedField encodedInstanceField = - appView.appInfo().resolveFieldOn(enumClass, instanceField).getResolvedField(); - assert encodedInstanceField != null; - boolean canBeOrdinal = instanceField.type.isIntType(); - Map<DexField, AbstractValue> data = new IdentityHashMap<>(); - EnumValueInfoMapCollection.EnumValueInfoMap enumValueInfoMap = - appView.appInfo().getEnumValueInfoMap(enumClass.type); - for (DexField staticField : enumValueInfoMap.enumValues()) { - ObjectState enumInstanceState = - computeEnumInstanceObjectState(enumClass, staticField, enumValueInfoMap); - if (enumInstanceState == null) { - // The enum instance is effectively unused. No need to generate anything for it, the path - // will never be taken. - } else { - AbstractValue fieldValue = enumInstanceState.getAbstractFieldValue(encodedInstanceField); - if (!(fieldValue.isSingleNumberValue() || fieldValue.isSingleStringValue())) { - return EnumInstanceFieldUnknownData.getInstance(); - } - data.put(staticField, fieldValue); - if (canBeOrdinal) { - int ordinalValue = enumValueInfoMap.getEnumValueInfo(staticField).ordinal; - assert fieldValue.isSingleNumberValue(); - int computedValue = fieldValue.asSingleNumberValue().getIntValue(); - if (computedValue != ordinalValue) { - canBeOrdinal = false; - } - } - } - } - if (canBeOrdinal) { - return new EnumInstanceFieldOrdinalData(); - } - return new EnumInstanceFieldMappingData(data); - } - - // We need to access the enum instance object state to figure out if it contains known constant - // field values. The enum instance may be accessed in two ways, directly through the enum - // static field, or through the enum $VALUES field. If none of them are kept, the instance is - // effectively unused. The object state may be stored in the enum static field optimization - // info, if kept, or in the $VALUES optimization info, if kept. - // If the enum instance is unused, this method answers null. - private ObjectState computeEnumInstanceObjectState( - DexProgramClass enumClass, - DexField staticField, - EnumValueInfoMapCollection.EnumValueInfoMap enumValueInfoMap) { - // Attempt 1: Get object state from the instance field's optimization info. - DexEncodedField encodedStaticField = enumClass.lookupStaticField(staticField); - AbstractValue enumInstanceValue = encodedStaticField.getOptimizationInfo().getAbstractValue(); - if (enumInstanceValue.isSingleFieldValue()) { - return enumInstanceValue.asSingleFieldValue().getState(); - } - if (enumInstanceValue.isUnknown()) { - return ObjectState.empty(); - } - assert enumInstanceValue.isZero(); - - // Attempt 2: Get object state from the values field's optimization info. - DexEncodedField valuesField = - enumClass.lookupStaticField( - factory.createField( - enumClass.type, - factory.createArrayType(1, enumClass.type), - factory.enumValuesFieldName)); - AbstractValue valuesValue = valuesField.getOptimizationInfo().getAbstractValue(); - if (valuesValue.isZero()) { - // Unused enum instance. - return null; - } - if (valuesValue.isUnknown()) { - return ObjectState.empty(); - } - assert valuesValue.isSingleFieldValue(); - ObjectState valuesState = valuesValue.asSingleFieldValue().getState(); - if (valuesState.isEnumValuesObjectState()) { - return valuesState - .asEnumValuesObjectState() - .getObjectStateForOrdinal(enumValueInfoMap.getEnumValueInfo(staticField).ordinal); - } - return ObjectState.empty(); - } - private void reportEnumsAnalysis() { assert debugLogEnabled; Reporter reporter = appView.options().reporter; @@ -1081,7 +1155,6 @@ VALUES_INVOKE, COMPARE_TO_INVOKE, UNSUPPORTED_LIBRARY_CALL, - MISSING_INFO_MAP, MISSING_INSTANCE_FIELD_DATA, INVALID_FIELD_READ, INVALID_FIELD_PUT,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java index b4bd339..40b0c86 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingCandidateAnalysis.java
@@ -12,7 +12,6 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexProto; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap; import com.android.tools.r8.ir.optimize.enums.EnumUnboxer.Reason; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.shaking.KeepInfoCollection; @@ -54,7 +53,7 @@ if (!clazz.isEnum()) { return false; } - if (!clazz.isEffectivelyFinal(appView)) { + if (clazz.superType != factory.enumType || !clazz.isEffectivelyFinal(appView)) { enumUnboxer.reportFailure(clazz.type, Reason.SUBTYPES); return false; } @@ -66,12 +65,6 @@ enumUnboxer.reportFailure(clazz.type, Reason.UNEXPECTED_STATIC_FIELD); return false; } - EnumValueInfoMap enumValueInfoMap = - appView.appInfo().withLiveness().getEnumValueInfoMap(clazz.type); - if (enumValueInfoMap == null) { - enumUnboxer.reportFailure(clazz.type, Reason.MISSING_INFO_MAP); - return false; - } return true; } @@ -79,15 +72,9 @@ // instances. private boolean enumHasBasicStaticFields(DexProgramClass clazz) { for (DexEncodedField staticField : clazz.staticFields()) { - if (staticField.field.type == clazz.type - && staticField.accessFlags.isEnum() - && staticField.accessFlags.isFinal()) { + if (isEnumField(staticField, clazz.type)) { // Enum field, valid, do nothing. - } else if (staticField.field.type.isArrayType() - && staticField.field.type.toArrayElementType(factory) == clazz.type - && staticField.accessFlags.isSynthetic() - && staticField.accessFlags.isFinal() - && staticField.field.name == factory.enumValuesFieldName) { + } else if (matchesValuesField(staticField, clazz.type, factory)) { // Field $VALUES, valid, do nothing. } else if (appView.appInfo().isFieldRead(staticField)) { // Only non read static fields are valid, and they are assumed unused. @@ -97,6 +84,21 @@ return true; } + static boolean isEnumField(DexEncodedField staticField, DexType enumType) { + return staticField.field.type == enumType + && staticField.accessFlags.isEnum() + && staticField.accessFlags.isFinal(); + } + + static boolean matchesValuesField( + DexEncodedField staticField, DexType enumType, DexItemFactory factory) { + return staticField.field.type.isArrayType() + && staticField.field.type.toArrayElementType(factory) == enumType + && staticField.accessFlags.isSynthetic() + && staticField.accessFlags.isFinal() + && staticField.field.name == factory.enumValuesFieldName; + } + private void removeEnumsInAnnotations() { for (DexProgramClass clazz : appView.appInfo().classes()) { if (clazz.isAnnotation()) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java index b6660fc..f230dda 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -13,8 +13,8 @@ import com.android.tools.r8.ir.code.Invoke; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; +import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import java.util.IdentityHashMap; @@ -29,9 +29,8 @@ EnumUnboxingLens( Map<DexType, DexType> typeMap, Map<DexMethod, DexMethod> methodMap, - Map<DexField, DexField> fieldMap, - BiMap<DexField, DexField> originalFieldSignatures, - BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures, + BidirectionalOneToOneMap<DexField, DexField> fieldMap, + BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures, GraphLens previousLens, DexItemFactory dexItemFactory, Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod, @@ -40,7 +39,6 @@ typeMap, methodMap, fieldMap, - originalFieldSignatures, originalMethodSignatures, previousLens, dexItemFactory); @@ -76,8 +74,9 @@ static class Builder { protected final Map<DexType, DexType> typeMap = new IdentityHashMap<>(); - protected final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create(); - protected final BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures = + protected final MutableBidirectionalOneToOneMap<DexField, DexField> newFieldSignatures = + new BidirectionalOneToOneHashMap<>(); + protected final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures = new BidirectionalOneToOneHashMap<>(); private Map<DexMethod, RewrittenPrototypeDescription> prototypeChangesPerMethod = @@ -94,7 +93,7 @@ if (from == to) { return; } - originalFieldSignatures.put(to, from); + newFieldSignatures.put(from, to); } public void move(DexMethod from, DexMethod to, boolean fromStatic, boolean toStatic) { @@ -143,16 +142,13 @@ public EnumUnboxingLens build( DexItemFactory dexItemFactory, GraphLens previousLens, Set<DexType> unboxedEnums) { - if (typeMap.isEmpty() - && originalFieldSignatures.isEmpty() - && originalMethodSignatures.isEmpty()) { + if (typeMap.isEmpty() && newFieldSignatures.isEmpty() && originalMethodSignatures.isEmpty()) { return null; } return new EnumUnboxingLens( typeMap, - originalMethodSignatures.getInverseOneToOneMap(), - originalFieldSignatures.inverse(), - originalFieldSignatures, + originalMethodSignatures.getInverseOneToOneMap().getForwardMap(), + newFieldSignatures, originalMethodSignatures, previousLens, dexItemFactory,
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 c456bd0..6c20c4f 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
@@ -21,9 +21,6 @@ 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.EnumValueInfoMapCollection; -import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfo; -import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap; import com.android.tools.r8.graph.FieldAccessFlags; import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature; import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature; @@ -76,8 +73,7 @@ private final AppView<AppInfoWithLiveness> appView; private final DexItemFactory factory; - private final EnumValueInfoMapCollection enumsToUnbox; - private final EnumInstanceFieldDataMap unboxedEnumsInstanceFieldData; + private final EnumDataMap unboxedEnumsData; private final UnboxedEnumMemberRelocator relocator; private NestedGraphLens enumUnboxingLens; @@ -93,18 +89,11 @@ EnumUnboxingRewriter( AppView<AppInfoWithLiveness> appView, - Set<DexType> enumsToUnbox, - EnumInstanceFieldDataMap unboxedEnumsInstanceFieldData, + EnumDataMap unboxedEnumsInstanceFieldData, UnboxedEnumMemberRelocator relocator) { this.appView = appView; this.factory = appView.dexItemFactory(); - EnumValueInfoMapCollection.Builder builder = EnumValueInfoMapCollection.builder(); - for (DexType toUnbox : enumsToUnbox) { - assert appView.appInfo().withLiveness().getEnumValueInfoMap(toUnbox) != null; - builder.put(toUnbox, appView.appInfo().withLiveness().getEnumValueInfoMap(toUnbox)); - } - this.enumsToUnbox = builder.build(); - this.unboxedEnumsInstanceFieldData = unboxedEnumsInstanceFieldData; + this.unboxedEnumsData = unboxedEnumsInstanceFieldData; this.relocator = relocator; // Custom methods for java.lang.Enum methods ordinal, equals and compareTo. @@ -147,14 +136,10 @@ this.enumUnboxingLens = enumUnboxingLens; } - public EnumValueInfoMapCollection getEnumsToUnbox() { - return enumsToUnbox; - } - Set<Phi> rewriteCode(IRCode code) { // We should not process the enum methods, they will be removed and they may contain invalid // rewriting rules. - if (enumsToUnbox.isEmpty()) { + if (unboxedEnumsData.isEmpty()) { return Sets.newIdentityHashSet(); } assert code.isConsistentSSABeforeTypesAreCorrect(); @@ -223,7 +208,7 @@ && invokeStatic.getArgument(0).isConstClass()) { DexType enumType = invokeStatic.getArgument(0).getConstInstruction().asConstClass().getValue(); - if (enumsToUnbox.containsEnum(enumType)) { + if (unboxedEnumsData.isUnboxedEnum(enumType)) { DexMethod valueOfMethod = computeValueOfUtilityMethod(enumType); Value outValue = invokeStatic.outValue(); Value rewrittenOutValue = null; @@ -282,17 +267,15 @@ } if (instruction.isStaticGet()) { StaticGet staticGet = instruction.asStaticGet(); - DexType holder = staticGet.getField().holder; - if (enumsToUnbox.containsEnum(holder)) { + DexField field = staticGet.getField(); + DexType holder = field.holder; + if (unboxedEnumsData.isUnboxedEnum(holder)) { if (staticGet.outValue() == null) { iterator.removeOrReplaceByDebugLocalRead(); continue; } - EnumValueInfoMap enumValueInfoMap = enumsToUnbox.getEnumValueInfoMap(holder); - assert enumValueInfoMap != null; affectedPhis.addAll(staticGet.outValue().uniquePhiUsers()); - EnumValueInfo enumValueInfo = enumValueInfoMap.getEnumValueInfo(staticGet.getField()); - if (enumValueInfo == null && staticGet.getField().name == factory.enumValuesFieldName) { + if (unboxedEnumsData.matchesValuesField(field)) { utilityMethods.computeIfAbsent( valuesUtilityMethod, m -> synthesizeValuesUtilityMethod()); DexField fieldValues = createValuesField(holder); @@ -300,7 +283,9 @@ DexMethod methodValues = createValuesMethod(holder); utilityMethods.computeIfAbsent( methodValues, - m -> computeValuesEncodedMethod(m, fieldValues, enumValueInfoMap.size())); + m -> + computeValuesEncodedMethod( + m, fieldValues, unboxedEnumsData.getValuesSize(holder))); Value rewrittenOutValue = code.createValue( ArrayTypeElement.create(TypeElement.getInt(), definitelyNotNull())); @@ -310,9 +295,10 @@ convertedEnums.put(invoke, holder); } else { // Replace by ordinal + 1 for null check (null is 0). - assert enumValueInfo != null - : "Invalid read to " + staticGet.getField().name + ", error during enum analysis"; - ConstNumber intConstant = code.createIntConstant(enumValueInfo.convertToInt()); + assert unboxedEnumsData.hasUnboxedValueFor(field) + : "Invalid read to " + field.name + ", error during enum analysis"; + ConstNumber intConstant = + code.createIntConstant(unboxedEnumsData.getUnboxedValue(field)); iterator.replaceCurrentInstruction(intConstant); convertedEnums.put(intConstant, holder); } @@ -322,7 +308,7 @@ if (instruction.isInstanceGet()) { InstanceGet instanceGet = instruction.asInstanceGet(); DexType holder = instanceGet.getField().holder; - if (enumsToUnbox.containsEnum(holder)) { + if (unboxedEnumsData.isUnboxedEnum(holder)) { DexMethod fieldMethod = computeInstanceFieldMethod(instanceGet.getField()); Value rewrittenOutValue = code.createValue( @@ -332,7 +318,7 @@ new InvokeStatic( fieldMethod, rewrittenOutValue, ImmutableList.of(instanceGet.object())); iterator.replaceCurrentInstruction(invoke); - if (enumsToUnbox.containsEnum(instanceGet.getField().type)) { + if (unboxedEnumsData.isUnboxedEnum(instanceGet.getField().type)) { convertedEnums.put(invoke, instanceGet.getField().type); } } @@ -393,7 +379,7 @@ private DexMethod computeInstanceFieldMethod(DexField field) { EnumInstanceFieldKnownData enumFieldKnownData = - unboxedEnumsInstanceFieldData.getInstanceFieldData(field.holder, field); + unboxedEnumsData.getInstanceFieldData(field.holder, field); if (enumFieldKnownData.isOrdinal()) { utilityMethods.computeIfAbsent(ordinalUtilityMethod, m -> synthesizeOrdinalMethod()); return ordinalUtilityMethod; @@ -437,7 +423,7 @@ return null; } DexType enumType = type.asClassType().getClassType(); - return enumsToUnbox.containsEnum(enumType) ? enumType : null; + return unboxedEnumsData.isUnboxedEnum(enumType) ? enumType : null; } public String compatibleName(DexType type) { @@ -478,7 +464,7 @@ } private DexMethod computeInstanceFieldUtilityMethod(DexType enumType, DexField field) { - assert enumsToUnbox.containsEnum(enumType); + assert unboxedEnumsData.isUnboxedEnum(enumType); assert field.holder == enumType || field.holder == factory.enumType; String methodName = "get" @@ -498,7 +484,7 @@ private DexMethod computeStringValueOfUtilityMethod(DexType enumType) { // TODO(b/167994636): remove duplication between instance field name read and this method. - assert enumsToUnbox.containsEnum(enumType); + assert unboxedEnumsData.isUnboxedEnum(enumType); String methodName = "string$valueOf$" + compatibleName(enumType); DexMethod fieldMethod = factory.createMethod( @@ -514,7 +500,7 @@ } private DexMethod computeValueOfUtilityMethod(DexType enumType) { - assert enumsToUnbox.containsEnum(enumType); + assert unboxedEnumsData.isUnboxedEnum(enumType); DexMethod valueOf = factory.createMethod( relocator.getNewMemberLocationFor(enumType), @@ -538,7 +524,7 @@ return null; } DexType classType = baseType.asClassType().getClassType(); - return enumsToUnbox.containsEnum(classType) ? classType : null; + return unboxedEnumsData.isUnboxedEnum(classType) ? classType : null; } void synthesizeEnumUnboxingUtilityMethods(IRConverter converter, ExecutorService executorService) @@ -593,16 +579,13 @@ private DexEncodedMethod synthesizeInstanceFieldMethod( DexMethod method, DexType enumType, DexField field, AbstractValue nullValue) { assert method.proto.returnType == field.type; - assert unboxedEnumsInstanceFieldData.getInstanceFieldData(enumType, field).isMapping(); + assert unboxedEnumsData.getInstanceFieldData(enumType, field).isMapping(); CfCode cfCode = new EnumUnboxingCfCodeProvider.EnumUnboxingInstanceFieldCfCodeProvider( appView, method.holder, field.type, - enumsToUnbox.getEnumValueInfoMap(enumType), - unboxedEnumsInstanceFieldData - .getInstanceFieldData(enumType, field) - .asEnumFieldMappingData(), + unboxedEnumsData.getInstanceFieldData(enumType, field).asEnumFieldMappingData(), nullValue) .generateCfCode(); return synthesizeUtilityMethod(cfCode, method, false); @@ -610,7 +593,7 @@ private DexEncodedMethod synthesizeValueOfUtilityMethod(DexMethod method, DexType enumType) { assert method.proto.returnType == factory.intType; - assert unboxedEnumsInstanceFieldData + assert unboxedEnumsData .getInstanceFieldData(enumType, factory.enumMembers.nameField) .isMapping(); CfCode cfCode = @@ -618,8 +601,7 @@ appView, method.holder, enumType, - enumsToUnbox.getEnumValueInfoMap(enumType), - unboxedEnumsInstanceFieldData + unboxedEnumsData .getInstanceFieldData(enumType, factory.enumMembers.nameField) .asEnumFieldMappingData()) .generateCfCode();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueInfoMapCollector.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueInfoMapCollector.java deleted file mode 100644 index c94be2e..0000000 --- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueInfoMapCollector.java +++ /dev/null
@@ -1,94 +0,0 @@ -// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. -package com.android.tools.r8.ir.optimize.enums; - -import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.DexField; -import com.android.tools.r8.graph.DexProgramClass; -import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.EnumValueInfoMapCollection; -import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfo; -import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap; -import com.android.tools.r8.graph.ProgramMethod; -import com.android.tools.r8.ir.code.IRCode; -import com.android.tools.r8.ir.code.Instruction; -import com.android.tools.r8.ir.code.InvokeDirect; -import com.android.tools.r8.ir.code.StaticPut; -import com.android.tools.r8.shaking.AppInfoWithLiveness; -import java.util.LinkedHashMap; - -/** - * Extracts the ordinal values and any anonymous subtypes for all Enum classes from their static - * initializer. - * - * <p>An Enum class has a field for each value. In the class initializer, each field is initialized - * to a singleton object that represents the value. This code matches on the corresponding call to - * the constructor (instance initializer) and extracts the value of the second argument, which is - * the ordinal and the holder which is the concrete type. - */ -public class EnumValueInfoMapCollector { - - private final AppView<AppInfoWithLiveness> appView; - - private final EnumValueInfoMapCollection.Builder valueInfoMapsBuilder = - EnumValueInfoMapCollection.builder(); - - public EnumValueInfoMapCollector(AppView<AppInfoWithLiveness> appView) { - this.appView = appView; - } - - public AppInfoWithLiveness run() { - for (DexProgramClass clazz : appView.appInfo().classes()) { - processClasses(clazz); - } - EnumValueInfoMapCollection valueInfoMaps = valueInfoMapsBuilder.build(); - if (!valueInfoMaps.isEmpty()) { - return appView.appInfo().withEnumValueInfoMaps(valueInfoMaps); - } - return appView.appInfo(); - } - - private void processClasses(DexProgramClass clazz) { - // Enum classes are flagged as such. Also, for library classes, the ordinals are not known. - if (!clazz.accessFlags.isEnum() || clazz.isNotProgramClass() || !clazz.hasClassInitializer()) { - return; - } - ProgramMethod initializer = clazz.getProgramClassInitializer(); - IRCode code = initializer.buildIR(appView); - LinkedHashMap<DexField, EnumValueInfo> enumValueInfoMap = new LinkedHashMap<>(); - for (StaticPut staticPut : code.<StaticPut>instructions(Instruction::isStaticPut)) { - if (staticPut.getField().type != clazz.type) { - continue; - } - Instruction newInstance = staticPut.value().definition; - if (newInstance == null || !newInstance.isNewInstance()) { - continue; - } - Instruction ordinal = null; - DexType type = null; - for (Instruction ctorCall : newInstance.outValue().uniqueUsers()) { - if (!ctorCall.isInvokeDirect()) { - continue; - } - InvokeDirect invoke = ctorCall.asInvokeDirect(); - if (!appView.dexItemFactory().isConstructor(invoke.getInvokedMethod()) - || invoke.arguments().size() < 3) { - continue; - } - ordinal = invoke.arguments().get(2).definition; - type = invoke.getInvokedMethod().holder; - break; - } - if (ordinal == null || !ordinal.isConstNumber() || type == null) { - return; - } - - EnumValueInfo info = new EnumValueInfo(type, ordinal.asConstNumber().getIntValue()); - if (enumValueInfoMap.put(staticPut.getField(), info) != null) { - return; - } - } - valueInfoMapsBuilder.put(clazz.type, new EnumValueInfoMap(enumValueInfoMap)); - } -}
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 09c4a5d..4e3fb01 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
@@ -1076,6 +1076,7 @@ assert !context.getHolderType().isD8R8SynthesizedLambdaClassType() || options.debug || appView.appInfo().hasPinnedInstanceInitializer(context.getHolderType()) + || appView.options().horizontalClassMergerOptions().isJavaLambdaMergingEnabled() : "Unexpected observable side effects from lambda `" + context.toSourceString() + "`"; } return;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java index 5840ea6..f0d7c28 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -195,6 +195,6 @@ @Override public void classInitializerMayBePostponed(DexEncodedMethod method) { - // Ignored. + method.getMutableOptimizationInfo().markClassInitializerMayBePostponed(); } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java index 7e78f27..262671f 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/info/field/InstanceFieldTypeInitializationInfo.java
@@ -5,10 +5,10 @@ package com.android.tools.r8.ir.optimize.info.field; import com.android.tools.r8.graph.AppView; -import com.android.tools.r8.graph.EnumValueInfoMapCollection; import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; import com.android.tools.r8.ir.analysis.type.TypeElement; +import com.android.tools.r8.ir.optimize.enums.EnumDataMap; import com.android.tools.r8.shaking.AppInfoWithLiveness; import java.util.Objects; @@ -49,14 +49,14 @@ @Override public InstanceFieldInitializationInfo rewrittenWithLens( AppView<AppInfoWithLiveness> appView, GraphLens lens) { - EnumValueInfoMapCollection unboxedEnums = appView.unboxedEnums(); + EnumDataMap enumDataMap = appView.unboxedEnums(); if (dynamicLowerBoundType != null - && unboxedEnums.containsEnum(dynamicLowerBoundType.getClassType())) { + && enumDataMap.isUnboxedEnum(dynamicLowerBoundType.getClassType())) { // No point in tracking the type of primitives. return UnknownInstanceFieldInitializationInfo.getInstance(); } if (dynamicUpperBoundType.isClassType() - && unboxedEnums.containsEnum(dynamicUpperBoundType.asClassType().getClassType())) { + && enumDataMap.isUnboxedEnum(dynamicUpperBoundType.asClassType().getClassType())) { // No point in tracking the type of primitives. return UnknownInstanceFieldInitializationInfo.getInstance(); }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java index 69bdef8..2c93f77 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/BooleanMethodOptimizer.java
@@ -19,7 +19,7 @@ import com.android.tools.r8.ir.code.Value; import java.util.Set; -public class BooleanMethodOptimizer implements LibraryMethodModelCollection { +public class BooleanMethodOptimizer extends StatelessLibraryMethodModelCollection { private final AppView<?> appView; private final DexItemFactory dexItemFactory;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java index 4323220..5ee7017 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/EnumMethodOptimizer.java
@@ -19,7 +19,8 @@ import com.android.tools.r8.ir.code.Value; import java.util.Set; -public class EnumMethodOptimizer implements LibraryMethodModelCollection { +public class EnumMethodOptimizer extends StatelessLibraryMethodModelCollection { + private final AppView<?> appView; EnumMethodOptimizer(AppView<?> appView) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java index d6c8ada..25916f9 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
@@ -35,7 +35,7 @@ /** The library types that are modeled. */ private final Set<DexType> modeledLibraryTypes = Sets.newIdentityHashSet(); - private final Map<DexType, LibraryMethodModelCollection> libraryMethodModelCollections = + private final Map<DexType, LibraryMethodModelCollection<?>> libraryMethodModelCollections = new IdentityHashMap<>(); public LibraryMemberOptimizer(AppView<?> appView) { @@ -43,6 +43,7 @@ register(new BooleanMethodOptimizer(appView)); register(new ObjectMethodOptimizer(appView)); register(new ObjectsMethodOptimizer(appView)); + register(new StringBuilderMethodOptimizer(appView)); register(new StringMethodOptimizer(appView)); if (appView.enableWholeProgramOptimizations()) { // Subtyping is required to prove the enum class is a subtype of java.lang.Enum. @@ -98,9 +99,9 @@ return modeledLibraryTypes.contains(type); } - private void register(LibraryMethodModelCollection optimizer) { + private void register(LibraryMethodModelCollection<?> optimizer) { DexType modeledType = optimizer.getType(); - LibraryMethodModelCollection existing = + LibraryMethodModelCollection<?> existing = libraryMethodModelCollections.put(modeledType, optimizer); assert existing == null; modeledLibraryTypes.add(modeledType); @@ -114,30 +115,37 @@ MethodProcessingId methodProcessingId) { Set<Value> affectedValues = Sets.newIdentityHashSet(); InstructionListIterator instructionIterator = code.instructionListIterator(); + Map<LibraryMethodModelCollection<?>, LibraryMethodModelCollection.State> optimizationStates = + new IdentityHashMap<>(); while (instructionIterator.hasNext()) { Instruction instruction = instructionIterator.next(); - if (instruction.isInvokeMethod()) { - InvokeMethod invoke = instruction.asInvokeMethod(); - DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, code.context()); - if (singleTarget != null) { - optimizeInvoke(code, instructionIterator, invoke, singleTarget, affectedValues); - } + if (!instruction.isInvokeMethod()) { + continue; } + + InvokeMethod invoke = instruction.asInvokeMethod(); + DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, code.context()); + if (singleTarget == null) { + continue; + } + + LibraryMethodModelCollection<?> optimizer = + libraryMethodModelCollections.get(singleTarget.getHolderType()); + if (optimizer == null) { + continue; + } + + LibraryMethodModelCollection.State optimizationState = + optimizationStates.computeIfAbsent( + optimizer, + libraryMethodModelCollection -> + libraryMethodModelCollection.createInitialState( + methodProcessor, methodProcessingId)); + optimizer.optimize( + code, instructionIterator, invoke, singleTarget, affectedValues, optimizationState); } if (!affectedValues.isEmpty()) { new TypeAnalysis(appView).narrowing(affectedValues); } } - - private void optimizeInvoke( - IRCode code, - InstructionListIterator instructionIterator, - InvokeMethod invoke, - DexClassAndMethod singleTarget, - Set<Value> affectedValues) { - LibraryMethodModelCollection optimizer = - libraryMethodModelCollections.getOrDefault( - singleTarget.getHolderType(), NopLibraryMethodModelCollection.getInstance()); - optimizer.optimize(code, instructionIterator, invoke, singleTarget, affectedValues); - } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java index 843e9ab..5cb30b3 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java
@@ -10,10 +10,18 @@ import com.android.tools.r8.ir.code.InstructionListIterator; import com.android.tools.r8.ir.code.InvokeMethod; import com.android.tools.r8.ir.code.Value; +import com.android.tools.r8.ir.conversion.MethodProcessingId; +import com.android.tools.r8.ir.conversion.MethodProcessor; +import com.android.tools.r8.ir.optimize.library.LibraryMethodModelCollection.State; import java.util.Set; /** Used to model the behavior of library methods for optimization purposes. */ -public interface LibraryMethodModelCollection { +public interface LibraryMethodModelCollection<T extends State> { + + default T createInitialState( + MethodProcessor methodProcessor, MethodProcessingId methodProcessingId) { + return null; + } /** * The library class whose methods are being modeled by this collection of models. As an example, @@ -30,5 +38,20 @@ InstructionListIterator instructionIterator, InvokeMethod invoke, DexClassAndMethod singleTarget, - Set<Value> affectedValues); + Set<Value> affectedValues, + T state); + + @SuppressWarnings("unchecked") + default void optimize( + IRCode code, + InstructionListIterator instructionIterator, + InvokeMethod invoke, + DexClassAndMethod singleTarget, + Set<Value> affectedValues, + Object state) { + optimize(code, instructionIterator, invoke, singleTarget, affectedValues, (T) state); + } + + /** Thread local optimization state to allow caching, etc. */ + interface State {} }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java index 55d1dc3..70ace2b 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodSideEffectModelCollection.java
@@ -36,6 +36,7 @@ .put(dexItemFactory.npeMethods.initWithMessage, alwaysTrue()) .put(dexItemFactory.objectMembers.constructor, alwaysTrue()) .put(dexItemFactory.objectMembers.getClass, alwaysTrue()) + .put(dexItemFactory.stringBuilderMethods.toString, alwaysTrue()) .put(dexItemFactory.stringMembers.hashCode, alwaysTrue()); putAll(builder, dexItemFactory.classMethods.getNames, alwaysTrue()); putAll(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LogMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LogMethodOptimizer.java index f8847d3..aa93f3c 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/library/LogMethodOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LogMethodOptimizer.java
@@ -17,7 +17,7 @@ import com.android.tools.r8.shaking.ProguardConfiguration; import java.util.Set; -public class LogMethodOptimizer implements LibraryMethodModelCollection { +public class LogMethodOptimizer extends StatelessLibraryMethodModelCollection { private static final int VERBOSE = 2; private static final int DEBUG = 3;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/NopLibraryMethodModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/NopLibraryMethodModelCollection.java index f852393..2eeb165 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/library/NopLibraryMethodModelCollection.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/NopLibraryMethodModelCollection.java
@@ -13,7 +13,7 @@ import com.android.tools.r8.ir.code.Value; import java.util.Set; -public class NopLibraryMethodModelCollection implements LibraryMethodModelCollection { +public class NopLibraryMethodModelCollection extends StatelessLibraryMethodModelCollection { private static final NopLibraryMethodModelCollection INSTANCE = new NopLibraryMethodModelCollection();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectMethodOptimizer.java index f16af45..c325500 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectMethodOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectMethodOptimizer.java
@@ -14,7 +14,7 @@ import com.android.tools.r8.ir.code.Value; import java.util.Set; -public class ObjectMethodOptimizer implements LibraryMethodModelCollection { +public class ObjectMethodOptimizer extends StatelessLibraryMethodModelCollection { private final DexItemFactory dexItemFactory;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java index 8fcc1ac..9d0c8c8 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/ObjectsMethodOptimizer.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.graph.AppView; import com.android.tools.r8.graph.DexClassAndMethod; import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexItemFactory.ObjectsMethods; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.ir.code.IRCode; import com.android.tools.r8.ir.code.InstructionListIterator; @@ -14,12 +15,17 @@ import com.android.tools.r8.ir.code.Value; import java.util.Set; -public class ObjectsMethodOptimizer implements LibraryMethodModelCollection { +public class ObjectsMethodOptimizer extends StatelessLibraryMethodModelCollection { + private final AppView<?> appView; private final DexItemFactory dexItemFactory; + private final ObjectsMethods objectsMethods; ObjectsMethodOptimizer(AppView<?> appView) { - this.dexItemFactory = appView.dexItemFactory(); + DexItemFactory dexItemFactory = appView.dexItemFactory(); + this.appView = appView; + this.dexItemFactory = dexItemFactory; + this.objectsMethods = dexItemFactory.objectsMethods; } @Override @@ -34,14 +40,16 @@ InvokeMethod invoke, DexClassAndMethod singleTarget, Set<Value> affectedValues) { - if (dexItemFactory.objectsMethods.isRequireNonNullMethod(singleTarget.getReference())) { + if (objectsMethods.isRequireNonNullMethod(singleTarget.getReference())) { optimizeRequireNonNull(instructionIterator, invoke, affectedValues); + } else if (singleTarget.getReference() == objectsMethods.toStringWithObject) { + optimizeToStringWithObject(code, instructionIterator, invoke, affectedValues); } } private void optimizeRequireNonNull( InstructionListIterator instructionIterator, InvokeMethod invoke, Set<Value> affectedValues) { - Value inValue = invoke.inValues().get(0); + Value inValue = invoke.getFirstArgument(); if (inValue.getType().isDefinitelyNotNull()) { Value outValue = invoke.outValue(); if (outValue != null) { @@ -52,4 +60,18 @@ instructionIterator.removeOrReplaceByDebugLocalRead(); } } + + private void optimizeToStringWithObject( + IRCode code, + InstructionListIterator instructionIterator, + InvokeMethod invoke, + Set<Value> affectedValues) { + Value object = invoke.getFirstArgument(); + if (object.getType().isDefinitelyNull()) { + instructionIterator.replaceCurrentInstructionWithConstString(appView, code, "null"); + if (invoke.hasOutValue()) { + affectedValues.addAll(invoke.outValue().affectedValues()); + } + } + } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StatelessLibraryMethodModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StatelessLibraryMethodModelCollection.java new file mode 100644 index 0000000..8b36204 --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StatelessLibraryMethodModelCollection.java
@@ -0,0 +1,46 @@ +// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize.library; + +import com.android.tools.r8.graph.DexClassAndMethod; +import com.android.tools.r8.ir.code.IRCode; +import com.android.tools.r8.ir.code.InstructionListIterator; +import com.android.tools.r8.ir.code.InvokeMethod; +import com.android.tools.r8.ir.code.Value; +import com.android.tools.r8.ir.conversion.MethodProcessingId; +import com.android.tools.r8.ir.conversion.MethodProcessor; +import com.android.tools.r8.ir.optimize.library.StatelessLibraryMethodModelCollection.State; +import java.util.Set; + +public abstract class StatelessLibraryMethodModelCollection + implements LibraryMethodModelCollection<State> { + + @Override + public final State createInitialState( + MethodProcessor methodProcessor, MethodProcessingId methodProcessingId) { + return null; + } + + public abstract void optimize( + IRCode code, + InstructionListIterator instructionIterator, + InvokeMethod invoke, + DexClassAndMethod singleTarget, + Set<Value> affectedValues); + + @Override + public final void optimize( + IRCode code, + InstructionListIterator instructionIterator, + InvokeMethod invoke, + DexClassAndMethod singleTarget, + Set<Value> affectedValues, + State state) { + assert state == null; + optimize(code, instructionIterator, invoke, singleTarget, affectedValues); + } + + static class State implements LibraryMethodModelCollection.State {} +}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java new file mode 100644 index 0000000..9cccb5d --- /dev/null +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
@@ -0,0 +1,269 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize.library; + +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.NEW_INSTANCE; + +import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexClassAndMethod; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexItemFactory.StringBuildingMethods; +import com.android.tools.r8.graph.DexMethod; +import com.android.tools.r8.graph.DexType; +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.InvokeMethod; +import com.android.tools.r8.ir.code.InvokeMethodWithReceiver; +import com.android.tools.r8.ir.code.InvokeStatic; +import com.android.tools.r8.ir.code.InvokeVirtual; +import com.android.tools.r8.ir.code.Value; +import com.android.tools.r8.ir.conversion.MethodProcessingId; +import com.android.tools.r8.ir.conversion.MethodProcessor; +import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations; +import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizations.UtilityMethodForCodeOptimizations; +import com.android.tools.r8.ir.optimize.library.StringBuilderMethodOptimizer.State; +import com.android.tools.r8.utils.InternalOptions; +import com.android.tools.r8.utils.WorkList; +import com.google.common.collect.Sets; +import it.unimi.dsi.fastutil.objects.Reference2BooleanMap; +import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap; +import java.util.Set; + +public class StringBuilderMethodOptimizer implements LibraryMethodModelCollection<State> { + + private final AppView<?> appView; + private final DexItemFactory dexItemFactory; + private final InternalOptions options; + private final StringBuildingMethods stringBuilderMethods; + + StringBuilderMethodOptimizer(AppView<?> appView) { + DexItemFactory dexItemFactory = appView.dexItemFactory(); + this.appView = appView; + this.dexItemFactory = dexItemFactory; + this.options = appView.options(); + this.stringBuilderMethods = dexItemFactory.stringBuilderMethods; + } + + @Override + public State createInitialState( + MethodProcessor methodProcessor, MethodProcessingId methodProcessingId) { + return new State(methodProcessor, methodProcessingId); + } + + @Override + public DexType getType() { + return dexItemFactory.stringBuilderType; + } + + @Override + public void optimize( + IRCode code, + InstructionListIterator instructionIterator, + InvokeMethod invoke, + DexClassAndMethod singleTarget, + Set<Value> affectedValues, + State state) { + if (invoke.isInvokeMethodWithReceiver()) { + InvokeMethodWithReceiver invokeWithReceiver = invoke.asInvokeMethodWithReceiver(); + if (stringBuilderMethods.isAppendMethod(singleTarget.getReference())) { + optimizeAppend(code, instructionIterator, invokeWithReceiver, singleTarget, state); + } + } + } + + private void optimizeAppend( + IRCode code, + InstructionListIterator instructionIterator, + InvokeMethodWithReceiver invoke, + DexClassAndMethod singleTarget, + State state) { + if (!state.isUnusedBuilder(invoke.getReceiver())) { + return; + } + if (invoke.hasOutValue()) { + invoke.outValue().replaceUsers(invoke.getReceiver()); + } + DexMethod appendMethod = singleTarget.getReference(); + if (stringBuilderMethods.isAppendPrimitiveMethod(appendMethod) + || stringBuilderMethods.isAppendStringMethod(appendMethod)) { + instructionIterator.removeOrReplaceByDebugLocalRead(); + } else if (stringBuilderMethods.isAppendObjectMethod(appendMethod)) { + Value object = invoke.getArgument(1); + if (object.isNeverNull()) { + // Replace the instruction by java.lang.Object.toString(). + instructionIterator.replaceCurrentInstruction( + InvokeVirtual.builder() + .setMethod(dexItemFactory.objectMembers.toString) + .setSingleArgument(object) + .build()); + } else if (options.canUseJavaUtilObjects()) { + // Replace the instruction by java.util.Objects.toString(). + instructionIterator.replaceCurrentInstruction( + InvokeStatic.builder() + .setMethod(dexItemFactory.objectsMethods.toStringWithObject) + .setSingleArgument(object) + .build()); + // Allow the java.util.Objects optimizer to optimize the newly added toString(). + instructionIterator.previous(); + } else { + // Replace the instruction by toStringIfNotNull(). + UtilityMethodForCodeOptimizations toStringIfNotNullMethod = + UtilityMethodsForCodeOptimizations.synthesizeToStringIfNotNullMethod( + appView, code.context(), state.methodProcessingId); + // TODO(b/172194277): Allow synthetics when generating CF. + if (toStringIfNotNullMethod != null) { + toStringIfNotNullMethod.optimize(state.methodProcessor); + InvokeStatic replacement = + InvokeStatic.builder() + .setMethod(toStringIfNotNullMethod.getMethod()) + .setSingleArgument(object) + .build(); + instructionIterator.replaceCurrentInstruction(replacement); + } + } + } + } + + class State implements LibraryMethodModelCollection.State { + + final MethodProcessor methodProcessor; + final MethodProcessingId methodProcessingId; + + final Reference2BooleanMap<Value> unusedBuilders = new Reference2BooleanOpenHashMap<>(); + + State(MethodProcessor methodProcessor, MethodProcessingId methodProcessingId) { + this.methodProcessor = methodProcessor; + this.methodProcessingId = methodProcessingId; + } + + boolean isUnusedBuilder(Value value) { + if (!unusedBuilders.containsKey(value)) { + computeIsUnusedBuilder(value); + assert unusedBuilders.containsKey(value); + } + return unusedBuilders.getBoolean(value); + } + + private void computeIsUnusedBuilder(Value value) { + assert !unusedBuilders.containsKey(value); + + Set<Value> aliases = Sets.newIdentityHashSet(); + boolean isUnused = computeAllAliasesIfUnusedStringBuilder(value, aliases); + aliases.forEach(alias -> unusedBuilders.put(alias, isUnused)); + } + + /** + * Adds all the aliases of the given StringBuilder value to {@param aliases}, or returns false + * if all aliases were not found (e.g., due to a phi user). + */ + private boolean computeAllAliasesIfUnusedStringBuilder(Value value, Set<Value> aliases) { + WorkList<Value> worklist = WorkList.newIdentityWorkList(value); + while (worklist.hasNext()) { + Value alias = worklist.next(); + aliases.add(alias); + + if (unusedBuilders.containsKey(alias)) { + assert !unusedBuilders.getBoolean(alias); + return false; + } + + // Don't track phi aliases. + if (alias.hasPhiUsers()) { + return false; + } + + // Analyze root, if any. + if (alias.isPhi()) { + return false; + } + + Instruction definition = alias.definition; + switch (definition.opcode()) { + case NEW_INSTANCE: + assert definition.asNewInstance().clazz == dexItemFactory.stringBuilderType; + break; + + case INVOKE_VIRTUAL: + { + InvokeVirtual invoke = definition.asInvokeVirtual(); + if (!stringBuilderMethods.isAppendMethod(invoke.getInvokedMethod())) { + // Unhandled definition. + return false; + } + worklist.addIfNotSeen(invoke.getReceiver()); + } + break; + + default: + // Unhandled definition. + return false; + } + + // Analyze all users. + for (Instruction user : alias.uniqueUsers()) { + switch (user.opcode()) { + case INVOKE_DIRECT: + { + InvokeDirect invoke = user.asInvokeDirect(); + + // Only allow invokes where the string builder value is the receiver. + if (invoke.arguments().lastIndexOf(alias) > 0) { + return false; + } + + // Only allow invoke-direct instructions that target the string builder constructor. + if (!stringBuilderMethods.isConstructorMethod(invoke.getInvokedMethod())) { + return false; + } + } + break; + + case INVOKE_VIRTUAL: + { + InvokeVirtual invoke = user.asInvokeVirtual(); + + // Only allow invokes where the string builder value is the receiver. + if (invoke.arguments().lastIndexOf(alias) > 0) { + return false; + } + + DexMethod invokedMethod = invoke.getInvokedMethod(); + + // Allow calls to append(), but make sure to introduce the newly introduced alias, + // if append() has an out-value. + if (stringBuilderMethods.isAppendMethod(invokedMethod)) { + if (invoke.hasOutValue()) { + worklist.addIfNotSeen(invoke.outValue()); + } + break; + } + + // Allow calls to toString(). + if (invokedMethod == stringBuilderMethods.toString) { + // Only allow unused StringBuilders. + if (invoke.hasOutValue() && invoke.outValue().hasNonDebugUsers()) { + return false; + } + break; + } + + // Invoke to unhandled method, give up. + return false; + } + + default: + // Unhandled user, give up. + return false; + } + } + } + return true; + } + } +}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java index 18ad3e6..c8d04ae 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringMethodOptimizer.java
@@ -18,7 +18,7 @@ import com.android.tools.r8.ir.code.Value; import java.util.Set; -public class StringMethodOptimizer implements LibraryMethodModelCollection { +public class StringMethodOptimizer extends StatelessLibraryMethodModelCollection { private final AppView<?> appView; private final DexItemFactory dexItemFactory;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLens.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLens.java index fbc4c12..75a78ab 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLens.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/ClassStaticizerGraphLens.java
@@ -9,21 +9,19 @@ import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.GraphLens.NestedGraphLens; import com.android.tools.r8.ir.code.Invoke.Type; -import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; -import com.google.common.collect.BiMap; +import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap; import com.google.common.collect.ImmutableMap; class ClassStaticizerGraphLens extends NestedGraphLens { ClassStaticizerGraphLens( AppView<?> appView, - BiMap<DexField, DexField> fieldMapping, - BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping) { + BidirectionalOneToOneMap<DexField, DexField> fieldMapping, + BidirectionalOneToOneMap<DexMethod, DexMethod> methodMapping) { super( ImmutableMap.of(), - methodMapping, + methodMapping.getForwardMap(), fieldMapping, - fieldMapping.inverse(), methodMapping.getInverseOneToOneMap(), appView.graphLens(), appView.dexItemFactory());
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java index 2313a5a..9d84c1f 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/staticizer/StaticizingProcessor.java
@@ -47,10 +47,9 @@ import com.android.tools.r8.utils.TraversalContinuation; import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; import com.android.tools.r8.utils.collections.LongLivedProgramMethodSetBuilder; +import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap; import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.android.tools.r8.utils.collections.SortedProgramMethodSet; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import com.google.common.collect.Streams; @@ -738,9 +737,10 @@ } private ProgramMethodSet staticizeMethodSymbols() { - BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping = + MutableBidirectionalOneToOneMap<DexMethod, DexMethod> methodMapping = new BidirectionalOneToOneHashMap<>(); - BiMap<DexField, DexField> fieldMapping = HashBiMap.create(); + MutableBidirectionalOneToOneMap<DexField, DexField> fieldMapping = + new BidirectionalOneToOneHashMap<>(); ProgramMethodSet staticizedMethods = ProgramMethodSet.create(); for (CandidateInfo candidate : classStaticizer.candidates.values()) { @@ -803,8 +803,8 @@ DexProgramClass candidateClass, DexType hostType, DexProgramClass hostClass, - BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping, - BiMap<DexField, DexField> fieldMapping) { + MutableBidirectionalOneToOneMap<DexMethod, DexMethod> methodMapping, + MutableBidirectionalOneToOneMap<DexField, DexField> fieldMapping) { candidateToHostMapping.put(candidateClass.type, hostType); // Process static fields.
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java index cd9fe4e..a61662c 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/string/StringBuilderOptimizer.java
@@ -30,6 +30,7 @@ import com.android.tools.r8.ir.code.InvokeMethod; import com.android.tools.r8.ir.code.InvokeMethodWithReceiver; import com.android.tools.r8.ir.code.InvokeVirtual; +import com.android.tools.r8.ir.code.NewInstance; import com.android.tools.r8.ir.code.NumberConversion; import com.android.tools.r8.ir.code.Value; import com.android.tools.r8.ir.code.ValueType; @@ -215,10 +216,9 @@ private Set<Value> findAllLocalBuilders() { // During the first iteration, collect builders that are locally created. // TODO(b/114002137): Make sure new-instance is followed by <init> before any other calls. - for (Instruction instr : code.instructions()) { - if (instr.isNewInstance() - && optimizationConfiguration.isBuilderType(instr.asNewInstance().clazz)) { - Value builder = instr.asNewInstance().dest(); + for (NewInstance newInstance : code.<NewInstance>instructions(Instruction::isNewInstance)) { + if (optimizationConfiguration.isBuilderType(newInstance.clazz)) { + Value builder = newInstance.asNewInstance().dest(); assert !builderToStringCounts.containsKey(builder); builderToStringCounts.put(builder, 0); } @@ -228,24 +228,21 @@ } int concatenationCount = 0; // During the second iteration, count builders' usage. - for (Instruction instr : code.instructions()) { - if (instr.isInvokeMethod()) { - InvokeMethod invoke = instr.asInvokeMethod(); - DexMethod invokedMethod = invoke.getInvokedMethod(); - if (optimizationConfiguration.isAppendMethod(invokedMethod)) { - concatenationCount++; - // The analysis might be overwhelmed. - if (concatenationCount > CONCATENATION_THRESHOLD) { - return ImmutableSet.of(); - } - } else if (optimizationConfiguration.isToStringMethod(invokedMethod)) { - assert invoke.arguments().size() == 1; - Value receiver = invoke.getArgument(0).getAliasedValue(); - for (Value builder : collectAllLinkedBuilders(receiver)) { - if (builderToStringCounts.containsKey(builder)) { - int count = builderToStringCounts.getInt(builder); - builderToStringCounts.put(builder, count + 1); - } + for (InvokeMethod invoke : code.<InvokeMethod>instructions(Instruction::isInvokeMethod)) { + DexMethod invokedMethod = invoke.getInvokedMethod(); + if (optimizationConfiguration.isAppendMethod(invokedMethod)) { + concatenationCount++; + // The analysis might be overwhelmed. + if (concatenationCount > CONCATENATION_THRESHOLD) { + return ImmutableSet.of(); + } + } else if (optimizationConfiguration.isToStringMethod(invokedMethod)) { + assert invoke.arguments().size() == 1; + Value receiver = invoke.getArgument(0).getAliasedValue(); + for (Value builder : collectAllLinkedBuilders(receiver)) { + if (builderToStringCounts.containsKey(builder)) { + int count = builderToStringCounts.getInt(builder); + builderToStringCounts.put(builder, count + 1); } } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java b/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java index 9484007..9625b93 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizations.java
@@ -72,4 +72,40 @@ ImmutableList.of(), ImmutableList.of()); } + + public static CfCode CfUtilityMethodsForCodeOptimizationsTemplates_toStringIfNotNull( + InternalOptions options, DexMethod method) { + CfLabel label0 = new CfLabel(); + CfLabel label1 = new CfLabel(); + CfLabel label2 = new CfLabel(); + CfLabel label3 = new CfLabel(); + return new CfCode( + method.holder, + 1, + 1, + ImmutableList.of( + label0, + new CfLoad(ValueType.OBJECT, 0), + new CfIf(If.Type.EQ, ValueType.OBJECT, label2), + label1, + new CfLoad(ValueType.OBJECT, 0), + new CfInvoke( + 182, + options.itemFactory.createMethod( + options.itemFactory.objectType, + options.itemFactory.createProto(options.itemFactory.stringType), + options.itemFactory.createString("toString")), + false), + new CfStackInstruction(CfStackInstruction.Opcode.Pop), + label2, + new CfFrame( + new Int2ReferenceAVLTreeMap<>( + new int[] {0}, + new FrameType[] {FrameType.initialized(options.itemFactory.objectType)}), + new ArrayDeque<>(Arrays.asList())), + new CfReturnVoid(), + label3), + ImmutableList.of(), + ImmutableList.of()); + } }
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java index 2c80105..6986b3d 100644 --- a/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java +++ b/src/main/java/com/android/tools/r8/ir/synthetic/DesugaredLibraryAPIConversionCfCodeProvider.java
@@ -276,7 +276,7 @@ factory.createMethod( wrapperField.holder, factory.createProto(factory.voidType, argType), - factory.initMethodName), + factory.constructorMethodName), false)); instructions.add(new CfReturn(ValueType.fromDexType(wrapperField.holder))); return standardCfCodeFromInstructions(instructions); @@ -303,7 +303,7 @@ factory.createMethod( factory.objectType, factory.createProto(factory.voidType), - factory.initMethodName), + factory.constructorMethodName), false)); instructions.add(new CfLoad(ValueType.fromDexType(wrapperField.holder), 0)); instructions.add(new CfLoad(ValueType.fromDexType(wrapperField.type), 1)); @@ -337,7 +337,7 @@ factory.createMethod( factory.runtimeExceptionType, factory.createProto(factory.voidType, factory.stringType), - factory.initMethodName), + factory.constructorMethodName), false)); instructions.add(new CfThrow()); return standardCfCodeFromInstructions(instructions);
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java index 75358f7..d21c6d9 100644 --- a/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java +++ b/src/main/java/com/android/tools/r8/ir/synthetic/EnumUnboxingCfCodeProvider.java
@@ -28,7 +28,6 @@ import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap; import com.android.tools.r8.ir.analysis.value.AbstractValue; import com.android.tools.r8.ir.code.If; import com.android.tools.r8.ir.code.ValueType; @@ -64,7 +63,6 @@ public static class EnumUnboxingInstanceFieldCfCodeProvider extends EnumUnboxingCfCodeProvider { private final DexType returnType; - private final EnumValueInfoMap enumValueInfoMap; private final EnumInstanceFieldMappingData fieldDataMap; private final AbstractValue nullValue; @@ -72,12 +70,10 @@ AppView<?> appView, DexType holder, DexType returnType, - EnumValueInfoMap enumValueInfoMap, EnumInstanceFieldMappingData fieldDataMap, AbstractValue nullValue) { super(appView, holder); this.returnType = returnType; - this.enumValueInfoMap = enumValueInfoMap; this.fieldDataMap = fieldDataMap; this.nullValue = nullValue; } @@ -101,19 +97,16 @@ // if (i == 1) { return 10;} // if (i == 2) { return 20;} - enumValueInfoMap.forEach( - (field, enumValueInfo) -> { - AbstractValue value = fieldDataMap.getData(field); - if (value != null) { - CfLabel dest = new CfLabel(); - instructions.add(new CfLoad(ValueType.fromDexType(factory.intType), 0)); - instructions.add(new CfConstNumber(enumValueInfo.convertToInt(), ValueType.INT)); - instructions.add(new CfIfCmp(If.Type.NE, ValueType.INT, dest)); - addCfInstructionsForAbstractValue(instructions, value, returnType); - instructions.add(new CfReturn(ValueType.fromDexType(returnType))); - instructions.add(dest); - instructions.add(new CfFrame(locals, ImmutableDeque.of())); - } + fieldDataMap.forEach( + (unboxedEnumValue, value) -> { + CfLabel dest = new CfLabel(); + instructions.add(new CfLoad(ValueType.fromDexType(factory.intType), 0)); + instructions.add(new CfConstNumber(unboxedEnumValue, ValueType.INT)); + instructions.add(new CfIfCmp(If.Type.NE, ValueType.INT, dest)); + addCfInstructionsForAbstractValue(instructions, value, returnType); + instructions.add(new CfReturn(ValueType.fromDexType(returnType))); + instructions.add(dest); + instructions.add(new CfFrame(locals, ImmutableDeque.of())); }); if (nullValue != null) { @@ -132,19 +125,16 @@ public static class EnumUnboxingValueOfCfCodeProvider extends EnumUnboxingCfCodeProvider { - private DexType enumType; - private EnumValueInfoMap map; + private final DexType enumType; private final EnumInstanceFieldMappingData fieldDataMap; public EnumUnboxingValueOfCfCodeProvider( AppView<?> appView, DexType holder, DexType enumType, - EnumValueInfoMap map, EnumInstanceFieldMappingData fieldDataMap) { super(appView, holder); this.enumType = enumType; - this.map = map; this.fieldDataMap = fieldDataMap; } @@ -180,16 +170,15 @@ // if (s.equals("A")) { return 1;} // if (s.equals("B")) { return 2;} - map.forEach( - (field, enumValueInfo) -> { + fieldDataMap.forEach( + (unboxedEnumValue, value) -> { CfLabel dest = new CfLabel(); instructions.add(new CfLoad(ValueType.fromDexType(factory.stringType), 0)); - AbstractValue value = fieldDataMap.getData(field); addCfInstructionsForAbstractValue(instructions, value, factory.stringType); instructions.add( new CfInvoke(Opcodes.INVOKEVIRTUAL, factory.stringMembers.equals, false)); instructions.add(new CfIf(If.Type.EQ, ValueType.INT, dest)); - instructions.add(new CfConstNumber(enumValueInfo.convertToInt(), ValueType.INT)); + instructions.add(new CfConstNumber(unboxedEnumValue, ValueType.INT)); instructions.add(new CfReturn(ValueType.INT)); instructions.add(dest); instructions.add(new CfFrame(locals, ImmutableDeque.of()));
diff --git a/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java b/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java index bd8db04..8ea4e1f 100644 --- a/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java +++ b/src/main/java/com/android/tools/r8/optimize/PublicizerLens.java
@@ -10,23 +10,22 @@ import com.android.tools.r8.graph.GraphLens; import com.android.tools.r8.graph.GraphLens.NestedGraphLens; import com.android.tools.r8.ir.code.Invoke.Type; -import com.android.tools.r8.utils.collections.BidirectionalManyToManyRepresentativeMap; +import com.android.tools.r8.utils.collections.EmptyBidirectionalOneToOneMap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; import java.util.Set; final class PublicizerLens extends NestedGraphLens { - private final AppView appView; + private final AppView<?> appView; private final Set<DexMethod> publicizedMethods; - private PublicizerLens(AppView appView, Set<DexMethod> publicizedMethods) { + private PublicizerLens(AppView<?> appView, Set<DexMethod> publicizedMethods) { super( ImmutableMap.of(), ImmutableMap.of(), - ImmutableMap.of(), - null, - BidirectionalManyToManyRepresentativeMap.empty(), + new EmptyBidirectionalOneToOneMap<>(), + new EmptyBidirectionalOneToOneMap<>(), appView.graphLens(), appView.dexItemFactory()); this.appView = appView;
diff --git a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingResult.java b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingResult.java index 09a9b27..9353111 100644 --- a/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingResult.java +++ b/src/main/java/com/android/tools/r8/optimize/bridgehoisting/BridgeHoistingResult.java
@@ -11,7 +11,8 @@ import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.ir.optimize.info.bridge.BridgeInfo; import com.android.tools.r8.shaking.AppInfoWithLiveness; -import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap; +import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap; import java.util.Set; import java.util.function.BiConsumer; @@ -20,8 +21,8 @@ private final AppView<AppInfoWithLiveness> appView; // Mapping from non-hoisted bridge methods to hoisted bridge methods. - private final BidirectionalManyToOneMap<DexMethod, DexMethod> bridgeToHoistedBridgeMap = - new BidirectionalManyToOneMap<>(); + private final MutableBidirectionalManyToOneMap<DexMethod, DexMethod> bridgeToHoistedBridgeMap = + new BidirectionalManyToOneHashMap<>(); // Mapping from non-hoisted bridge methods to the set of contexts in which they are accessed. private final MethodAccessInfoCollection.IdentityBuilder bridgeMethodAccessInfoCollectionBuilder = @@ -32,7 +33,7 @@ } public void forEachHoistedBridge(BiConsumer<ProgramMethod, BridgeInfo> consumer) { - bridgeToHoistedBridgeMap.forEach( + bridgeToHoistedBridgeMap.forEachManyToOneMapping( (bridges, hoistedBridge) -> { DexProgramClass clazz = appView.definitionForProgramType(hoistedBridge.getHolderType()); ProgramMethod method = hoistedBridge.lookupOnProgramClass(clazz);
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java index b871193..0fe63b8 100644 --- a/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java +++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingLens.java
@@ -11,6 +11,8 @@ import com.android.tools.r8.graph.GraphLens.NestedGraphLens; import com.android.tools.r8.shaking.AppInfoWithLiveness; import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; +import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; @@ -20,14 +22,13 @@ private RepackagingLens( AppView<AppInfoWithLiveness> appView, - BiMap<DexField, DexField> originalFieldSignatures, - BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures, + BidirectionalOneToOneMap<DexField, DexField> newFieldSignatures, + BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures, BiMap<DexType, DexType> originalTypes) { super( originalTypes.inverse(), - originalMethodSignatures.getInverseBacking(), - originalFieldSignatures.inverse(), - originalFieldSignatures, + originalMethodSignatures.getInverseOneToOneMap().getForwardMap(), + newFieldSignatures, originalMethodSignatures, appView.graphLens(), appView.dexItemFactory()); @@ -48,12 +49,13 @@ public static class Builder { protected final BiMap<DexType, DexType> originalTypes = HashBiMap.create(); - protected final BiMap<DexField, DexField> originalFieldSignatures = HashBiMap.create(); - protected final BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures = + protected final MutableBidirectionalOneToOneMap<DexField, DexField> newFieldSignatures = + new BidirectionalOneToOneHashMap<>(); + protected final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures = new BidirectionalOneToOneHashMap<>(); public void recordMove(DexField from, DexField to) { - originalFieldSignatures.put(to, from); + newFieldSignatures.put(from, to); } public void recordMove(DexMethod from, DexMethod to) { @@ -67,7 +69,7 @@ public RepackagingLens build(AppView<AppInfoWithLiveness> appView) { assert !originalTypes.isEmpty(); return new RepackagingLens( - appView, originalFieldSignatures, originalMethodSignatures, originalTypes); + appView, newFieldSignatures, originalMethodSignatures, originalTypes); } } }
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java index c6a2424..17d06a3 100644 --- a/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java +++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingTreeFixer.java
@@ -81,7 +81,7 @@ DexEncodedMethod.EMPTY_ARRAY, DexEncodedMethod.EMPTY_ARRAY, dexItemFactory.getSkipNameValidationForTesting(), - DexProgramClass::checksumFromType, + clazz.getChecksumSupplier(), fixupSynthesizedFrom(clazz.getSynthesizedFrom())); newClass.setInstanceFields(fixupFields(clazz.instanceFields())); newClass.setStaticFields(fixupFields(clazz.staticFields()));
diff --git a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java index 91484f7..9d5cec5 100644 --- a/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java +++ b/src/main/java/com/android/tools/r8/repackaging/RepackagingUseRegistry.java
@@ -25,12 +25,14 @@ import com.android.tools.r8.graph.SuccessfulMemberResolutionResult; import com.android.tools.r8.graph.UseRegistry; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.InternalOptions; import java.util.function.Consumer; import java.util.function.Predicate; public class RepackagingUseRegistry extends UseRegistry { private final AppInfoWithLiveness appInfo; + private final InternalOptions options; private final RepackagingConstraintGraph constraintGraph; private final ProgramDefinition context; private final InitClassLens initClassLens; @@ -42,6 +44,7 @@ ProgramDefinition context) { super(appView.dexItemFactory()); this.appInfo = appView.appInfo(); + this.options = appView.options(); this.constraintGraph = constraintGraph; this.context = context; this.initClassLens = appView.initClassLens(); @@ -60,31 +63,51 @@ return false; } - private boolean isOnlyAccessibleFromSamePackage(DexClassAndMember<?, ?> member) { - AccessFlags<?> accessFlags = member.getAccessFlags(); + private boolean isOnlyAccessibleFromSamePackage( + SuccessfulMemberResolutionResult<?, ?> resolutionResult, boolean isInvoke) { + AccessFlags<?> accessFlags = resolutionResult.getResolutionPair().getAccessFlags(); if (accessFlags.isPackagePrivate()) { return true; } - if (accessFlags.isProtected() - && !appInfo.isSubtype(context.getContextType(), member.getHolderType())) { - return true; + if (accessFlags.isProtected()) { + if (!appInfo.isSubtype( + context.getContextType(), resolutionResult.getResolvedHolder().getType())) { + return true; + } + // Check for assignability if we are generating CF: + // https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.10.1.8 + if (isInvoke + && options.isGeneratingClassFiles() + && !appInfo.isSubtype( + resolutionResult.getInitialResolutionHolder().getType(), context.getContextType())) { + return true; + } } return false; } public void registerFieldAccess(DexField field) { - registerMemberAccess(appInfo.resolveField(field)); + registerMemberAccess(appInfo.resolveField(field), false); } public ProgramMethod registerMethodReference(DexMethod method) { ResolutionResult resolutionResult = appInfo.unsafeResolveMethodDueToDexFormat(method); - registerMemberAccess(resolutionResult); + registerMemberAccess(resolutionResult, false); return resolutionResult.isSingleResolution() ? resolutionResult.asSingleResolution().getResolvedProgramMethod() : null; } + private void registerMemberAccessForInvoke(MemberResolutionResult<?, ?> resolutionResult) { + registerMemberAccess(resolutionResult, true); + } + public void registerMemberAccess(MemberResolutionResult<?, ?> resolutionResult) { + registerMemberAccess(resolutionResult, false); + } + + private void registerMemberAccess( + MemberResolutionResult<?, ?> resolutionResult, boolean isInvoke) { SuccessfulMemberResolutionResult<?, ?> successfulResolutionResult = resolutionResult.asSuccessfulMemberResolutionResult(); if (successfulResolutionResult == null) { @@ -96,14 +119,16 @@ } // Check access to the initial resolution holder. - registerClassTypeAccess(successfulResolutionResult.getInitialResolutionHolder()); + DexClass initialResolutionHolder = successfulResolutionResult.getInitialResolutionHolder(); + registerClassTypeAccess(initialResolutionHolder); // Similarly, check access to the resolved member. DexClassAndMember<?, ?> resolutionPair = successfulResolutionResult.getResolutionPair(); if (resolutionPair != null) { RepackagingConstraintGraph.Node resolvedMemberNode = constraintGraph.getNode(resolutionPair.getDefinition()); - if (resolvedMemberNode != null && isOnlyAccessibleFromSamePackage(resolutionPair)) { + if (resolvedMemberNode != null + && isOnlyAccessibleFromSamePackage(successfulResolutionResult, isInvoke)) { node.addNeighbor(resolvedMemberNode); } } @@ -149,27 +174,27 @@ @Override public void registerInvokeVirtual(DexMethod invokedMethod) { - registerMemberAccess(appInfo.resolveMethod(invokedMethod, false)); + registerMemberAccessForInvoke(appInfo.resolveMethod(invokedMethod, false)); } @Override public void registerInvokeDirect(DexMethod invokedMethod) { - registerMemberAccess(appInfo.unsafeResolveMethodDueToDexFormat(invokedMethod)); + registerMemberAccessForInvoke(appInfo.unsafeResolveMethodDueToDexFormat(invokedMethod)); } @Override public void registerInvokeStatic(DexMethod invokedMethod) { - registerMemberAccess(appInfo.unsafeResolveMethodDueToDexFormat(invokedMethod)); + registerMemberAccessForInvoke(appInfo.unsafeResolveMethodDueToDexFormat(invokedMethod)); } @Override public void registerInvokeInterface(DexMethod invokedMethod) { - registerMemberAccess(appInfo.resolveMethod(invokedMethod, true)); + registerMemberAccessForInvoke(appInfo.resolveMethod(invokedMethod, true)); } @Override public void registerInvokeSuper(DexMethod invokedMethod) { - registerMemberAccess(appInfo.unsafeResolveMethodDueToDexFormat(invokedMethod)); + registerMemberAccessForInvoke(appInfo.unsafeResolveMethodDueToDexFormat(invokedMethod)); } @Override
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 7687ad4..7f93d17 100644 --- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java +++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -25,8 +25,6 @@ import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.DirectMappedDexApplication; -import com.android.tools.r8.graph.EnumValueInfoMapCollection; -import com.android.tools.r8.graph.EnumValueInfoMapCollection.EnumValueInfoMap; import com.android.tools.r8.graph.FieldAccessInfo; import com.android.tools.r8.graph.FieldAccessInfoCollection; import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl; @@ -40,6 +38,7 @@ import com.android.tools.r8.graph.ObjectAllocationInfoCollectionImpl; import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.graph.PrunedItems; import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult; import com.android.tools.r8.graph.SubtypingInfo; import com.android.tools.r8.ir.analysis.type.ClassTypeElement; @@ -181,8 +180,6 @@ final Set<DexType> prunedTypes; /** A map from switchmap class types to their corresponding switchmaps. */ final Map<DexField, Int2ReferenceMap<DexField>> switchMaps; - /** A map from enum types to their value types and ordinals. */ - final EnumValueInfoMapCollection enumValueInfoMaps; /* A cache to improve the lookup performance of lookupSingleVirtualTarget */ private final SingleTargetLookupCache singleTargetLookupCache = new SingleTargetLookupCache(); @@ -227,7 +224,6 @@ Object2BooleanMap<DexReference> identifierNameStrings, Set<DexType> prunedTypes, Map<DexField, Int2ReferenceMap<DexField>> switchMaps, - EnumValueInfoMapCollection enumValueInfoMaps, Set<DexType> lockCandidates, Map<DexType, Visibility> initClassReferences) { super(syntheticItems, classToFeatureSplitMap, mainDexClasses); @@ -266,9 +262,9 @@ this.identifierNameStrings = identifierNameStrings; this.prunedTypes = prunedTypes; this.switchMaps = switchMaps; - this.enumValueInfoMaps = enumValueInfoMaps; this.lockCandidates = lockCandidates; this.initClassReferences = initClassReferences; + verify(); } private AppInfoWithLiveness( @@ -313,25 +309,20 @@ previous.identifierNameStrings, previous.prunedTypes, previous.switchMaps, - previous.enumValueInfoMaps, previous.lockCandidates, previous.initClassReferences); } - private AppInfoWithLiveness( - AppInfoWithLiveness previous, - DirectMappedDexApplication application, - Set<DexType> removedClasses, - Collection<? extends DexReference> additionalPinnedItems) { + private AppInfoWithLiveness(AppInfoWithLiveness previous, PrunedItems prunedItems) { this( - previous.getSyntheticItems().commitPrunedClasses(application, removedClasses), - previous.getClassToFeatureSplitMap().withoutPrunedClasses(removedClasses), - previous.getMainDexClasses().withoutPrunedClasses(removedClasses), + previous.getSyntheticItems().commitPrunedItems(prunedItems), + previous.getClassToFeatureSplitMap().withoutPrunedItems(prunedItems), + previous.getMainDexClasses().withoutPrunedItems(prunedItems), previous.deadProtoTypes, previous.missingTypes, - removedClasses == null - ? previous.liveTypes - : Sets.difference(previous.liveTypes, removedClasses), + prunedItems.hasRemovedClasses() + ? Sets.difference(previous.liveTypes, prunedItems.getRemovedClasses()) + : previous.liveTypes, previous.targetedMethods, previous.failedResolutionTargets, previous.bootstrapMethods, @@ -342,7 +333,7 @@ previous.methodAccessInfoCollection, previous.objectAllocationInfoCollection, previous.callSites, - extendPinnedItems(previous, additionalPinnedItems), + extendPinnedItems(previous, prunedItems.getAdditionalPinnedItems()), previous.mayHaveSideEffects, previous.noSideEffects, previous.assumedValues, @@ -362,14 +353,17 @@ previous.noStaticClassMerging, previous.neverPropagateValue, previous.identifierNameStrings, - removedClasses == null - ? previous.prunedTypes - : CollectionUtils.mergeSets(previous.prunedTypes, removedClasses), + prunedItems.hasRemovedClasses() + ? CollectionUtils.mergeSets(previous.prunedTypes, prunedItems.getRemovedClasses()) + : previous.prunedTypes, previous.switchMaps, - previous.enumValueInfoMaps, previous.lockCandidates, previous.initClassReferences); - assert keepInfo.verifyNoneArePinned(removedClasses, previous); + } + + private void verify() { + assert keepInfo.verifyPinnedTypesAreLive(liveTypes); + assert objectAllocationInfoCollection.verifyAllocatedTypesAreLive(liveTypes, this); } private static KeepInfoCollection extendPinnedItems( @@ -410,9 +404,7 @@ } public AppInfoWithLiveness( - AppInfoWithLiveness previous, - Map<DexField, Int2ReferenceMap<DexField>> switchMaps, - EnumValueInfoMapCollection enumValueInfoMaps) { + AppInfoWithLiveness previous, Map<DexField, Int2ReferenceMap<DexField>> switchMaps) { super( previous.getSyntheticItems().commit(previous.app()), previous.getClassToFeatureSplitMap(), @@ -452,10 +444,10 @@ this.identifierNameStrings = previous.identifierNameStrings; this.prunedTypes = previous.prunedTypes; this.switchMaps = switchMaps; - this.enumValueInfoMaps = enumValueInfoMaps; this.lockCandidates = previous.lockCandidates; this.initClassReferences = previous.initClassReferences; previous.markObsolete(); + verify(); } public static AppInfoWithLivenessModifier modifier() { @@ -693,16 +685,6 @@ return missingTypes; } - public EnumValueInfoMapCollection getEnumValueInfoMapCollection() { - assert checkIfObsolete(); - return enumValueInfoMaps; - } - - public EnumValueInfoMap getEnumValueInfoMap(DexType enumType) { - assert checkIfObsolete(); - return enumValueInfoMaps.getEnumValueInfoMap(enumType); - } - public Int2ReferenceMap<DexField> getSwitchMap(DexField field) { assert checkIfObsolete(); return switchMaps.get(field); @@ -803,6 +785,13 @@ if (isPinned(field.field)) { return false; } + return isFieldOnlyWrittenInMethodIgnoringPinning(field, method); + } + + public boolean isFieldOnlyWrittenInMethodIgnoringPinning( + DexEncodedField field, DexEncodedMethod method) { + assert checkIfObsolete(); + assert isFieldWritten(field) : "Expected field `" + field.toSourceString() + "` to be written"; FieldAccessInfo fieldAccessInfo = getFieldAccessInfoCollection().get(field.field); return fieldAccessInfo != null && fieldAccessInfo.isWritten() @@ -822,7 +811,10 @@ DexType holder = field.getHolderType(); return fieldAccessInfo.isWrittenOnlyInMethodSatisfying( method -> - method.getDefinition().isInstanceInitializer() && method.getHolderType() == holder); + method.getHolderType() == holder + && method + .getDefinition() + .isOrWillBeInlinedIntoInstanceInitializer(dexItemFactory())); } public boolean isStaticFieldWrittenOnlyInEnclosingStaticInitializer(DexEncodedField field) { @@ -933,17 +925,16 @@ * Returns a copy of this AppInfoWithLiveness where the set of classes is pruned using the given * DexApplication object. */ - public AppInfoWithLiveness prunedCopyFrom( - DirectMappedDexApplication application, - Set<DexType> removedClasses, - Collection<? extends DexReference> additionalPinnedItems) { + public AppInfoWithLiveness prunedCopyFrom(PrunedItems prunedItems) { assert checkIfObsolete(); - if (!removedClasses.isEmpty()) { + if (prunedItems.hasRemovedClasses()) { // Rebuild the hierarchy. - objectAllocationInfoCollection.mutate(mutator -> {}, this); - keepInfo.mutate(keepInfo -> keepInfo.removeKeepInfoForPrunedItems(removedClasses)); + objectAllocationInfoCollection.mutate( + mutator -> mutator.removeAllocationsForPrunedItems(prunedItems), this); + keepInfo.mutate( + keepInfo -> keepInfo.removeKeepInfoForPrunedItems(prunedItems.getRemovedClasses())); } - return new AppInfoWithLiveness(this, application, removedClasses, additionalPinnedItems); + return new AppInfoWithLiveness(this, prunedItems); } public AppInfoWithLiveness rebuildWithLiveness( @@ -1005,7 +996,6 @@ // Don't rewrite pruned types - the removed types are identified by their original name. prunedTypes, lens.rewriteFieldKeys(switchMaps), - enumValueInfoMaps.rewrittenWithLens(lens), lens.rewriteTypes(lockCandidates), lens.rewriteTypeKeys(initClassReferences)); } @@ -1210,13 +1200,7 @@ public AppInfoWithLiveness withSwitchMaps(Map<DexField, Int2ReferenceMap<DexField>> switchMaps) { assert checkIfObsolete(); assert this.switchMaps.isEmpty(); - return new AppInfoWithLiveness(this, switchMaps, enumValueInfoMaps); - } - - public AppInfoWithLiveness withEnumValueInfoMaps(EnumValueInfoMapCollection enumValueInfoMaps) { - assert checkIfObsolete(); - assert this.enumValueInfoMaps.isEmpty(); - return new AppInfoWithLiveness(this, switchMaps, enumValueInfoMaps); + return new AppInfoWithLiveness(this, switchMaps); } /**
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java index f6b5aa2..095ba07 100644 --- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java +++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -47,7 +47,6 @@ import com.android.tools.r8.graph.DirectMappedDexApplication; import com.android.tools.r8.graph.DirectMappedDexApplication.Builder; import com.android.tools.r8.graph.EnclosingMethodAttribute; -import com.android.tools.r8.graph.EnumValueInfoMapCollection; import com.android.tools.r8.graph.FieldAccessInfoCollectionImpl; import com.android.tools.r8.graph.FieldAccessInfoImpl; import com.android.tools.r8.graph.FieldResolutionResult; @@ -115,6 +114,7 @@ import com.android.tools.r8.utils.Visibility; import com.android.tools.r8.utils.WorkList; import com.android.tools.r8.utils.collections.ProgramFieldSet; +import com.android.tools.r8.utils.collections.ProgramMethodMap; import com.android.tools.r8.utils.collections.ProgramMethodSet; import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.ImmutableSet; @@ -364,8 +364,8 @@ private final DesugaredLibraryConversionWrapperAnalysis desugaredLibraryWrapperAnalysis; private final Map<DexType, Pair<LambdaClass, ProgramMethod>> lambdaClasses = new IdentityHashMap<>(); - private final Map<DexEncodedMethod, Map<DexCallSite, LambdaClass>> lambdaCallSites = - new IdentityHashMap<>(); + private final ProgramMethodMap<Map<DexCallSite, LambdaClass>> lambdaCallSites = + ProgramMethodMap.create(); private final Map<DexMethod, ProgramMethod> methodsWithBackports = new IdentityHashMap<>(); private final Set<DexProgramClass> classesWithSerializableLambdas = Sets.newIdentityHashSet(); @@ -821,6 +821,7 @@ // Utility to avoid adding to the worklist if already live. private boolean enqueueMarkMethodLiveAction(ProgramMethod method, KeepReason reason) { if (liveMethods.add(method, reason)) { + assert !method.getDefinition().getOptimizationInfo().forceInline(); workList.enqueueMarkMethodLiveAction(method, reason); return true; } @@ -929,7 +930,7 @@ LambdaClass lambdaClass = lambdaRewriter.getOrCreateLambdaClass(descriptor, context); lambdaClasses.put(lambdaClass.type, new Pair<>(lambdaClass, context)); lambdaCallSites - .computeIfAbsent(contextMethod, k -> new IdentityHashMap<>()) + .computeIfAbsent(context, k -> new IdentityHashMap<>()) .put(callSite, lambdaClass); if (lambdaClass.descriptor.interfaces.contains(appView.dexItemFactory().serializableType)) { classesWithSerializableLambdas.add(context.getHolder()); @@ -3023,6 +3024,9 @@ Map<DexType, Pair<DexProgramClass, ProgramMethod>> syntheticInstantiations = new IdentityHashMap<>(); + ProgramMethodMap<Set<DexField>> syntheticStaticFieldReadsByContext = + ProgramMethodMap.createLinked(); + Map<DexMethod, ProgramMethod> liveMethods = new IdentityHashMap<>(); Map<DexType, DexClasspathClass> syntheticClasspathClasses = new IdentityHashMap<>(); @@ -3099,6 +3103,10 @@ InstantiationReason.SYNTHESIZED_CLASS, fakeReason); } + syntheticStaticFieldReadsByContext.forEach( + (context, fields) -> + fields.forEach( + field -> enqueuer.workList.enqueueTraceStaticFieldRead(field, context))); for (ProgramMethod liveMethod : liveMethods.values()) { assert !enqueuer.targetedMethods.contains(liveMethod.getDefinition()); enqueuer.markMethodAsTargeted(liveMethod, fakeReason); @@ -3106,6 +3114,26 @@ } enqueuer.liveNonProgramTypes.addAll(syntheticClasspathClasses.values()); } + + void registerStatelessLambdaInstanceFieldReads( + ProgramMethodMap<Map<DexCallSite, LambdaClass>> lambdaCallSites) { + lambdaCallSites.forEach(this::registerStatelessLambdaInstanceFieldReads); + } + + private void registerStatelessLambdaInstanceFieldReads( + ProgramMethod context, Map<DexCallSite, LambdaClass> callSites) { + Set<DexField> syntheticStaticFieldReadsInContext = null; + for (LambdaClass lambdaClass : callSites.values()) { + if (lambdaClass.isStateless()) { + if (syntheticStaticFieldReadsInContext == null) { + syntheticStaticFieldReadsInContext = + syntheticStaticFieldReadsByContext.computeIfAbsent( + context, ignore -> Sets.newLinkedHashSet()); + } + syntheticStaticFieldReadsInContext.add(lambdaClass.lambdaField); + } + } + } } private void synthesize() { @@ -3179,6 +3207,7 @@ // Rewrite all of the invoke-dynamic instructions to lambda class instantiations. lambdaCallSites.forEach(this::rewriteLambdaCallSites); + additions.registerStatelessLambdaInstanceFieldReads(lambdaCallSites); // Remove all '$deserializeLambda$' methods which are not supported by desugaring. for (DexProgramClass clazz : classesWithSerializableLambdas) { @@ -3307,7 +3336,6 @@ joinIdentifierNameStrings(rootSet.identifierNameStrings, identifierNameStrings), Collections.emptySet(), Collections.emptyMap(), - EnumValueInfoMapCollection.empty(), lockCandidates, initClassReferences); appInfo.markObsolete(); @@ -3441,9 +3469,9 @@ } private void rewriteLambdaCallSites( - DexEncodedMethod method, Map<DexCallSite, LambdaClass> callSites) { + ProgramMethod context, Map<DexCallSite, LambdaClass> callSites) { assert !callSites.isEmpty(); - int replaced = LambdaRewriter.desugarLambdas(method, callSites::get); + int replaced = LambdaRewriter.desugarLambdas(context, callSites::get); assert replaced == callSites.size(); }
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java index 85a7b88..da98174 100644 --- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java +++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -6,7 +6,6 @@ import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; import com.android.tools.r8.errors.Unreachable; -import com.android.tools.r8.graph.AppInfo; import com.android.tools.r8.graph.DexDefinitionSupplier; import com.android.tools.r8.graph.DexEncodedField; import com.android.tools.r8.graph.DexEncodedMethod; @@ -23,7 +22,6 @@ import com.android.tools.r8.utils.InternalOptions; import com.google.common.collect.Streams; import java.util.ArrayList; -import java.util.Collection; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; @@ -155,14 +153,7 @@ && getInfo(reference, definitions).isMinificationAllowed(configuration); } - public final boolean verifyNoneArePinned(Collection<DexType> types, AppInfo appInfo) { - for (DexType type : types) { - DexProgramClass clazz = - asProgramClassOrNull(appInfo.definitionForWithoutExistenceAssert(type)); - assert clazz == null || !getClassInfo(clazz).isPinned(); - } - return true; - } + public abstract boolean verifyPinnedTypesAreLive(Set<DexType> liveTypes); // TODO(b/156715504): We should try to avoid the need for iterating pinned items. @Deprecated @@ -446,6 +437,15 @@ } @Override + public boolean verifyPinnedTypesAreLive(Set<DexType> liveTypes) { + keepClassInfo.forEach( + (type, info) -> { + assert !info.isPinned() || liveTypes.contains(type); + }); + return true; + } + + @Override public void forEachPinnedType(Consumer<DexType> consumer) { keepClassInfo.forEach( (type, info) -> {
diff --git a/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java b/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java index a5fbd32..4e6b632 100644 --- a/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java +++ b/src/main/java/com/android/tools/r8/shaking/MainDexClasses.java
@@ -7,6 +7,7 @@ import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.graph.GraphLens; +import com.android.tools.r8.graph.PrunedItems; import com.google.common.collect.Sets; import java.util.Set; import java.util.function.Consumer; @@ -87,10 +88,10 @@ return mainDexClasses.size(); } - public MainDexClasses withoutPrunedClasses(Set<DexType> prunedClasses) { + public MainDexClasses withoutPrunedItems(PrunedItems prunedItems) { MainDexClasses mainDexClassesAfterPruning = createEmptyMainDexClasses(); for (DexType mainDexClass : mainDexClasses) { - if (!prunedClasses.contains(mainDexClass)) { + if (!prunedItems.getRemovedClasses().contains(mainDexClass)) { mainDexClassesAfterPruning.mainDexClasses.add(mainDexClass); } }
diff --git a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java index 58e85ff..33e64f0 100644 --- a/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java +++ b/src/main/java/com/android/tools/r8/shaking/StaticClassMerger.java
@@ -24,10 +24,10 @@ import com.android.tools.r8.utils.SingletonEquivalence; import com.android.tools.r8.utils.TraversalContinuation; import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; +import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap; import com.google.common.base.Equivalence; import com.google.common.base.Equivalence.Wrapper; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Multiset.Entry; @@ -199,8 +199,9 @@ private final Map<MergeKey, Representative> representatives = new HashMap<>(); - private final BiMap<DexField, DexField> fieldMapping = HashBiMap.create(); - private final BidirectionalOneToOneHashMap<DexMethod, DexMethod> methodMapping = + private final MutableBidirectionalOneToOneMap<DexField, DexField> newFieldSignatures = + new BidirectionalOneToOneHashMap<>(); + private final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> methodMapping = new BidirectionalOneToOneHashMap<>(); private int numberOfMergedClasses = 0; @@ -222,27 +223,18 @@ public NestedGraphLens run() { appView.appInfo().classesWithDeterministicOrder().forEach(this::merge); - if (Log.ENABLED) { - Log.info( - getClass(), - "Merged %s classes with %s members.", - numberOfMergedClasses, - fieldMapping.size() + methodMapping.size()); - } appView.setStaticallyMergedClasses(mergedClassesBuilder.build()); return buildGraphLens(); } private NestedGraphLens buildGraphLens() { - if (!fieldMapping.isEmpty() || !methodMapping.isEmpty()) { - BiMap<DexField, DexField> originalFieldSignatures = fieldMapping.inverse(); - BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures = + if (!newFieldSignatures.isEmpty() || !methodMapping.isEmpty()) { + BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures = methodMapping.getInverseOneToOneMap(); return new NestedGraphLens( ImmutableMap.of(), - methodMapping, - fieldMapping, - originalFieldSignatures, + methodMapping.getForwardMap(), + newFieldSignatures, originalMethodSignatures, appView.graphLens(), appView.dexItemFactory()); @@ -501,7 +493,7 @@ DexMethod originalMethod = methodMapping.getRepresentativeKeyOrDefault(sourceMethod.method, sourceMethod.method); - methodMapping.forcePut(originalMethod, sourceMethodAfterMove.method); + methodMapping.put(originalMethod, sourceMethodAfterMove.method); existingMethods.add(equivalence.wrap(sourceMethodAfterMove.method)); } @@ -536,8 +528,8 @@ result[index++] = sourceFieldAfterMove; DexField originalField = - fieldMapping.inverse().getOrDefault(sourceField.field, sourceField.field); - fieldMapping.forcePut(originalField, sourceFieldAfterMove.field); + newFieldSignatures.getRepresentativeKeyOrDefault(sourceField.field, sourceField.field); + newFieldSignatures.put(originalField, sourceFieldAfterMove.field); existingFields.add(equivalence.wrap(sourceFieldAfterMove.field)); }
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 434b5d2..6f7d998 100644 --- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java +++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.shaking; +import static com.android.tools.r8.dex.Constants.TEMPORARY_INSTANCE_INITIALIZER_PREFIX; import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; import static com.android.tools.r8.ir.code.Invoke.Type.DIRECT; import static com.android.tools.r8.ir.code.Invoke.Type.STATIC; @@ -58,7 +59,8 @@ import com.android.tools.r8.utils.OptionalBool; import com.android.tools.r8.utils.Timing; import com.android.tools.r8.utils.TraversalContinuation; -import com.android.tools.r8.utils.collections.BidirectionalManyToOneMap; +import com.android.tools.r8.utils.collections.BidirectionalManyToOneHashMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalManyToOneMap; import com.google.common.base.Equivalence; import com.google.common.base.Equivalence.Wrapper; import com.google.common.collect.Iterables; @@ -206,8 +208,8 @@ private final Set<DexProgramClass> mergeCandidates = new LinkedHashSet<>(); // Map from source class to target class. - private final BidirectionalManyToOneMap<DexType, DexType> mergedClasses = - new BidirectionalManyToOneMap<>(); + private final MutableBidirectionalManyToOneMap<DexType, DexType> mergedClasses = + new BidirectionalManyToOneHashMap<>(); // Set of types that must not be merged into their subtype. private final Set<DexType> pinnedTypes = Sets.newIdentityHashSet(); @@ -901,8 +903,6 @@ private class ClassMerger { - private static final String CONSTRUCTOR_NAME = "constructor"; - private final DexProgramClass source; private final DexProgramClass target; private final VerticalClassMergerGraphLens.Builder deferredRenamings = @@ -1365,7 +1365,7 @@ DexMethod newSignature; int count = 1; do { - DexString newName = getFreshName(CONSTRUCTOR_NAME, count, oldHolder); + DexString newName = getFreshName(TEMPORARY_INSTANCE_INITIALIZER_PREFIX, count, oldHolder); newSignature = application.dexItemFactory.createMethod(target.type, method.method.proto, newName); count++;
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java index f634f48..008380c 100644 --- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java +++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMergerGraphLens.java
@@ -17,9 +17,10 @@ import com.android.tools.r8.graph.classmerging.VerticallyMergedClasses; import com.android.tools.r8.ir.code.Invoke.Type; import com.android.tools.r8.utils.IterableUtils; +import com.android.tools.r8.utils.collections.BidirectionalManyToOneRepresentativeMap; import com.android.tools.r8.utils.collections.BidirectionalOneToOneHashMap; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; +import com.android.tools.r8.utils.collections.BidirectionalOneToOneMap; +import com.android.tools.r8.utils.collections.MutableBidirectionalOneToOneMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import java.util.Collection; @@ -69,20 +70,18 @@ private VerticalClassMergerGraphLens( AppView<?> appView, VerticallyMergedClasses mergedClasses, - Map<DexField, DexField> fieldMap, + BidirectionalManyToOneRepresentativeMap<DexField, DexField> fieldMap, Map<DexMethod, DexMethod> methodMap, Set<DexMethod> mergedMethods, Map<DexType, Map<DexMethod, GraphLensLookupResultProvider>> contextualVirtualToDirectMethodMaps, - BiMap<DexField, DexField> originalFieldSignatures, - BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures, + BidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures, Map<DexMethod, DexMethod> originalMethodSignaturesForBridges, GraphLens previousLens) { super( mergedClasses.getForwardMap(), methodMap, fieldMap, - originalFieldSignatures, originalMethodSignatures, previousLens, appView.dexItemFactory()); @@ -166,13 +165,14 @@ private final DexItemFactory dexItemFactory; - protected final BiMap<DexField, DexField> fieldMap = HashBiMap.create(); + protected final MutableBidirectionalOneToOneMap<DexField, DexField> fieldMap = + new BidirectionalOneToOneHashMap<>(); protected final Map<DexMethod, DexMethod> methodMap = new IdentityHashMap<>(); private final ImmutableSet.Builder<DexMethod> mergedMethodsBuilder = ImmutableSet.builder(); private final Map<DexType, Map<DexMethod, GraphLensLookupResultProvider>> contextualVirtualToDirectMethodMaps = new IdentityHashMap<>(); - private final BidirectionalOneToOneHashMap<DexMethod, DexMethod> originalMethodSignatures = + private final MutableBidirectionalOneToOneMap<DexMethod, DexMethod> originalMethodSignatures = new BidirectionalOneToOneHashMap<>(); private final Map<DexMethod, DexMethod> originalMethodSignaturesForBridges = new IdentityHashMap<>(); @@ -185,11 +185,10 @@ static Builder createBuilderForFixup(Builder builder, VerticallyMergedClasses mergedClasses) { Builder newBuilder = new Builder(builder.dexItemFactory); - for (Map.Entry<DexField, DexField> entry : builder.fieldMap.entrySet()) { - newBuilder.map( - entry.getKey(), - builder.getFieldSignatureAfterClassMerging(entry.getValue(), mergedClasses)); - } + builder.fieldMap.forEach( + (key, value) -> + newBuilder.map( + key, builder.getFieldSignatureAfterClassMerging(value, mergedClasses))); for (Map.Entry<DexMethod, DexMethod> entry : builder.methodMap.entrySet()) { newBuilder.map( entry.getKey(), @@ -237,7 +236,6 @@ if (mergedClasses.isEmpty()) { return null; } - BiMap<DexField, DexField> originalFieldSignatures = fieldMap.inverse(); // Build new graph lens. return new VerticalClassMergerGraphLens( appView, @@ -246,7 +244,6 @@ methodMap, mergedMethodsBuilder.build(), contextualVirtualToDirectMethodMaps, - originalFieldSignatures, originalMethodSignatures, originalMethodSignaturesForBridges, appView.graphLens()); @@ -308,7 +305,7 @@ } public boolean hasOriginalSignatureMappingFor(DexField field) { - return fieldMap.inverse().containsKey(field); + return fieldMap.containsValue(field); } public boolean hasOriginalSignatureMappingFor(DexMethod method) {
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 dcdaafc..bc7d42e 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -18,6 +18,7 @@ import com.android.tools.r8.graph.GraphLens.NonIdentityGraphLens; import com.android.tools.r8.graph.ProgramDefinition; import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.graph.PrunedItems; import com.android.tools.r8.ir.conversion.MethodProcessingId; import com.android.tools.r8.synthesis.SyntheticFinalization.Result; import com.google.common.collect.ImmutableList; @@ -223,10 +224,18 @@ return nonLecacySyntheticItems.containsKey(type) || legacySyntheticTypes.contains(type); } + private boolean isLegacyCommittedSynthetic(DexType type) { + return legacySyntheticTypes.contains(type); + } + public boolean isPendingSynthetic(DexType type) { return pendingDefinitions.containsKey(type) || legacyPendingClasses.containsKey(type); } + public boolean isLegacyPendingSynthetic(DexType type) { + return legacyPendingClasses.containsKey(type); + } + public boolean isSyntheticClass(DexType type) { return isCommittedSynthetic(type) || isPendingSynthetic(type) @@ -238,6 +247,14 @@ return isSyntheticClass(clazz.type); } + public boolean isLegacySyntheticClass(DexType type) { + return isLegacyCommittedSynthetic(type) || isLegacyPendingSynthetic(type); + } + + public boolean isLegacySyntheticClass(DexProgramClass clazz) { + return isLegacySyntheticClass(clazz.getType()); + } + public Collection<DexProgramClass> getLegacyPendingClasses() { return Collections.unmodifiableCollection(legacyPendingClasses.values()); } @@ -301,14 +318,13 @@ // Commit of the synthetic items to a new fully populated application. public CommittedItems commit(DexApplication application) { - return commitPrunedClasses(application, Collections.emptySet()); + return commitPrunedItems(PrunedItems.empty(application)); } - public CommittedItems commitPrunedClasses( - DexApplication application, Set<DexType> removedClasses) { + public CommittedItems commitPrunedItems(PrunedItems prunedItems) { return commit( - application, - removedClasses, + prunedItems.getPrunedApp(), + prunedItems.getNoLongerSyntheticItems(), legacyPendingClasses, legacySyntheticTypes, pendingDefinitions,
diff --git a/src/main/java/com/android/tools/r8/tracereferences/MissingDefinitionsDiagnostic.java b/src/main/java/com/android/tools/r8/tracereferences/MissingDefinitionsDiagnostic.java index 12a2462..1d4e46a 100644 --- a/src/main/java/com/android/tools/r8/tracereferences/MissingDefinitionsDiagnostic.java +++ b/src/main/java/com/android/tools/r8/tracereferences/MissingDefinitionsDiagnostic.java
@@ -82,12 +82,18 @@ builder.append(" without definition"); builder.append(System.lineSeparator()); builder.append(System.lineSeparator()); - builder.append("Classe(s) without definition:" + System.lineSeparator()); - appendSorted(builder, missingClasses); - builder.append("Field(s) without definition:" + System.lineSeparator()); - appendSorted(builder, missingFields); - builder.append("Method(s) without definition:" + System.lineSeparator()); - appendSorted(builder, missingMethods); + if (missingClasses.size() > 0) { + builder.append("Classe(s) without definition:" + System.lineSeparator()); + appendSorted(builder, missingClasses); + } + if (missingFields.size() > 0) { + builder.append("Field(s) without definition:" + System.lineSeparator()); + appendSorted(builder, missingFields); + } + if (missingMethods.size() > 0) { + builder.append("Method(s) without definition:" + System.lineSeparator()); + appendSorted(builder, missingMethods); + } return builder.toString(); } }
diff --git a/src/main/java/com/android/tools/r8/utils/CfVersionUtils.java b/src/main/java/com/android/tools/r8/utils/CfVersionUtils.java new file mode 100644 index 0000000..dadf460 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/CfVersionUtils.java
@@ -0,0 +1,23 @@ +// 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 com.android.tools.r8.utils; + +import com.android.tools.r8.cf.CfVersion; +import com.android.tools.r8.graph.DexEncodedMethod; +import com.android.tools.r8.utils.structural.Ordered; +import java.util.List; + +public class CfVersionUtils { + + public static CfVersion max(List<DexEncodedMethod> methods) { + CfVersion result = null; + for (DexEncodedMethod method : methods) { + if (method.hasClassFileVersion()) { + result = Ordered.maxIgnoreNull(result, method.getClassFileVersion()); + } + } + return result; + } +}
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 cd3a2db..175a39e 100644 --- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java +++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -21,6 +21,7 @@ import com.android.tools.r8.dex.Marker.Backend; import com.android.tools.r8.dex.Marker.Tool; import com.android.tools.r8.errors.CompilationError; +import com.android.tools.r8.errors.ExperimentalClassFileVersionDiagnostic; import com.android.tools.r8.errors.IncompleteNestNestDesugarDiagnosic; import com.android.tools.r8.errors.InterfaceDesugarMissingTypeDiagnostic; import com.android.tools.r8.errors.InvalidDebugInfoException; @@ -38,7 +39,6 @@ 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.EnumValueInfoMapCollection; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.classmerging.HorizontallyMergedLambdaClasses; import com.android.tools.r8.graph.classmerging.StaticallyMergedClasses; @@ -49,6 +49,7 @@ import com.android.tools.r8.ir.conversion.MethodProcessingId; import com.android.tools.r8.ir.desugar.DesugaredLibraryConfiguration; import com.android.tools.r8.ir.optimize.Inliner; +import com.android.tools.r8.ir.optimize.enums.EnumDataMap; import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.position.Position; @@ -108,11 +109,13 @@ ON } - public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V11; + public static final CfVersion SUPPORTED_CF_VERSION = CfVersion.V15; + public static final CfVersion EXPERIMENTAL_CF_VERSION = CfVersion.V12; + public static final int SUPPORTED_DEX_VERSION = AndroidApiLevel.LATEST.getDexVersion().getIntValue(); - public static final int ASM_VERSION = Opcodes.ASM7; + public static final int ASM_VERSION = Opcodes.ASM9; public final DexItemFactory itemFactory; @@ -202,7 +205,7 @@ enableClassStaticizer = false; enableDevirtualization = false; enableLambdaMerging = false; - enableHorizontalClassMerging = false; + horizontalClassMergerOptions.disable(); enableStaticClassMerging = false; enableVerticalClassMerging = false; enableEnumUnboxing = false; @@ -238,11 +241,6 @@ public boolean enableFieldBitAccessAnalysis = System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null; public boolean enableStaticClassMerging = true; - public boolean enableHorizontalClassMerging = true; - public boolean enableHorizontalClassMergingConstructorMerging = true; - public int horizontalClassMergingMaxGroupSize = 30; - public int horizontalClassMergingSyntheticArgumentCount = 3; - public boolean enableHorizontalClassMergingOfKotlinLambdas = true; public boolean enableVerticalClassMerging = true; public boolean enableArgumentRemoval = true; public boolean enableUnusedInterfaceRemoval = true; @@ -582,7 +580,7 @@ * and check cast instructions needs to be collected. */ public boolean isClassMergingExtensionRequired() { - return enableHorizontalClassMerging || enableVerticalClassMerging; + return horizontalClassMergerOptions.isEnabled() || enableVerticalClassMerging; } @Override @@ -615,6 +613,8 @@ private final CallSiteOptimizationOptions callSiteOptimizationOptions = new CallSiteOptimizationOptions(); + private final HorizontalClassMergerOptions horizontalClassMergerOptions = + new HorizontalClassMergerOptions(); private final ProtoShrinkingOptions protoShrinking = new ProtoShrinkingOptions(); private final KotlinOptimizationOptions kotlinOptimizationOptions = new KotlinOptimizationOptions(); @@ -638,6 +638,10 @@ return callSiteOptimizationOptions; } + public HorizontalClassMergerOptions horizontalClassMergerOptions() { + return horizontalClassMergerOptions; + } + public ProtoShrinkingOptions protoShrinking() { return protoShrinking; } @@ -1043,6 +1047,23 @@ reporter.warning(new StringDiagnostic(message.toString(), origin)); } + private final Box<Boolean> reportedExperimentClassFileVersion = new Box<>(false); + + public void warningExperimentalClassFileVersion(Origin origin) { + synchronized (reportedExperimentClassFileVersion) { + if (reportedExperimentClassFileVersion.get()) { + return; + } + reportedExperimentClassFileVersion.set(true); + reporter.warning( + new ExperimentalClassFileVersionDiagnostic( + origin, + "One or more classes has class file version >= " + + EXPERIMENTAL_CF_VERSION.major() + + " which is not officially supported.")); + } + } + public boolean printWarnings() { boolean printed = false; boolean printOutdatedToolchain = false; @@ -1243,6 +1264,70 @@ } } + public static class HorizontalClassMergerOptions { + + public boolean enable = true; + public boolean enableConstructorMerging = true; + public boolean enableJavaLambdaMerging = false; + public boolean enableKotlinLambdaMerging = true; + + public int syntheticArgumentCount = 3; + public int maxGroupSize = 30; + + public void disable() { + enable = false; + } + + @Deprecated + public void disableKotlinLambdaMerging() { + enableKotlinLambdaMerging = false; + } + + public void enable() { + enable = true; + } + + public void enableIf(boolean enable) { + this.enable = enable; + } + + public void enableJavaLambdaMerging() { + enableJavaLambdaMerging = true; + } + + public void enableKotlinLambdaMergingIf(boolean enableKotlinLambdaMerging) { + this.enableKotlinLambdaMerging = enableKotlinLambdaMerging; + } + + public int getMaxGroupSize() { + return maxGroupSize; + } + + public int getSyntheticArgumentCount() { + return syntheticArgumentCount; + } + + public boolean isConstructorMergingEnabled() { + return enableConstructorMerging; + } + + public boolean isDisabled() { + return !isEnabled(); + } + + public boolean isEnabled() { + return enable; + } + + public boolean isJavaLambdaMergingEnabled() { + return enableJavaLambdaMerging; + } + + public boolean isKotlinLambdaMergingEnabled() { + return enableKotlinLambdaMerging; + } + } + public static class ProtoShrinkingOptions { public boolean enableGeneratedExtensionRegistryShrinking = false; @@ -1297,7 +1382,7 @@ public BiConsumer<DexItemFactory, StaticallyMergedClasses> staticallyMergedClassesConsumer = ConsumerUtils.emptyBiConsumer(); - public BiConsumer<DexItemFactory, EnumValueInfoMapCollection> unboxedEnumsConsumer = + public BiConsumer<DexItemFactory, EnumDataMap> unboxedEnumsConsumer = ConsumerUtils.emptyBiConsumer(); public BiConsumer<DexItemFactory, VerticallyMergedClasses> verticallyMergedClassesConsumer = @@ -1522,6 +1607,10 @@ return intermediate || hasMinApi(AndroidApiLevel.L); } + public boolean canUseJavaUtilObjects() { + return (isGeneratingClassFiles() && !cfToCfDesugar) || hasMinApi(AndroidApiLevel.K); + } + public boolean canUseRequireNonNull() { return isGeneratingDex() && hasMinApi(AndroidApiLevel.K); }
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 aee34f6..871a7a5 100644 --- a/src/main/java/com/android/tools/r8/utils/IterableUtils.java +++ b/src/main/java/com/android/tools/r8/utils/IterableUtils.java
@@ -15,6 +15,10 @@ public class IterableUtils { + public static <T> Iterable<T> append(Iterable<T> iterable, T element) { + return Iterables.concat(iterable, singleton(element)); + } + public static <T> List<T> ensureUnmodifiableList(Iterable<T> iterable) { List<T> list; if (iterable instanceof List<?>) {
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 902924a..a90d2bc 100644 --- a/src/main/java/com/android/tools/r8/utils/StringUtils.java +++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -341,4 +341,11 @@ throwable.printStackTrace(new PrintWriter(sw)); return sw.toString(); } + + public static String capitalize(String stringToCapitalize) { + if (stringToCapitalize == null || stringToCapitalize.isEmpty()) { + return stringToCapitalize; + } + return stringToCapitalize.substring(0, 1).toUpperCase() + stringToCapitalize.substring(1); + } }
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyMap.java new file mode 100644 index 0000000..09b3c27 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyMap.java
@@ -0,0 +1,27 @@ +// 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.collections; + +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +/** Interface that accommodates many-to-many mappings. */ +public interface BidirectionalManyToManyMap<K, V> { + + boolean containsKey(K key); + + boolean containsValue(V value); + + void forEach(BiConsumer<? super K, ? super V> consumer); + + void forEachKey(Consumer<? super K> consumer); + + Set<K> getKeys(V value); + + Set<V> getValues(K key); + + boolean isEmpty(); +}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyRepresentativeMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyRepresentativeMap.java index a4878dc..773c066 100644 --- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyRepresentativeMap.java +++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToManyRepresentativeMap.java
@@ -4,91 +4,27 @@ package com.android.tools.r8.utils.collections; -import java.util.Map; +/** + * Interface that accommodates many-to-many mappings. + * + * <p>This interface additionally adds a "representative" for each one-to-many/many-to-one mapping. + * The representative for a given key is a value from {@link #getValues(K)}. The representative for + * a given value is a key from {@link #getKeys(V)}. + */ +public interface BidirectionalManyToManyRepresentativeMap<K, V> + extends BidirectionalManyToManyMap<K, V> { -public abstract class BidirectionalManyToManyRepresentativeMap<K, V> { + K getRepresentativeKey(V value); - public static <K, V> BidirectionalManyToManyRepresentativeMap<K, V> empty() { - return new EmptyBidirectionalManyToManyRepresentativeMap<>(); - } - - public abstract boolean containsKey(K key); - - public abstract boolean containsValue(V value); - - public abstract Map<K, V> getForwardBacking(); - - public abstract Map<V, K> getInverseBacking(); - - public final Inverse getInverseManyToManyMap() { - return new Inverse(); - } - - public abstract K getRepresentativeKey(V value); - - public final K getRepresentativeKeyOrDefault(V value, K defaultValue) { + default K getRepresentativeKeyOrDefault(V value, K defaultValue) { K representativeKey = getRepresentativeKey(value); return representativeKey != null ? representativeKey : defaultValue; } - public abstract V getRepresentativeValue(K key); + V getRepresentativeValue(K key); - public final V getRepresentativeValueOrDefault(K key, V defaultValue) { + default V getRepresentativeValueOrDefault(K key, V defaultValue) { V representativeValue = getRepresentativeValue(key); return representativeValue != null ? representativeValue : defaultValue; } - - public abstract Iterable<K> getKeys(V value); - - public abstract Iterable<V> getValues(K key); - - public abstract boolean isEmpty(); - - public class Inverse extends BidirectionalManyToManyRepresentativeMap<V, K> { - - @Override - public boolean containsKey(V key) { - return BidirectionalManyToManyRepresentativeMap.this.containsValue(key); - } - - @Override - public boolean containsValue(K value) { - return BidirectionalManyToManyRepresentativeMap.this.containsKey(value); - } - - @Override - public Map<V, K> getForwardBacking() { - return BidirectionalManyToManyRepresentativeMap.this.getInverseBacking(); - } - - @Override - public Map<K, V> getInverseBacking() { - return BidirectionalManyToManyRepresentativeMap.this.getForwardBacking(); - } - - @Override - public V getRepresentativeKey(K value) { - return BidirectionalManyToManyRepresentativeMap.this.getRepresentativeValue(value); - } - - @Override - public K getRepresentativeValue(V key) { - return BidirectionalManyToManyRepresentativeMap.this.getRepresentativeKey(key); - } - - @Override - public Iterable<V> getKeys(K value) { - return BidirectionalManyToManyRepresentativeMap.this.getValues(value); - } - - @Override - public Iterable<K> getValues(V key) { - return BidirectionalManyToManyRepresentativeMap.this.getKeys(key); - } - - @Override - public boolean isEmpty() { - return BidirectionalManyToManyRepresentativeMap.this.isEmpty(); - } - } }
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneHashMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneHashMap.java new file mode 100644 index 0000000..c217e91 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneHashMap.java
@@ -0,0 +1,143 @@ +// 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.collections; + +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class BidirectionalManyToOneHashMap<K, V> implements MutableBidirectionalManyToOneMap<K, V> { + + private final Map<K, V> backing; + private final Map<V, Set<K>> inverse; + + public BidirectionalManyToOneHashMap() { + this(new IdentityHashMap<>(), new IdentityHashMap<>()); + } + + private BidirectionalManyToOneHashMap(Map<K, V> backing, Map<V, Set<K>> inverse) { + this.backing = backing; + this.inverse = inverse; + } + + @Override + public void clear() { + backing.clear(); + inverse.clear(); + } + + @Override + public boolean containsKey(K key) { + return backing.containsKey(key); + } + + @Override + public boolean containsValue(V value) { + return inverse.containsKey(value); + } + + @Override + public void forEach(BiConsumer<? super K, ? super V> consumer) { + backing.forEach(consumer); + } + + @Override + public void forEachKey(Consumer<? super K> consumer) { + backing.keySet().forEach(consumer); + } + + @Override + public void forEachManyToOneMapping(BiConsumer<? super Set<K>, V> consumer) { + inverse.forEach((value, keys) -> consumer.accept(keys, value)); + } + + @Override + public V get(Object key) { + return backing.get(key); + } + + @Override + public V getOrDefault(Object key, V value) { + return backing.getOrDefault(key, value); + } + + @Override + public Map<K, V> getForwardMap() { + return backing; + } + + @Override + public Set<K> keySet() { + return backing.keySet(); + } + + @Override + public Set<K> getKeys(V value) { + return inverse.getOrDefault(value, Collections.emptySet()); + } + + @Override + public Set<V> getValues(K key) { + V value = get(key); + return value != null ? Collections.singleton(value) : Collections.emptySet(); + } + + @Override + public boolean isEmpty() { + return backing.isEmpty(); + } + + @Override + public V remove(K key) { + V value = backing.remove(key); + if (value != null) { + Set<K> keys = inverse.get(value); + keys.remove(key); + if (keys.isEmpty()) { + inverse.remove(value); + } + } + return value; + } + + @Override + public void removeAll(Iterable<K> keys) { + keys.forEach(this::remove); + } + + @Override + public Set<K> removeValue(V value) { + Set<K> keys = inverse.remove(value); + if (keys == null) { + return Collections.emptySet(); + } + for (K key : keys) { + V removedValue = backing.remove(key); + assert removedValue == value; + } + return keys; + } + + @Override + public void put(K key, V value) { + remove(key); + backing.put(key, value); + inverse.computeIfAbsent(value, ignore -> new LinkedHashSet<>()).add(key); + } + + @Override + public void put(Iterable<K> keys, V value) { + keys.forEach(key -> put(key, value)); + } + + @Override + public Set<V> values() { + return inverse.keySet(); + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java index 361b761..1404c6a 100644 --- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java +++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneMap.java
@@ -4,109 +4,27 @@ package com.android.tools.r8.utils.collections; -import java.util.Collections; -import java.util.IdentityHashMap; -import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; -public class BidirectionalManyToOneMap<K, V> { +/** + * Interface that accommodates many-to-one mappings. + * + * <p>This interface inherits from {@link BidirectionalManyToManyMap} to allow implementing + * many-to-many mappings using many-to-one mappings. + */ +public interface BidirectionalManyToOneMap<K, V> extends BidirectionalManyToManyMap<K, V> { - private final Map<K, V> backing; - private final Map<V, Set<K>> inverse; + void forEachManyToOneMapping(BiConsumer<? super Set<K>, V> consumer); - public BidirectionalManyToOneMap() { - this(new IdentityHashMap<>(), new IdentityHashMap<>()); - } + V get(Object key); - private BidirectionalManyToOneMap(Map<K, V> backing, Map<V, Set<K>> inverse) { - this.backing = backing; - this.inverse = inverse; - } + V getOrDefault(Object key, V defaultValue); - public static <K, V> BidirectionalManyToOneMap<K, V> empty() { - return new BidirectionalManyToOneMap<>(Collections.emptyMap(), Collections.emptyMap()); - } + Map<K, V> getForwardMap(); - public boolean containsKey(K key) { - return backing.containsKey(key); - } + Set<K> keySet(); - public boolean containsValue(V value) { - return inverse.containsKey(value); - } - - public void forEach(BiConsumer<Set<K>, V> consumer) { - inverse.forEach((value, keys) -> consumer.accept(keys, value)); - } - - public V get(K key) { - return backing.get(key); - } - - public V getOrDefault(K key, V value) { - return backing.getOrDefault(key, value); - } - - public Map<K, V> getForwardMap() { - return backing; - } - - public Set<K> keySet() { - return backing.keySet(); - } - - public boolean hasKey(K key) { - return backing.containsKey(key); - } - - public boolean hasValue(V value) { - return inverse.containsKey(value); - } - - public Set<K> getKeys(V value) { - return inverse.getOrDefault(value, Collections.emptySet()); - } - - public Set<K> getKeysOrNull(V value) { - return inverse.get(value); - } - - public boolean isEmpty() { - return backing.isEmpty(); - } - - public void remove(K key) { - V value = backing.remove(key); - if (value != null) { - Set<K> keys = inverse.get(value); - keys.remove(key); - if (keys.isEmpty()) { - inverse.remove(value); - } - } - } - - public Set<K> removeValue(V value) { - Set<K> keys = inverse.remove(value); - if (keys == null) { - return Collections.emptySet(); - } - for (K key : keys) { - V removedValue = backing.remove(key); - assert removedValue == value; - } - return keys; - } - - public void put(K key, V value) { - remove(key); - backing.put(key, value); - inverse.computeIfAbsent(value, ignore -> new LinkedHashSet<>()).add(key); - } - - public Set<V> values() { - return inverse.keySet(); - } + Set<V> values(); }
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneRepresentativeHashMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneRepresentativeHashMap.java new file mode 100644 index 0000000..15e1bec --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneRepresentativeHashMap.java
@@ -0,0 +1,55 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.utils.collections; + +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Set; + +public class BidirectionalManyToOneRepresentativeHashMap<K, V> + extends BidirectionalManyToOneHashMap<K, V> + implements MutableBidirectionalManyToOneRepresentativeMap<K, V> { + + private final Map<V, K> representatives = new IdentityHashMap<>(); + + @Override + public void clear() { + super.clear(); + representatives.clear(); + } + + @Override + public K removeRepresentativeFor(V value) { + return representatives.remove(value); + } + + @Override + public void setRepresentative(V value, K representative) { + representatives.put(value, representative); + } + + @Override + public K getRepresentativeKey(V value) { + Set<K> keys = getKeys(value); + if (!keys.isEmpty()) { + return keys.size() == 1 ? keys.iterator().next() : representatives.get(value); + } + return null; + } + + @Override + public V getRepresentativeValue(K key) { + return get(key); + } + + @Override + public Set<V> getValues(K key) { + if (containsKey(key)) { + return Collections.singleton(get(key)); + } + return Collections.emptySet(); + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneRepresentativeMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneRepresentativeMap.java new file mode 100644 index 0000000..3e7bf7b --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalManyToOneRepresentativeMap.java
@@ -0,0 +1,14 @@ +// 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.collections; + +/** + * Interface that accommodates many-to-one mappings. + * + * <p>This interface implicitly adds a "representative" for each many-to-one mapping by inheriting + * from {@link BidirectionalManyToManyRepresentativeMap}. + */ +public interface BidirectionalManyToOneRepresentativeMap<K, V> + extends BidirectionalManyToOneMap<K, V>, BidirectionalManyToManyRepresentativeMap<K, V> {}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToManyHashMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToManyHashMap.java new file mode 100644 index 0000000..552a406 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToManyHashMap.java
@@ -0,0 +1,137 @@ +// 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.collections; + +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class BidirectionalOneToManyHashMap<K, V> implements MutableBidirectionalOneToManyMap<K, V> { + + private final Map<K, Set<V>> backing; + private final Map<V, K> inverse; + + public BidirectionalOneToManyHashMap() { + this(new IdentityHashMap<>(), new IdentityHashMap<>()); + } + + private BidirectionalOneToManyHashMap(Map<K, Set<V>> backing, Map<V, K> inverse) { + this.backing = backing; + this.inverse = inverse; + } + + @Override + public void clear() { + backing.clear(); + inverse.clear(); + } + + @Override + public boolean containsKey(K key) { + return backing.containsKey(key); + } + + @Override + public boolean containsValue(V value) { + return inverse.containsKey(value); + } + + @Override + public void forEach(BiConsumer<? super K, ? super V> consumer) { + backing.forEach((key, values) -> values.forEach(value -> consumer.accept(key, value))); + } + + @Override + public void forEachKey(Consumer<? super K> consumer) { + backing.keySet().forEach(consumer); + } + + @Override + public void forEachOneToManyMapping(BiConsumer<K, Set<V>> consumer) { + backing.forEach(consumer); + } + + @Override + public Set<V> get(Object key) { + return backing.get(key); + } + + @Override + public Set<V> getOrDefault(Object key, Set<V> value) { + return backing.getOrDefault(key, value); + } + + @Override + public K getKey(V value) { + return inverse.get(value); + } + + @Override + public Set<K> getKeys(V value) { + K key = inverse.get(value); + return key != null ? Collections.singleton(key) : Collections.emptySet(); + } + + @Override + public Set<V> getValues(K key) { + return getOrDefault(key, Collections.emptySet()); + } + + @Override + public boolean isEmpty() { + return backing.isEmpty(); + } + + public Set<K> keySet() { + return backing.keySet(); + } + + @Override + public Set<V> remove(K key) { + Set<V> values = backing.remove(key); + if (values == null) { + return Collections.emptySet(); + } + for (V value : values) { + K removedKey = inverse.remove(value); + assert removedKey == key; + } + return values; + } + + @Override + public void removeAll(Iterable<K> keys) { + keys.forEach(this::remove); + } + + @Override + public K removeValue(V value) { + K key = inverse.remove(value); + if (key != null) { + Set<V> values = backing.get(key); + values.remove(value); + if (values.isEmpty()) { + backing.remove(key); + } + } + return key; + } + + @Override + public void put(K key, V value) { + removeValue(value); + backing.computeIfAbsent(key, ignore -> new LinkedHashSet<>()).add(value); + inverse.put(value, key); + } + + @Override + public void put(K key, Set<V> values) { + values.forEach(value -> put(key, value)); + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToManyMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToManyMap.java new file mode 100644 index 0000000..addb1ed --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToManyMap.java
@@ -0,0 +1,25 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.utils.collections; + +import java.util.Set; +import java.util.function.BiConsumer; + +/** + * Interface that accommodates many-to-many mappings. + * + * <p>This interface inherits from {@link BidirectionalManyToManyMap} to allow implementing + * many-to-many mappings using many-to-one mappings. + */ +public interface BidirectionalOneToManyMap<K, V> extends BidirectionalManyToManyMap<K, V> { + + void forEachOneToManyMapping(BiConsumer<K, Set<V>> consumer); + + Set<V> get(Object key); + + Set<V> getOrDefault(Object key, Set<V> defaultValue); + + K getKey(V value); +}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToManyRepresentativeHashMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToManyRepresentativeHashMap.java new file mode 100644 index 0000000..93562e7 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToManyRepresentativeHashMap.java
@@ -0,0 +1,71 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.utils.collections; + +import com.google.common.collect.Streams; +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Set; + +public class BidirectionalOneToManyRepresentativeHashMap<K, V> + extends BidirectionalOneToManyHashMap<K, V> + implements MutableBidirectionalOneToManyRepresentativeMap<K, V> { + + private final Map<K, V> representatives = new IdentityHashMap<>(); + + @Override + public void clear() { + super.clear(); + representatives.clear(); + } + + @Override + public K getRepresentativeKey(V value) { + return getKey(value); + } + + @Override + public V getRepresentativeValue(K key) { + Set<V> values = getValues(key); + if (!values.isEmpty()) { + return values.size() == 1 ? values.iterator().next() : representatives.get(key); + } + return null; + } + + @Override + public Set<V> remove(K key) { + Set<V> values = super.remove(key); + removeRepresentativeFor(key); + return values; + } + + @Override + public void removeAll(Iterable<K> keys) { + super.removeAll(keys); + assert Streams.stream(keys).noneMatch(representatives::containsKey); + } + + @Override + public V removeRepresentativeFor(K key) { + return representatives.remove(key); + } + + @Override + public K removeValue(V value) { + K key = super.removeValue(value); + if (getValues(key).size() <= 1 || getRepresentativeValue(key) == value) { + removeRepresentativeFor(key); + } + return key; + } + + @Override + public void setRepresentative(K key, V representative) { + assert getValues(key).size() > 1; + assert getValues(key).contains(representative); + representatives.put(key, representative); + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToManyRepresentativeMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToManyRepresentativeMap.java new file mode 100644 index 0000000..557173c --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToManyRepresentativeMap.java
@@ -0,0 +1,14 @@ +// 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.collections; + +/** + * Interface that accommodates one-to-many mappings. + * + * <p>This interface implicitly adds a "representative" for each many-to-one mapping by inheriting + * from {@link BidirectionalManyToManyRepresentativeMap}. + */ +public interface BidirectionalOneToManyRepresentativeMap<K, V> + extends BidirectionalOneToManyMap<K, V>, BidirectionalManyToManyRepresentativeMap<K, V> {}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java index d618b52..8e2edb5 100644 --- a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java +++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneHashMap.java
@@ -4,15 +4,16 @@ package com.android.tools.r8.utils.collections; -import com.android.tools.r8.utils.IterableUtils; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; -import java.util.Collection; +import java.util.Collections; import java.util.Map; import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Consumer; public class BidirectionalOneToOneHashMap<K, V> - extends BidirectionalManyToManyRepresentativeMap<K, V> implements Map<K, V> { + implements MutableBidirectionalOneToOneMap<K, V>, Map<K, V> { private final BiMap<K, V> backing; @@ -44,8 +45,19 @@ return backing.entrySet(); } - public V forcePut(K key, V value) { - return backing.forcePut(key, value); + @Override + public void forEach(BiConsumer<? super K, ? super V> consumer) { + backing.forEach(consumer); + } + + @Override + public void forEachKey(Consumer<? super K> consumer) { + backing.keySet().forEach(consumer); + } + + @Override + public void forEachManyToOneMapping(BiConsumer<? super Set<K>, V> consumer) { + backing.forEach((key, value) -> consumer.accept(Collections.singleton(key), value)); } @Override @@ -54,15 +66,17 @@ } @Override - public BiMap<K, V> getForwardBacking() { + public V getOrDefault(Object key, V defaultValue) { + V value = get(key); + return value != null ? value : defaultValue; + } + + @Override + public BiMap<K, V> getForwardMap() { return backing; } @Override - public BiMap<V, K> getInverseBacking() { - return backing.inverse(); - } - public BidirectionalOneToOneHashMap<V, K> getInverseOneToOneMap() { return new BidirectionalOneToOneHashMap<>(backing.inverse()); } @@ -78,19 +92,19 @@ } @Override - public Iterable<K> getKeys(V value) { + public Set<K> getKeys(V value) { if (containsValue(value)) { - return IterableUtils.singleton(getRepresentativeKey(value)); + return Collections.singleton(getRepresentativeKey(value)); } - return IterableUtils.empty(); + return Collections.emptySet(); } @Override - public Iterable<V> getValues(K key) { + public Set<V> getValues(K key) { if (containsKey(key)) { - return IterableUtils.singleton(getRepresentativeValue(key)); + return Collections.singleton(getRepresentativeValue(key)); } - return IterableUtils.empty(); + return Collections.emptySet(); } @Override @@ -105,11 +119,12 @@ @Override public V put(K key, V value) { - return backing.put(key, value); + return backing.forcePut(key, value); } - public void putAll(BidirectionalOneToOneHashMap<K, V> map) { - putAll(map.backing); + @Override + public void putAll(BidirectionalManyToManyMap<K, V> map) { + map.forEach(this::put); } @Override @@ -128,7 +143,7 @@ } @Override - public Collection<V> values() { + public Set<V> values() { return backing.values(); } }
diff --git a/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneMap.java new file mode 100644 index 0000000..e5085c2 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/BidirectionalOneToOneMap.java
@@ -0,0 +1,22 @@ +// 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.collections; + +import com.google.common.collect.BiMap; + +/** + * Interface that accommodates one-to-one mappings. + * + * <p>This interface inherits from {@link BidirectionalManyToManyRepresentativeMap} to allow + * implementing many-to-many mappings using one-to-one mappings. + */ +public interface BidirectionalOneToOneMap<K, V> + extends BidirectionalManyToOneRepresentativeMap<K, V> { + + @Override + BiMap<K, V> getForwardMap(); + + BidirectionalOneToOneMap<V, K> getInverseOneToOneMap(); +}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalManyToManyRepresentativeMap.java b/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalManyToManyRepresentativeMap.java deleted file mode 100644 index dbcb753..0000000 --- a/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalManyToManyRepresentativeMap.java +++ /dev/null
@@ -1,58 +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.collections; - -import com.android.tools.r8.utils.IterableUtils; -import java.util.Collections; -import java.util.Map; - -public class EmptyBidirectionalManyToManyRepresentativeMap<K, V> - extends BidirectionalManyToManyRepresentativeMap<K, V> { - - @Override - public boolean containsKey(K key) { - return false; - } - - @Override - public boolean containsValue(V value) { - return false; - } - - @Override - public Map<K, V> getForwardBacking() { - return Collections.emptyMap(); - } - - @Override - public Map<V, K> getInverseBacking() { - return Collections.emptyMap(); - } - - @Override - public K getRepresentativeKey(V value) { - return null; - } - - @Override - public V getRepresentativeValue(K key) { - return null; - } - - @Override - public Iterable<K> getKeys(V value) { - return IterableUtils.empty(); - } - - @Override - public Iterable<V> getValues(K key) { - return IterableUtils.empty(); - } - - @Override - public boolean isEmpty() { - return true; - } -}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalOneToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalOneToOneMap.java new file mode 100644 index 0000000..f2c10c3 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/EmptyBidirectionalOneToOneMap.java
@@ -0,0 +1,98 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.utils.collections; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import java.util.Collections; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class EmptyBidirectionalOneToOneMap<K, V> + implements BidirectionalOneToOneMap<K, V>, + BidirectionalManyToOneRepresentativeMap<K, V>, + BidirectionalManyToManyRepresentativeMap<K, V> { + + @Override + public boolean containsKey(K key) { + return false; + } + + @Override + public boolean containsValue(V value) { + return false; + } + + @Override + public void forEach(BiConsumer<? super K, ? super V> consumer) { + // Intentionally empty. + } + + @Override + public void forEachKey(Consumer<? super K> consumer) { + // Intentionally empty. + } + + @Override + public void forEachManyToOneMapping(BiConsumer<? super Set<K>, V> consumer) { + // Intentionally empty. + } + + @Override + public V get(Object key) { + return null; + } + + @Override + public V getOrDefault(Object key, V defaultValue) { + return defaultValue; + } + + @Override + public BiMap<K, V> getForwardMap() { + return HashBiMap.create(); + } + + @Override + public K getRepresentativeKey(V value) { + return null; + } + + @Override + public V getRepresentativeValue(K key) { + return null; + } + + @Override + public Set<K> getKeys(V value) { + return Collections.emptySet(); + } + + @Override + public Set<V> getValues(K key) { + return Collections.emptySet(); + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public Set<K> keySet() { + return Collections.emptySet(); + } + + @Override + public Set<V> values() { + return Collections.emptySet(); + } + + @Override + public BidirectionalOneToOneMap<V, K> getInverseOneToOneMap() { + return new EmptyBidirectionalOneToOneMap<>(); + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneMap.java new file mode 100644 index 0000000..7953e91 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneMap.java
@@ -0,0 +1,23 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.utils.collections; + +import java.util.Set; + +/** Interface that provides mutable access to the implementation of a many-to-one mapping. */ +public interface MutableBidirectionalManyToOneMap<K, V> extends BidirectionalManyToOneMap<K, V> { + + void clear(); + + void put(K key, V value); + + void put(Iterable<K> key, V value); + + V remove(K key); + + void removeAll(Iterable<K> keys); + + Set<K> removeValue(V value); +}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneRepresentativeMap.java b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneRepresentativeMap.java new file mode 100644 index 0000000..24f91ac --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalManyToOneRepresentativeMap.java
@@ -0,0 +1,14 @@ +// 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.collections; + +/** Interface that provides mutable access to the implementation of a many-to-one mapping. */ +public interface MutableBidirectionalManyToOneRepresentativeMap<K, V> + extends MutableBidirectionalManyToOneMap<K, V>, BidirectionalManyToOneRepresentativeMap<K, V> { + + K removeRepresentativeFor(V value); + + void setRepresentative(V value, K representative); +}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalOneToManyMap.java b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalOneToManyMap.java new file mode 100644 index 0000000..1b9d464 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalOneToManyMap.java
@@ -0,0 +1,23 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.utils.collections; + +import java.util.Set; + +/** Interface that provides mutable access to the implementation of a one-to-many mapping. */ +public interface MutableBidirectionalOneToManyMap<K, V> extends BidirectionalOneToManyMap<K, V> { + + void clear(); + + Set<V> remove(K key); + + void removeAll(Iterable<K> keys); + + K removeValue(V value); + + void put(K key, V value); + + void put(K key, Set<V> values); +}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalOneToManyRepresentativeMap.java b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalOneToManyRepresentativeMap.java new file mode 100644 index 0000000..987eb6f --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalOneToManyRepresentativeMap.java
@@ -0,0 +1,14 @@ +// 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.collections; + +/** Interface that provides mutable access to the implementation of a one-to-many mapping. */ +public interface MutableBidirectionalOneToManyRepresentativeMap<K, V> + extends MutableBidirectionalOneToManyMap<K, V>, BidirectionalOneToManyRepresentativeMap<K, V> { + + V removeRepresentativeFor(K key); + + void setRepresentative(K key, V representative); +}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalOneToOneMap.java b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalOneToOneMap.java new file mode 100644 index 0000000..5e5f469 --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/MutableBidirectionalOneToOneMap.java
@@ -0,0 +1,13 @@ +// 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.collections; + +/** Interface that provides mutable access to the implementation of a one-to-one mapping. */ +public interface MutableBidirectionalOneToOneMap<K, V> extends BidirectionalOneToOneMap<K, V> { + + V put(K key, V value); + + void putAll(BidirectionalManyToManyMap<K, V> map); +}
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 new file mode 100644 index 0000000..d49928a --- /dev/null +++ b/src/main/java/com/android/tools/r8/utils/collections/ProgramMethodMap.java
@@ -0,0 +1,57 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.utils.collections; + +import com.android.tools.r8.graph.ProgramMethod; +import com.android.tools.r8.utils.ProgramMethodEquivalence; +import com.google.common.base.Equivalence.Wrapper; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.Supplier; + +public class ProgramMethodMap<V> { + + private final Map<Wrapper<ProgramMethod>, V> backing; + + private ProgramMethodMap(Supplier<Map<Wrapper<ProgramMethod>, V>> backingFactory) { + backing = backingFactory.get(); + } + + public static <V> ProgramMethodMap<V> create() { + return new ProgramMethodMap<>(HashMap::new); + } + + public static <V> ProgramMethodMap<V> createLinked() { + return new ProgramMethodMap<>(LinkedHashMap::new); + } + + public void clear() { + backing.clear(); + } + + public V computeIfAbsent(ProgramMethod method, Function<ProgramMethod, V> fn) { + return backing.computeIfAbsent(wrap(method), key -> fn.apply(key.get())); + } + + public void forEach(BiConsumer<ProgramMethod, V> consumer) { + backing.forEach((wrapper, value) -> consumer.accept(wrapper.get(), value)); + } + + public boolean isEmpty() { + return backing.isEmpty(); + } + + public V put(ProgramMethod method, V value) { + Wrapper<ProgramMethod> wrapper = ProgramMethodEquivalence.get().wrap(method); + return backing.put(wrapper, value); + } + + private static Wrapper<ProgramMethod> wrap(ProgramMethod method) { + return ProgramMethodEquivalence.get().wrap(method); + } +}
diff --git a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java index ee264cc..234732c 100644 --- a/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java +++ b/src/main/java/com/android/tools/r8/utils/structural/CompareToVisitorBase.java
@@ -104,6 +104,11 @@ } @Override + ItemSpecification<T> self() { + return this; + } + + @Override public ItemSpecification<T> withAssert(Predicate<T> predicate) { assert predicate.test(item1); assert predicate.test(item2); @@ -159,6 +164,22 @@ } @Override + public ItemSpecification<T> withShortArray(Function<T, short[]> getter) { + if (order == 0) { + short[] is1 = getter.apply(item1); + short[] is2 = getter.apply(item2); + int minLength = Math.min(is1.length, is2.length); + for (int i = 0; i < minLength && order == 0; i++) { + order = parent.visitInt(is1[i], is2[i]); + } + if (order == 0) { + order = parent.visitInt(is1.length, is2.length); + } + } + return this; + } + + @Override public <S> ItemSpecification<T> withConditionalCustomItem( Predicate<T> predicate, Function<T, S> getter, @@ -184,5 +205,13 @@ } return this; } + + @Override + public ItemSpecification<T> withDexReference(Function<T, DexReference> getter) { + if (order == 0) { + order = parent.visitDexReference(getter.apply(item1), getter.apply(item2)); + } + return this; + } } }
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java b/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java index c64e0b8..892183d 100644 --- a/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java +++ b/src/main/java/com/android/tools/r8/utils/structural/HashCodeVisitor.java
@@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.utils.structural; +import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept; import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept; import java.util.Arrays; @@ -23,6 +24,10 @@ */ public class HashCodeVisitor<T> extends StructuralSpecification<T, HashCodeVisitor<T>> { + public static <T extends StructuralItem<T>> int run(T item) { + return run(item, item.getStructuralMapping()); + } + public static <T> int run(T item, StructuralMapping<T> visit) { HashCodeVisitor<T> visitor = new HashCodeVisitor<>(item); visit.apply(visitor); @@ -37,6 +42,11 @@ this.item = item; } + @Override + HashCodeVisitor<T> self() { + return this; + } + private HashCodeVisitor<T> amend(int value) { // This mirrors the behavior of Objects.hash(values...) / Arrays.hashCode(array). hashCode = 31 * hashCode + value; @@ -75,6 +85,11 @@ } @Override + public HashCodeVisitor<T> withShortArray(Function<T, short[]> getter) { + return amend(Arrays.hashCode(getter.apply(item))); + } + + @Override protected <S> HashCodeVisitor<T> withConditionalCustomItem( Predicate<T> predicate, Function<T, S> getter, @@ -98,4 +113,9 @@ } return this; } + + @Override + public HashCodeVisitor<T> withDexReference(Function<T, DexReference> getter) { + return amend(getter.apply(item).hashCode()); + } }
diff --git a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java index ba499a3..e63ac6c 100644 --- a/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java +++ b/src/main/java/com/android/tools/r8/utils/structural/HashingVisitorWithTypeEquivalence.java
@@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.utils.structural; +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.utils.structural.StructuralItem.CompareToAccept; @@ -101,6 +102,11 @@ } @Override + ItemSpecification<T> self() { + return this; + } + + @Override public ItemSpecification<T> withAssert(Predicate<T> predicate) { assert predicate.test(item); return this; @@ -140,6 +146,15 @@ } @Override + public ItemSpecification<T> withShortArray(Function<T, short[]> getter) { + short[] ints = getter.apply(item); + for (int i = 0; i < ints.length; i++) { + parent.visitInt(ints[i]); + } + return this; + } + + @Override protected <S> ItemSpecification<T> withConditionalCustomItem( Predicate<T> predicate, Function<T, S> getter, @@ -160,5 +175,11 @@ parent.visitItemIterator(getter.apply(item), hasher); return this; } + + @Override + public ItemSpecification<T> withDexReference(Function<T, DexReference> getter) { + parent.visitDexReference(getter.apply(item)); + return this; + } } }
diff --git a/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java b/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java index 891fbd7..a81fc18 100644 --- a/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java +++ b/src/main/java/com/android/tools/r8/utils/structural/StructuralSpecification.java
@@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.utils.structural; +import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept; import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept; import java.util.Arrays; @@ -16,6 +17,14 @@ public abstract class StructuralSpecification<T, V extends StructuralSpecification<T, V>> { + abstract V self(); + + /** Apply a structural mapping to the present specification. */ + public final V withSpec(StructuralMapping<T> spec) { + spec.apply(this); + return self(); + } + /** * Base for accessing and visiting a sub-part on an item. * @@ -85,6 +94,25 @@ StructuralItem::acceptHashing); } + public final <S extends StructuralItem<S>> V withItemArrayAllowingNullMembers( + Function<T, S[]> getter) { + return withItemIterator( + getter.andThen(a -> Arrays.asList(a).iterator()), + (a, b, visitor) -> { + if (a == null || b == null) { + return visitor.visitBool(a != null, b != null); + } + return a.acceptCompareTo(b, visitor); + }, + (a, visitor) -> { + if (a == null) { + visitor.visitInt(0); + } else { + a.acceptHashing(visitor); + } + }); + } + /** * Helper to declare an assert on the item. * @@ -103,4 +131,8 @@ public abstract V withDouble(ToDoubleFunction<T> getter); public abstract V withIntArray(Function<T, int[]> getter); + + public abstract V withShortArray(Function<T, short[]> getter); + + public abstract V withDexReference(Function<T, DexReference> getter); }
diff --git a/src/test/examples/classmerging/ConflictInGeneratedNameTest.java b/src/test/examples/classmerging/ConflictInGeneratedNameTest.java index 6d566ec..396e73c 100644 --- a/src/test/examples/classmerging/ConflictInGeneratedNameTest.java +++ b/src/test/examples/classmerging/ConflictInGeneratedNameTest.java
@@ -15,11 +15,11 @@ public A() { print("In A.<init>()"); - constructor$classmerging$ConflictInGeneratedNameTest$A(); + $r8$constructor$classmerging$ConflictInGeneratedNameTest$A(); } - private void constructor$classmerging$ConflictInGeneratedNameTest$A() { - print("In A.constructor$classmerging$ConflictInGeneratedNameTest$A()"); + private void $r8$constructor$classmerging$ConflictInGeneratedNameTest$A() { + print("In A.$r8$constructor$classmerging$ConflictInGeneratedNameTest$A()"); } public void printState() {
diff --git a/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java b/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java index b54be6b..627d255 100644 --- a/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java +++ b/src/test/examplesAndroidO/lambdadesugaring/LambdaDesugaring.java
@@ -398,7 +398,15 @@ try { testEnforcedSignatureHelper(); } catch (Exception e) { - System.out.println(e.getMessage()); + if (e.getMessage().contains("cannot be cast to lambdadesugaring.LambdaDesugaring$B") + || e.getMessage() + .contains("cannot be cast to class lambdadesugaring.LambdaDesugaring$B")) { + System.out.println( + "lambdadesugaring.LambdaDesugaring$A cannot be cast to" + + " lambdadesugaring.LambdaDesugaring$B"); + } else { + System.out.println(e.getMessage()); + } } atA(t -> new LambdaDesugaring().reorder(t));
diff --git a/src/test/examplesJava15/records/Main.java b/src/test/examplesJava15/records/Main.java new file mode 100644 index 0000000..7be696d --- /dev/null +++ b/src/test/examplesJava15/records/Main.java
@@ -0,0 +1,16 @@ +// 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 records; + +public class Main { + + record Person(String name, int age) {} + + public static void main(String[] args) { + Person janeDoe = new Person("Jane Doe", 42); + System.out.println(janeDoe.name); + System.out.println(janeDoe.age); + } +}
diff --git a/src/test/examplesJava15/sealed/Compiler.java b/src/test/examplesJava15/sealed/Compiler.java new file mode 100644 index 0000000..8d29eea --- /dev/null +++ b/src/test/examplesJava15/sealed/Compiler.java
@@ -0,0 +1,10 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package sealed; + +public sealed abstract class Compiler permits R8Compiler, D8Compiler { + + public abstract void run(); +} \ No newline at end of file
diff --git a/src/test/examplesJava15/sealed/D8Compiler.java b/src/test/examplesJava15/sealed/D8Compiler.java new file mode 100644 index 0000000..e86df73 --- /dev/null +++ b/src/test/examplesJava15/sealed/D8Compiler.java
@@ -0,0 +1,12 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package sealed; + +public final class D8Compiler extends Compiler { + + public void run() { + System.out.println("D8 compiler"); + } +}
diff --git a/src/test/examplesJava15/sealed/Main.java b/src/test/examplesJava15/sealed/Main.java new file mode 100644 index 0000000..f163a21 --- /dev/null +++ b/src/test/examplesJava15/sealed/Main.java
@@ -0,0 +1,13 @@ +// 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 sealed; + +public class Main { + + public static void main(String[] args) { + new R8Compiler().run(); + new D8Compiler().run(); + } +}
diff --git a/src/test/examplesJava15/sealed/R8Compiler.java b/src/test/examplesJava15/sealed/R8Compiler.java new file mode 100644 index 0000000..72a522a --- /dev/null +++ b/src/test/examplesJava15/sealed/R8Compiler.java
@@ -0,0 +1,12 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package sealed; + +public final class R8Compiler extends Compiler { + + public void run() { + System.out.println("R8 compiler"); + } +}
diff --git a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java index cea34f9..5bb4d63 100644 --- a/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java +++ b/src/test/java/com/android/tools/r8/CfFrontendExamplesTest.java
@@ -21,12 +21,25 @@ import java.util.List; import java.util.function.BiConsumer; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; import org.objectweb.asm.ClassReader; import org.objectweb.asm.util.ASMifier; import org.objectweb.asm.util.TraceClassVisitor; +@RunWith(Parameterized.class) public class CfFrontendExamplesTest extends TestBase { + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + public CfFrontendExamplesTest(TestParameters parameters) { + parameters.assertNoneRuntime(); + } + @Test public void testArithmetic() throws Exception { runTest("arithmetic.Arithmetic");
diff --git a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java index bda7fe1..73d1d3a 100644 --- a/src/test/java/com/android/tools/r8/DiagnosticsChecker.java +++ b/src/test/java/com/android/tools/r8/DiagnosticsChecker.java
@@ -48,10 +48,10 @@ void run(DiagnosticsHandler handler) throws CompilationFailedException; } - public static void checkContains(String snippet, List<Diagnostic> diagnostics) { + private static void checkContains(String snippet, List<Diagnostic> diagnostics) { List<String> messages = ListUtils.map(diagnostics, Diagnostic::getDiagnosticMessage); System.out.println("Expecting match for '" + snippet + "'"); - System.out.println("StdErr:\n" + messages); + System.out.println("Diagnostics messages:\n" + messages); assertTrue( "Expected to find snippet '" + snippet @@ -60,10 +60,26 @@ diagnostics.stream().anyMatch(d -> d.getDiagnosticMessage().contains(snippet))); } + private static void checkNotContains(String snippet, List<Diagnostic> diagnostics) { + List<String> messages = ListUtils.map(diagnostics, Diagnostic::getDiagnosticMessage); + System.out.println("Expecting no match for '" + snippet + "'"); + System.out.println("Diagnostics messages:\n" + messages); + assertTrue( + "Expected to *not* find snippet '" + + snippet + + "' in error messages:\n" + + String.join("\n", messages), + diagnostics.stream().noneMatch(d -> d.getDiagnosticMessage().contains(snippet))); + } + public static void checkContains(Collection<String> snippets, List<Diagnostic> diagnostics) { snippets.forEach(snippet -> checkContains(snippet, diagnostics)); } + public static void checkNotContains(Collection<String> snippets, List<Diagnostic> diagnostics) { + snippets.forEach(snippet -> checkNotContains(snippet, diagnostics)); + } + public void checkErrorsContains(String snippet) { checkContains(snippet, errors); }
diff --git a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java index 9a36a04..bee542f 100644 --- a/src/test/java/com/android/tools/r8/KotlinCompilerTool.java +++ b/src/test/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -21,6 +21,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.function.Consumer; import java.util.stream.Collectors; import org.junit.rules.TemporaryFolder; @@ -29,29 +30,35 @@ public static final class KotlinCompiler { private final String name; - private final Path path; + private final Path lib; + private final Path compiler; - public KotlinCompiler(String name, Path path) { + public KotlinCompiler(String name) { this.name = name; - this.path = path; + this.lib = Paths.get(ToolHelper.THIRD_PARTY_DIR, "kotlin", name, "kotlinc", "lib"); + this.compiler = lib.resolve("kotlin-compiler.jar"); } - public Path getPath() { - return path; + public KotlinCompiler(String name, Path compiler) { + this.name = name; + this.compiler = compiler; + this.lib = null; + } + + public Path getCompiler() { + return compiler; + } + + public Path getFolder() { + return lib; + } + + @Override + public String toString() { + return name; } } - public static KotlinCompiler KOTLINC = - new KotlinCompiler( - "kotlinc", - Paths.get( - ToolHelper.THIRD_PARTY_DIR, - "kotlin", - "kotlin-compiler-1.3.72", - "kotlinc", - "lib", - "kotlin-compiler.jar")); - private final CfRuntime jdk; private final TestState state; private final KotlinCompiler compiler; @@ -70,6 +77,14 @@ this.targetVersion = targetVersion; } + public KotlinCompiler getCompiler() { + return compiler; + } + + public KotlinTargetVersion getTargetVersion() { + return targetVersion; + } + public static KotlinCompilerTool create( CfRuntime jdk, TemporaryFolder temp, @@ -104,6 +119,24 @@ return addSourceFilesWithNonKtExtension(temp, Arrays.asList(files)); } + public KotlinCompilerTool includeRuntime() { + assert !additionalArguments.contains("-include-runtime"); + addArguments("-include-runtime"); + return this; + } + + public KotlinCompilerTool noReflect() { + assert !additionalArguments.contains("-no-reflect"); + addArguments("-no-reflect"); + return this; + } + + public KotlinCompilerTool noStdLib() { + assert !additionalArguments.contains("-no-stdlib"); + addArguments("-no-stdlib"); + return this; + } + public KotlinCompilerTool addSourceFilesWithNonKtExtension( TemporaryFolder temp, Collection<Path> files) { return addSourceFiles( @@ -145,6 +178,11 @@ return this; } + public KotlinCompilerTool apply(Consumer<KotlinCompilerTool> consumer) { + consumer.accept(this); + return this; + } + private Path getOrCreateOutputPath() throws IOException { return output != null ? output : state.getNewTempFolder().resolve("out.jar"); } @@ -167,7 +205,7 @@ List<String> cmdline = new ArrayList<>(); cmdline.add(jdk.getJavaExecutable().toString()); cmdline.add("-cp"); - cmdline.add(compiler.getPath().toString()); + cmdline.add(compiler.getCompiler().toString()); cmdline.add(ToolHelper.K2JVMCompiler); if (useJvmAssertions) { cmdline.add("-Xassertions=jvm");
diff --git a/src/test/java/com/android/tools/r8/KotlinTestBase.java b/src/test/java/com/android/tools/r8/KotlinTestBase.java index 6cd9cfd..f8dc222 100644 --- a/src/test/java/com/android/tools/r8/KotlinTestBase.java +++ b/src/test/java/com/android/tools/r8/KotlinTestBase.java
@@ -5,6 +5,8 @@ import static org.hamcrest.CoreMatchers.containsString; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; +import com.android.tools.r8.TestRuntime.CfRuntime; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.FileUtils; @@ -12,13 +14,19 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.IdentityHashMap; import java.util.List; +import java.util.Map; +import java.util.function.Consumer; import java.util.stream.Collectors; import org.hamcrest.Matcher; public abstract class KotlinTestBase extends TestBase { - protected static final String checkParameterIsNotNullSignature = + protected static final String checkParameterIsNotNullSignature = "void kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull(" + "java.lang.Object, java.lang.String)"; protected static final String throwParameterIsNotNullExceptionSignature = @@ -29,10 +37,14 @@ private static final String RSRC = "kotlinR8TestResources"; + private static final Map<String, KotlinCompileMemoizer> compileMemoizers = new HashMap<>(); + + protected final KotlinCompiler kotlinc; protected final KotlinTargetVersion targetVersion; - protected KotlinTestBase(KotlinTargetVersion targetVersion) { + protected KotlinTestBase(KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { this.targetVersion = targetVersion; + this.kotlinc = kotlinc; } protected static List<Path> getKotlinFilesInTestPackage(Package pkg) throws IOException { @@ -42,8 +54,7 @@ .collect(Collectors.toList()); } - protected static Path getKotlinFileInTestPackage(Package pkg, String fileName) - throws IOException { + protected static Path getKotlinFileInTestPackage(Package pkg, String fileName) { String folder = DescriptorUtils.getBinaryNameFromJavaType(pkg.getName()); return getKotlinFileInTest(folder, fileName); } @@ -56,9 +67,14 @@ return Paths.get(ToolHelper.TESTS_DIR, RSRC, folder, fileName + FileUtils.KT_EXTENSION); } - protected Path getKotlinJarFile(String folder) { - return Paths.get(ToolHelper.TESTS_BUILD_DIR, RSRC, - targetVersion.getFolderName(), folder + FileUtils.JAR_EXTENSION); + protected static List<Path> getKotlinFilesInResource(String folder) { + try { + return Files.walk(Paths.get(ToolHelper.TESTS_DIR, RSRC, folder)) + .filter(path -> path.toString().endsWith(".kt") || path.toString().endsWith(".java")) + .collect(Collectors.toList()); + } catch (IOException e) { + throw new RuntimeException(e); + } } protected Path getJavaJarFile(String folder) { @@ -73,4 +89,60 @@ protected static Matcher<String> expectedInfoMessagesFromKotlinStdLib() { return containsString("No VersionRequirement"); } + + protected KotlinCompilerTool kotlinCompilerTool() { + return KotlinCompilerTool.create(CfRuntime.getCheckedInJdk9(), temp, kotlinc, targetVersion); + } + + public static KotlinCompileMemoizer getCompileMemoizer(Path... source) { + return new KotlinCompileMemoizer(Arrays.asList(source)); + } + + public static KotlinCompileMemoizer getCompileMemoizer(Collection<Path> sources) { + assert sources.size() > 0; + return new KotlinCompileMemoizer(sources); + } + + public static KotlinCompileMemoizer getCompileMemoizer( + Collection<Path> sources, String sharedFolder) { + return compileMemoizers.computeIfAbsent( + sharedFolder, ignore -> new KotlinCompileMemoizer(sources)); + } + + public static class KotlinCompileMemoizer { + + private final Collection<Path> sources; + private Consumer<KotlinCompilerTool> kotlinCompilerToolConsumer = x -> {}; + private final Map<KotlinCompiler, Map<KotlinTargetVersion, Path>> compiledPaths = + new IdentityHashMap<>(); + + public KotlinCompileMemoizer(Collection<Path> sources) { + this.sources = sources; + } + + public KotlinCompileMemoizer configure(Consumer<KotlinCompilerTool> consumer) { + this.kotlinCompilerToolConsumer = consumer; + return this; + } + + public Path getForConfiguration(KotlinCompiler compiler, KotlinTargetVersion targetVersion) { + Map<KotlinTargetVersion, Path> kotlinTargetVersionPathMap = compiledPaths.get(compiler); + if (kotlinTargetVersionPathMap == null) { + kotlinTargetVersionPathMap = new IdentityHashMap<>(); + compiledPaths.put(compiler, kotlinTargetVersionPathMap); + } + return kotlinTargetVersionPathMap.computeIfAbsent( + targetVersion, + ignored -> { + try { + return kotlinc(compiler, targetVersion) + .addSourceFiles(sources) + .apply(kotlinCompilerToolConsumer) + .compile(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + } }
diff --git a/src/test/java/com/android/tools/r8/R8CfVersionTest.java b/src/test/java/com/android/tools/r8/R8CfVersionTest.java new file mode 100644 index 0000000..4f051ad --- /dev/null +++ b/src/test/java/com/android/tools/r8/R8CfVersionTest.java
@@ -0,0 +1,54 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8; + +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.cf.CfVersion; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import java.io.IOException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class R8CfVersionTest extends TestBase { + + private final CfVersion targetVersion = CfVersion.V1_8; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNoneRuntime().build(); + } + + public R8CfVersionTest(TestParameters parameters) {} + + @Test + public void testCfVersionR8() throws IOException { + CodeInspector inspector = new CodeInspector(ToolHelper.R8_WITH_DEPS_JAR); + inspector.forAllClasses( + clazz -> { + assertTrue( + clazz + .getDexProgramClass() + .getInitialClassFileVersion() + .isLessThanOrEqualTo(targetVersion)); + }); + } + + @Test + public void testCfVersionR8Lib() throws IOException { + CodeInspector inspector = new CodeInspector(ToolHelper.R8LIB_JAR); + inspector.forAllClasses( + clazz -> { + assertTrue( + clazz + .getDexProgramClass() + .getInitialClassFileVersion() + .isLessThanOrEqualTo(targetVersion)); + }); + } +}
diff --git a/src/test/java/com/android/tools/r8/R8TestRunResult.java b/src/test/java/com/android/tools/r8/R8TestRunResult.java index 0afdccf..bbd5836 100644 --- a/src/test/java/com/android/tools/r8/R8TestRunResult.java +++ b/src/test/java/com/android/tools/r8/R8TestRunResult.java
@@ -94,8 +94,7 @@ } public <E extends Throwable> R8TestRunResult inspectStackTrace( - ThrowingBiConsumer<StackTrace, CodeInspector, E> consumer) - throws E, IOException, ExecutionException { + ThrowingBiConsumer<StackTrace, CodeInspector, E> consumer) throws E, IOException { consumer.accept(getStackTrace(), new CodeInspector(app, proguardMap)); return self(); }
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java index 5669af0..e1d4c9e 100644 --- a/src/test/java/com/android/tools/r8/TestBase.java +++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1702,6 +1702,16 @@ return AndroidApiLevel.L; } + public static boolean canUseJavaUtilObjects(TestParameters parameters) { + return parameters.isCfRuntime() + || parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K); + } + + public static boolean canUseRequireNonNull(TestParameters parameters) { + return parameters.isDexRuntime() + && parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.K); + } + public Path compileToZip( TestParameters parameters, Collection<Class<?>> classPath, Class<?>... compilationUnit) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java index abdfb83..f196151 100644 --- a/src/test/java/com/android/tools/r8/TestCompilerBuilder.java +++ b/src/test/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -47,7 +47,7 @@ options -> { options.testing.allowClassInlinerGracefulExit = false; options.testing.reportUnusedProguardConfigurationRules = true; - options.enableHorizontalClassMerging = true; + options.horizontalClassMergerOptions().enable(); }; final Backend backend; @@ -130,7 +130,6 @@ public T addHorizontallyMergedClassesInspectorIf( boolean condition, Consumer<HorizontallyMergedClassesInspector> inspector) { - if (condition) { return addHorizontallyMergedClassesInspector(inspector); }
diff --git a/src/test/java/com/android/tools/r8/TestParametersBuilder.java b/src/test/java/com/android/tools/r8/TestParametersBuilder.java index f93d6d6..9b84425 100644 --- a/src/test/java/com/android/tools/r8/TestParametersBuilder.java +++ b/src/test/java/com/android/tools/r8/TestParametersBuilder.java
@@ -19,10 +19,6 @@ public class TestParametersBuilder { - // Static computation of VMs configured as available by the testing invocation. - private static final List<TestRuntime> availableRuntimes = - getAvailableRuntimes().collect(Collectors.toList()); - // Predicate describing which test parameters are applicable to the test. // Built via the methods found below. Defaults to no applicable parameters, i.e., the emtpy set. private Predicate<TestParameters> filter = param -> false; @@ -154,6 +150,7 @@ private Predicate<AndroidApiLevel> apiLevelFilter = param -> false; private List<AndroidApiLevel> explicitApiLevels = new ArrayList<>(); + private List<TestRuntime> customRuntimes = new ArrayList<>(); private TestParametersBuilder withApiFilter(Predicate<AndroidApiLevel> filter) { enableApiLevels = true; @@ -195,13 +192,23 @@ return withApiFilter(api -> api.getLevel() < endExclusive.getLevel()); } + public TestParametersBuilder withCustomRuntime(TestRuntime runtime) { + assert getUnfilteredAvailableRuntimes().noneMatch(r -> r == runtime); + customRuntimes.add(runtime); + return this; + } + public TestParametersCollection build() { assert !enableApiLevels || enableApiLevelsForCf || hasDexRuntimeFilter; - return new TestParametersCollection( + List<TestParameters> availableParameters = getAvailableRuntimes() .flatMap(this::createParameters) .filter(filter) - .collect(Collectors.toList())); + .collect(Collectors.toList()); + List<TestParameters> customParameters = + customRuntimes.stream().flatMap(this::createParameters).collect(Collectors.toList()); + availableParameters.addAll(customParameters); + return new TestParametersCollection(availableParameters); } public Stream<TestParameters> createParameters(TestRuntime runtime) {
diff --git a/src/test/java/com/android/tools/r8/TestRuntime.java b/src/test/java/com/android/tools/r8/TestRuntime.java index db1ca7f..04b61c0 100644 --- a/src/test/java/com/android/tools/r8/TestRuntime.java +++ b/src/test/java/com/android/tools/r8/TestRuntime.java
@@ -27,6 +27,7 @@ JDK9("jdk9", 53), JDK10("jdk10", 54), JDK11("jdk11", 55), + JDK15("jdk15", 59), ; private final String name; @@ -67,6 +68,7 @@ private static final Path JDK9_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "openjdk-9.0.4"); private static final Path JDK11_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "jdk-11"); + private static final Path JDK15_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "jdk-15"); public static CfRuntime getCheckedInJdk8() { Path home; @@ -81,30 +83,28 @@ return new CfRuntime(CfVm.JDK8, home); } - public static CfRuntime getCheckedInJdk9() { - Path home; + private static Path getCheckedInJdkHome(Path path) { if (ToolHelper.isLinux()) { - home = JDK9_PATH.resolve("linux"); + return path.resolve("linux"); } else if (ToolHelper.isMac()) { - home = JDK9_PATH.resolve("osx"); + return path.resolve("osx"); } else { assert ToolHelper.isWindows(); - home = JDK9_PATH.resolve("windows"); + return path.resolve("windows"); } - return new CfRuntime(CfVm.JDK9, home); + } + + public static CfRuntime getCheckedInJdk9() { + return new CfRuntime(CfVm.JDK9, getCheckedInJdkHome(JDK9_PATH)); } public static CfRuntime getCheckedInJdk11() { - Path home; - if (ToolHelper.isLinux()) { - home = JDK11_PATH.resolve("Linux"); - } else if (ToolHelper.isMac()) { - home = Paths.get(JDK11_PATH.toString(), "Mac", "Contents", "Home"); - } else { - assert ToolHelper.isWindows(); - home = JDK11_PATH.resolve("Windows"); - } - return new CfRuntime(CfVm.JDK11, home); + return new CfRuntime(CfVm.JDK11, getCheckedInJdkHome(JDK11_PATH)); + } + + // TODO(b/169692487): Add this to 'getCheckedInCfRuntimes' when we start having support for JDK15. + public static CfRuntime getCheckedInJdk15() { + return new CfRuntime(CfVm.JDK15, getCheckedInJdkHome(JDK15_PATH)); } public static List<CfRuntime> getCheckedInCfRuntimes() {
diff --git a/src/test/java/com/android/tools/r8/ToolHelper.java b/src/test/java/com/android/tools/r8/ToolHelper.java index 3ad6107..367a8f3 100644 --- a/src/test/java/com/android/tools/r8/ToolHelper.java +++ b/src/test/java/com/android/tools/r8/ToolHelper.java
@@ -10,6 +10,7 @@ import static org.junit.Assert.fail; import com.android.tools.r8.DeviceRunner.DeviceRunnerConfigurationException; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestBase.Backend; import com.android.tools.r8.TestRuntime.CfRuntime; import com.android.tools.r8.ToolHelper.DexVm.Kind; @@ -121,8 +122,8 @@ public static final String JAVA_CLASSES_DIR = BUILD_DIR + "classes/java/"; public static final String JDK_11_TESTS_CLASSES_DIR = JAVA_CLASSES_DIR + "jdk11Tests/"; - public static final String ASM_JAR = BUILD_DIR + "deps/asm-8.0.jar"; - public static final String ASM_UTIL_JAR = BUILD_DIR + "deps/asm-util-8.0.jar"; + public static final String ASM_JAR = BUILD_DIR + "deps/asm-9.0.jar"; + public static final String ASM_UTIL_JAR = BUILD_DIR + "deps/asm-util-9.0.jar"; public static final Path API_SAMPLE_JAR = Paths.get("tests", "r8_api_usage_sample.jar"); @@ -140,17 +141,7 @@ public static final String RHINO_ANDROID_JAR = "third_party/rhino-android-1.1.1/rhino-android-1.1.1.jar"; public static final String RHINO_JAR = "third_party/rhino-1.7.10/rhino-1.7.10.jar"; - static final String KT_PRELOADER = - "third_party/kotlin/kotlin-compiler-1.3.72/kotlinc/lib/kotlin-preloader.jar"; - public static final String KT_COMPILER = - "third_party/kotlin/kotlin-compiler-1.3.72/kotlinc/lib/kotlin-compiler.jar"; public static final String K2JVMCompiler = "org.jetbrains.kotlin.cli.jvm.K2JVMCompiler"; - public static final String KT_STDLIB = - "third_party/kotlin/kotlin-compiler-1.3.72/kotlinc/lib/kotlin-stdlib.jar"; - public static final String KT_REFLECT = - "third_party/kotlin/kotlin-compiler-1.3.72/kotlinc/lib/kotlin-reflect.jar"; - public static final String KT_SCRIPT_RT = - "third_party/kotlin/kotlin-compiler-1.3.72/kotlinc/lib/kotlin-script-runtime.jar"; private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar"; private static final AndroidApiLevel DEFAULT_MIN_SDK = AndroidApiLevel.I; @@ -280,10 +271,18 @@ return compareTo(other) > 0; } + public boolean isNewerThanOrEqual(Version other) { + return compareTo(other) >= 0; + } + public boolean isAtLeast(Version other) { return compareTo(other) >= 0; } + public boolean isOlderThan(Version other) { + return compareTo(other) < 0; + } + public boolean isOlderThanOrEqual(Version other) { return compareTo(other) <= 0; } @@ -841,16 +840,22 @@ throw new Unreachable("Unable to find a most recent android.jar"); } - public static Path getKotlinStdlibJar() { - Path path = Paths.get(KT_STDLIB); - assert Files.exists(path) : "Expected kotlin stdlib jar"; - return path; + public static Path getKotlinStdlibJar(KotlinCompiler kotlinc) { + Path stdLib = kotlinc.getFolder().resolve("kotlin-stdlib.jar"); + assert Files.exists(stdLib) : "Expected kotlin stdlib jar"; + return stdLib; } - public static Path getKotlinReflectJar() { - Path path = Paths.get(KT_REFLECT); - assert Files.exists(path) : "Expected kotlin reflect jar"; - return path; + public static Path getKotlinReflectJar(KotlinCompiler kotlinc) { + Path reflectJar = kotlinc.getFolder().resolve("kotlin-reflect.jar"); + assert Files.exists(reflectJar) : "Expected kotlin reflect jar"; + return reflectJar; + } + + public static Path getKotlinScriptRuntime(KotlinCompiler kotlinc) { + Path reflectJar = kotlinc.getFolder().resolve("kotlin-script-runtime.jar"); + assert Files.exists(reflectJar) : "Expected kotlin script runtime jar"; + return reflectJar; } public static Path getJdwpTestsCfJarPath(AndroidApiLevel minSdk) { @@ -1355,41 +1360,6 @@ return runJava(runtime, ImmutableList.of(), classpath, args); } - @Deprecated - public static ProcessResult runKotlinc( - List<Path> classPaths, - Path directoryToCompileInto, - List<String> extraOptions, - Path... filesToCompile) - throws IOException { - List<String> cmdline = new ArrayList<>(Arrays.asList(getJavaExecutable())); - cmdline.add("-jar"); - cmdline.add(KT_PRELOADER); - cmdline.add("org.jetbrains.kotlin.preloading.Preloader"); - cmdline.add("-cp"); - cmdline.add(KT_COMPILER); - cmdline.add(K2JVMCompiler); - String[] strings = Arrays.stream(filesToCompile).map(Path::toString).toArray(String[]::new); - Collections.addAll(cmdline, strings); - cmdline.add("-d"); - cmdline.add(directoryToCompileInto.toString()); - List<String> cp = classPaths == null ? null - : classPaths.stream().map(Path::toString).collect(Collectors.toList()); - if (cp != null) { - cmdline.add("-cp"); - if (isWindows()) { - cmdline.add(String.join(";", cp)); - } else { - cmdline.add(String.join(":", cp)); - } - } - if (extraOptions != null) { - cmdline.addAll(extraOptions); - } - ProcessBuilder builder = new ProcessBuilder(cmdline); - return ToolHelper.runProcess(builder); - } - public static ProcessResult runJava( CfRuntime runtime, List<String> vmArgs, List<Path> classpath, String... args) throws IOException { @@ -2160,6 +2130,14 @@ } } + public static KotlinCompiler getKotlinC_1_3_72() { + return new KotlinCompiler("kotlin-compiler-1.3.72"); + } + + public static KotlinCompiler[] getKotlinCompilers() { + return new KotlinCompiler[] {getKotlinC_1_3_72()}; + } + public static void disassemble(AndroidApp app, PrintStream ps) throws IOException, ExecutionException { DexApplication application =
diff --git a/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java b/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java index 524cbfa..607f96d 100644 --- a/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java +++ b/src/test/java/com/android/tools/r8/annotations/SourceDebugExtensionTest.java
@@ -4,16 +4,16 @@ package com.android.tools.r8.annotations; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; import com.android.tools.r8.CompilationFailedException; import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.TestRuntime; import com.android.tools.r8.TestRuntime.CfRuntime; import com.android.tools.r8.ToolHelper; @@ -25,6 +25,7 @@ import com.android.tools.r8.utils.codeinspector.CodeInspector; import java.io.IOException; import java.nio.file.Path; +import java.util.List; import java.util.concurrent.ExecutionException; import org.junit.Test; import org.junit.runner.RunWith; @@ -35,14 +36,17 @@ public class SourceDebugExtensionTest extends TestBase { private final TestParameters parameters; + private final KotlinCompiler kotlinCompiler; - @Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + @Parameters(name = "{0}, kotlinc: {1}") + public static List<Object[]> data() { + return buildParameters( + getTestParameters().withAllRuntimesAndApiLevels().build(), getKotlinCompilers()); } - public SourceDebugExtensionTest(TestParameters parameters) { + public SourceDebugExtensionTest(TestParameters parameters, KotlinCompiler kotlinCompiler) { this.parameters = parameters; + this.kotlinCompiler = kotlinCompiler; } @Test @@ -50,7 +54,7 @@ CfRuntime cfRuntime = parameters.isCfRuntime() ? parameters.getRuntime().asCf() : TestRuntime.getCheckedInJdk9(); Path kotlinSources = - kotlinc(cfRuntime, getStaticTemp(), KOTLINC, KotlinTargetVersion.JAVA_8) + kotlinc(cfRuntime, getStaticTemp(), kotlinCompiler, KotlinTargetVersion.JAVA_8) .addSourceFiles( getFilesInTestFolderRelativeToClass( KotlinInlineFunctionRetraceTest.class, "kt", ".kt")) @@ -58,7 +62,7 @@ CodeInspector kotlinInspector = new CodeInspector(kotlinSources); inspectSourceDebugExtension(kotlinInspector); testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinCompiler)) .addProgramFiles(kotlinSources) .addKeepAttributes(ProguardKeepAttributes.SOURCE_DEBUG_EXTENSION) .addKeepAllClassesRule()
diff --git a/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java b/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java index 693eced..7aa4205 100644 --- a/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java +++ b/src/test/java/com/android/tools/r8/cf/bootstrap/KotlinCompilerTreeShakingTest.java
@@ -3,13 +3,12 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.cf.bootstrap; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static org.junit.Assert.assertTrue; import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.internal.CompilationTestBase; @@ -38,27 +37,29 @@ "Hello.kt"); private static final int MAX_SIZE = (int) (31361268 * 0.4); - private TestParameters parameters; + private final TestParameters parameters; + private final KotlinCompiler kotlinCompiler; - @Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withCfRuntimes().build(); + @Parameters(name = "{0}, kotlinc: {1}") + public static List<Object[]> data() { + return buildParameters(getTestParameters().withCfRuntimes().build(), getKotlinCompilers()); } - public KotlinCompilerTreeShakingTest(TestParameters parameters) { + public KotlinCompilerTreeShakingTest(TestParameters parameters, KotlinCompiler kotlinCompiler) { this.parameters = parameters; + this.kotlinCompiler = kotlinCompiler; } @Test public void testForRuntime() throws Exception { // Compile Hello.kt and make sure it works as expected. Path classPathBefore = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, KotlinTargetVersion.JAVA_8) + kotlinc(parameters.getRuntime().asCf(), kotlinCompiler, KotlinTargetVersion.JAVA_8) .addSourceFiles(HELLO_KT) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar()) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinCompiler)) .addClasspath(classPathBefore) .run(parameters.getRuntime(), PKG_NAME + ".HelloKt") .assertSuccessWithOutputLines("I'm Woody. Howdy, howdy, howdy."); @@ -69,41 +70,40 @@ + "b/144877828: assertion error in method naming state during interface method renaming; " + "b/144859533: umbrella" ) + @Test public void test() throws Exception { List<Path> libs = ImmutableList.of( - ToolHelper.getKotlinStdlibJar(), - ToolHelper.getKotlinReflectJar(), - Paths.get(ToolHelper.KT_SCRIPT_RT)); + ToolHelper.getKotlinStdlibJar(kotlinCompiler), + ToolHelper.getKotlinReflectJar(kotlinCompiler), + ToolHelper.getKotlinScriptRuntime(kotlinCompiler)); // Process kotlin-compiler.jar. Path r8ProcessedKotlinc = testForR8(parameters.getBackend()) .addLibraryFiles(libs) .addLibraryFiles(ToolHelper.getJava8RuntimeJar()) - .addProgramFiles(Paths.get(ToolHelper.KT_COMPILER)) + .addProgramFiles(kotlinCompiler.getCompiler()) .addKeepAttributes("*Annotation*") .addKeepClassAndMembersRules(ToolHelper.K2JVMCompiler) .addKeepClassAndMembersRules("**.K2JVMCompilerArguments") .addKeepClassAndMembersRules("**.*Argument*") .addKeepClassAndMembersRules("**.Freezable") .addKeepRules( - "-keepclassmembers class * {", - " *** parseCommandLineArguments(...);", - "}" - ) + "-keepclassmembers class * {", " *** parseCommandLineArguments(...);", "}") .addKeepRules( "-keepclassmembers,allowoptimization enum * {", " public static **[] values();", " public static ** valueOf(java.lang.String);", "}") - .addOptionsModification(o -> { - // Ignore com.sun.tools.javac.main.JavaCompiler and others - // Resulting jar may not be able to deal with .java source files, though. - o.ignoreMissingClasses = true; - // b/144861100: invoke-static on interface is allowed up to JDK 8. - o.testing.allowInvokeErrors = true; - }) + .addOptionsModification( + o -> { + // Ignore com.sun.tools.javac.main.JavaCompiler and others + // Resulting jar may not be able to deal with .java source files, though. + o.ignoreMissingClasses = true; + // b/144861100: invoke-static on interface is allowed up to JDK 8. + o.testing.allowInvokeErrors = true; + }) .compile() .writeToZip(); @@ -125,7 +125,7 @@ .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar()) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinCompiler)) .addClasspath(classPathAfter) .run(parameters.getRuntime(), PKG_NAME + ".HelloKt") .assertSuccessWithOutputLines("I'm Woody. Howdy, howdy, howdy.");
diff --git a/src/test/java/com/android/tools/r8/classFiltering/ClassFilteringTest.java b/src/test/java/com/android/tools/r8/classFiltering/ClassFilteringTest.java index 3c6aab6..f8a97c4 100644 --- a/src/test/java/com/android/tools/r8/classFiltering/ClassFilteringTest.java +++ b/src/test/java/com/android/tools/r8/classFiltering/ClassFilteringTest.java
@@ -4,11 +4,8 @@ package com.android.tools.r8.classFiltering; -import com.android.tools.r8.ArchiveProgramResourceProvider; import com.android.tools.r8.CompilationFailedException; -import com.android.tools.r8.ProgramResource; -import com.android.tools.r8.ProgramResourceProvider; -import com.android.tools.r8.ResourceException; +import com.android.tools.r8.OutputMode; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -118,14 +115,16 @@ @Test public void testDexMergingWithChecksumMissing() throws Exception { - // Step #1: Build the dex file seperately as an incremental build tools usually do but this time + // Step #1: Build the dex file separately as an incremental build tools usually do but this time // make one of the dex file missing checksum information. - Path[] dexInput = new Path[] { - buildDex(TestClass.class,true, null), - buildDex(TestClass.Keep.class,true, null), - buildDex(TestClass.Remove.class, false, null)}; + Path[] dexInput = + new Path[] { + buildDex(TestClass.class, true, null), + buildDex(TestClass.Keep.class, true, null), + buildDex(TestClass.Remove.class, false, null) + }; - // Step #2: Now use D8 as a merging tool and verify that the compilation fails as expect. + // Step #2: Now use D8 as a merging tool and verify that the compilation fails as expected. try { testForD8() .setMinApi(parameters.getApiLevel()) @@ -140,10 +139,8 @@ } } - /* @Test - public void testMultidexOutput() - throws CompilationFailedException, IOException, ExecutionException, ResourceException { + public void testDexFilePerClassFilteringOutput() throws Exception { // Step #1: Build the program pretending to be multidex files with DexPerClass. final Path outZip = testForD8() .setMinApi(parameters.getApiLevel()) @@ -153,22 +150,15 @@ .compile() .writeToZip(); - final long crc = ToolHelper.getClassByteCrc(TestClass.Remove.class); - // Step #2: Verify that the checksums are present and filtering is working as expected. - ProgramResourceProvider filter = new ArchiveProvider(outZip) { - @Override - public boolean includeClassWithChecksum(String classDescriptor, Long checksum) { - return !checksum.equals(crc); - } - }; + final long crc = ToolHelper.getClassByteCrc(TestClass.Remove.class); testForD8() - .addProgramResourceProvider(filter) + .addProgramFiles(outZip) + .apply(b -> b.getBuilder().setDexClassChecksumFilter((desc, checksum) -> checksum != crc)) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), TestClass.class) .assertSuccessWithOutput("Keep No Remove "); } - */ @Test public void testLambdaChecksum() throws Exception {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingNonTrivialTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingNonTrivialTest.java index 4d7d7fe..42df5c0 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingNonTrivialTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingNonTrivialTest.java
@@ -23,7 +23,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .addHorizontallyMergedClassesInspectorIf( enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class)) .enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingTest.java index cc092e9..3b0af4f 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/AbstractMethodMergingTest.java
@@ -23,7 +23,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .addHorizontallyMergedClassesInspectorIf( enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class)) .enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptResourceFileContentsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptResourceFileContentsTest.java index 2c4ac9d..db8d4c7 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptResourceFileContentsTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptResourceFileContentsTest.java
@@ -36,7 +36,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .addOptionsModification(options -> options.dataResourceConsumer = dataResourceConsumer) .enableNeverClassInliningAnnotations() .addDataEntryResources(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java index ea50082..0ebbb9d 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/AdaptVerticallyMergedResourceFileContentsTest.java
@@ -35,7 +35,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .addOptionsModification(options -> options.dataResourceConsumer = dataResourceConsumer) .enableNeverClassInliningAnnotations() .addDataEntryResources(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassWithInstanceFieldsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassWithInstanceFieldsTest.java index 25bb4ec..31a49cc 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassWithInstanceFieldsTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassWithInstanceFieldsTest.java
@@ -25,7 +25,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNeverClassInliningAnnotations() .enableInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectCheckCastTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectCheckCastTest.java index 745fc26..bc5380e 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectCheckCastTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectCheckCastTest.java
@@ -24,7 +24,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectInstanceOfTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectInstanceOfTest.java index 6a61d2f..6551ad2 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectInstanceOfTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByDirectInstanceOfTest.java
@@ -24,7 +24,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByIndirectCheckCastToInterfaceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByIndirectCheckCastToInterfaceTest.java index 5eb49b1..6017cbe 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByIndirectCheckCastToInterfaceTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistinguishedByIndirectCheckCastToInterfaceTest.java
@@ -26,7 +26,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNoVerticalClassMergingAnnotations() .enableInliningAnnotations() .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistuingishedByIndirectInstanceOfInterfaceCheckCast.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistuingishedByIndirectInstanceOfInterfaceCheckCast.java index 70e7c0d..cf05da6 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistuingishedByIndirectInstanceOfInterfaceCheckCast.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesDistuingishedByIndirectInstanceOfInterfaceCheckCast.java
@@ -26,7 +26,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNoVerticalClassMergingAnnotations() .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java index 3c9d066..5abdc09 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java
@@ -26,7 +26,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentVisibilityFieldsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentVisibilityFieldsTest.java index 90e407e..68a81b1 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentVisibilityFieldsTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentVisibilityFieldsTest.java
@@ -32,7 +32,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
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 5907c150..9ac569b 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
@@ -42,7 +42,8 @@ .addKeepFeatureMainRule(Feature1Main.class) .addKeepFeatureMainRule(Feature2Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel()) .compile()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java index 2bab975..6714388 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
@@ -25,7 +25,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
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 5ee01b3..1f0e3e6 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
@@ -26,7 +26,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithOverlappingVisibilitiesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithOverlappingVisibilitiesTest.java index 565c1bd..608f6fe 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithOverlappingVisibilitiesTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithOverlappingVisibilitiesTest.java
@@ -29,7 +29,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithStaticFields.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithStaticFields.java index f00e07e..2810ce8 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithStaticFields.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithStaticFields.java
@@ -23,7 +23,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel()) .addHorizontallyMergedClassesInspectorIf(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/CompanionClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/CompanionClassMergingTest.java index b14763f..9aca9fe 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/CompanionClassMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/CompanionClassMergingTest.java
@@ -27,7 +27,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .addOptionsModification(options -> options.enableClassInlining = false) .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
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 9ccc2d4..c03361f 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
@@ -25,7 +25,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java index 2383a03..935e75e 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorCantInlineTest.java
@@ -27,7 +27,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingAfterUnusedArgumentRemovalTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingAfterUnusedArgumentRemovalTest.java index 32f7454..d38efb3 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingAfterUnusedArgumentRemovalTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingAfterUnusedArgumentRemovalTest.java
@@ -22,7 +22,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel()) .addHorizontallyMergedClassesInspectorIf(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java index 185e090..8936dc1 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingOverlapTest.java
@@ -32,7 +32,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
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 f658909..f878fd5 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
@@ -33,7 +33,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .addHorizontallyMergedClassesInspectorIf( enableHorizontalClassMerging, inspector ->
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTest.java index b1d77c2..21c3486 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTest.java
@@ -23,7 +23,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java index f6def29..50da81e 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingTrivialOverlapTest.java
@@ -32,7 +32,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithArgumentsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithArgumentsTest.java index a4a0fd9..7891de2 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithArgumentsTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ConstructorMergingWithArgumentsTest.java
@@ -26,7 +26,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/DistinguishExceptionClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/DistinguishExceptionClassesTest.java index c751735..749ae58 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/DistinguishExceptionClassesTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/DistinguishExceptionClassesTest.java
@@ -22,7 +22,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) .assertSuccessWithOutputLines("test success")
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 4cdca16..5d63188 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
@@ -25,7 +25,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel()) .addHorizontallyMergedClassesInspectorIf(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/FieldTypeMergedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/FieldTypeMergedTest.java index 616c88a..58434a9 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/FieldTypeMergedTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/FieldTypeMergedTest.java
@@ -29,7 +29,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/GenericStaticFieldTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/GenericStaticFieldTest.java index 9b86b43..af72afe 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/GenericStaticFieldTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/GenericStaticFieldTest.java
@@ -20,7 +20,8 @@ .addKeepMainRule(Main.class) .addKeepRules("-keepattributes Signatures") .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel()) // .addHorizontallyMergedClassesInspectorIf(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/IdenticalFieldMembersTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/IdenticalFieldMembersTest.java index 6968e1a..9dae7a2 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/IdenticalFieldMembersTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/IdenticalFieldMembersTest.java
@@ -23,7 +23,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java index 9bdaca6..b069bb1 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritInterfaceWithDefaultTest.java
@@ -30,7 +30,8 @@ .addKeepMainRule(Main.class) .allowStdoutMessages() .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritOverrideInterfaceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritOverrideInterfaceTest.java index cedee87..8c7644d 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritOverrideInterfaceTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritOverrideInterfaceTest.java
@@ -24,7 +24,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNoVerticalClassMergingAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritsFromLibraryClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritsFromLibraryClassTest.java index cfafb12..575c2cd 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritsFromLibraryClassTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InheritsFromLibraryClassTest.java
@@ -30,7 +30,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
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 075448a..98e74e4 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
@@ -22,7 +22,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNeverClassInliningAnnotations() .addKeepAttributes("InnerClasses", "EnclosingMethod") .setMinApi(parameters.getApiLevel())
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 a27fdbd..59e77dd 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
@@ -36,7 +36,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(TestClass.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .addHorizontallyMergedClassesInspector(
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java new file mode 100644 index 0000000..adabb3e --- /dev/null +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/JavaLambdaMergingTest.java
@@ -0,0 +1,91 @@ +// 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.classmerging.horizontal; + +import static com.android.tools.r8.ir.desugar.LambdaRewriter.LAMBDA_CLASS_NAME_PREFIX; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.utils.codeinspector.VerticallyMergedClassesInspector; +import java.util.Set; +import java.util.stream.Collectors; +import org.junit.Test; + +public class JavaLambdaMergingTest extends HorizontalClassMergingTestBase { + + public JavaLambdaMergingTest(TestParameters parameters, boolean enableHorizontalClassMerging) { + super(parameters, enableHorizontalClassMerging); + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .addOptionsModification( + options -> { + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging); + assertFalse(options.horizontalClassMergerOptions().isJavaLambdaMergingEnabled()); + options.horizontalClassMergerOptions().enableJavaLambdaMerging(); + }) + .addHorizontallyMergedClassesInspectorIf( + enableHorizontalClassMerging && parameters.isDexRuntime(), + inspector -> { + Set<DexType> lambdaSources = + inspector.getSources().stream() + .filter(x -> x.toSourceString().contains(LAMBDA_CLASS_NAME_PREFIX)) + .collect(Collectors.toSet()); + assertEquals(3, lambdaSources.size()); + DexType firstTarget = inspector.getTarget(lambdaSources.iterator().next()); + for (DexType lambdaSource : lambdaSources) { + assertTrue( + inspector + .getTarget(lambdaSource) + .toSourceString() + .contains(LAMBDA_CLASS_NAME_PREFIX)); + assertEquals(firstTarget, inspector.getTarget(lambdaSource)); + } + }) + .addVerticallyMergedClassesInspector( + VerticallyMergedClassesInspector::assertNoClassesMerged) + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("Hello world!"); + } + + public static class Main { + + public static void main(String[] args) { + HelloGreeter helloGreeter = + System.currentTimeMillis() > 0 + ? () -> System.out.print("Hello") + : () -> { + throw new RuntimeException(); + }; + WorldGreeter worldGreeter = + System.currentTimeMillis() > 0 + ? () -> System.out.println(" world!") + : () -> { + throw new RuntimeException(); + }; + helloGreeter.hello(); + worldGreeter.world(); + } + } + + interface HelloGreeter { + + void hello(); + } + + interface WorldGreeter { + + void world(); + } +}
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 847db55..a553abe 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
@@ -26,7 +26,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .addOptionsModification(options -> options.testing.verificationSizeLimitInBytesOverride = 4) .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergeNonFinalAndFinalClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergeNonFinalAndFinalClassTest.java index a5eb885..c8a36b6 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergeNonFinalAndFinalClassTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergeNonFinalAndFinalClassTest.java
@@ -25,7 +25,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .addHorizontallyMergedClassesInspectorIf( enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class)) .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergePackagePrivateWithPublicClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergePackagePrivateWithPublicClassTest.java index a1c8faa..04d69b1d 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergePackagePrivateWithPublicClassTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergePackagePrivateWithPublicClassTest.java
@@ -27,7 +27,8 @@ PackagePrivateClassRunner.class, PackagePrivateClassRunner.getPrivateClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java index 2256e5d..f913560 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorForwardingTest.java
@@ -32,7 +32,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorStackTraceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorStackTraceTest.java index fc4f076..a11a7d6 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorStackTraceTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedConstructorStackTraceTest.java
@@ -46,7 +46,8 @@ .addKeepAttributeLineNumberTable() .addKeepAttributeSourceFile() .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNoVerticalClassMergingAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel()) @@ -63,7 +64,9 @@ 2, StackTraceLine.builder() .setClassName(A.class.getTypeName()) - .setMethodName("<init>") + // TODO(b/124483578): The synthetic method should not be part of the + // retraced stack trace. + .setMethodName("$r8$init$bridge") .setFileName(getClass().getSimpleName() + ".java") .setLineNumber(0) .build())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedVirtualMethodStackTraceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedVirtualMethodStackTraceTest.java index 00d6e59..8c9d11d 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedVirtualMethodStackTraceTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergedVirtualMethodStackTraceTest.java
@@ -46,7 +46,8 @@ .addKeepAttributeSourceFile() .addDontWarn(C.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel()) @@ -64,7 +65,6 @@ StackTrace expectedStackTraceWithMergedMethod = StackTrace.builder() .add(expectedStackTrace) - .add( 1, StackTraceLine.builder()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java index d25d516..34972f6 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/MergingProducesFieldCollisionTest.java
@@ -31,7 +31,8 @@ .addProgramClassFileData(transformedC) .addProgramClasses(Parent.class, A.class, B.class, Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassTest.java index 5edc29f..b416826 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NestClassTest.java
@@ -58,7 +58,8 @@ .addKeepMainRule(examplesTypeName(BasicNestHostHorizontalClassMerging.class)) .addExamplesProgramFiles(R.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .compile()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesWithNonAbstractClassesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesWithNonAbstractClassesTest.java index 2bc2c97..5566f45 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesWithNonAbstractClassesTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoAbstractClassesWithNonAbstractClassesTest.java
@@ -26,7 +26,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoClassesOrMembersWithAnnotationsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoClassesOrMembersWithAnnotationsTest.java index d17fa75..74d0ad6 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NoClassesOrMembersWithAnnotationsTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NoClassesOrMembersWithAnnotationsTest.java
@@ -31,7 +31,8 @@ .addKeepMainRule(Main.class) .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNeverClassInliningAnnotations() .enableInliningAnnotations() .setMinApi(parameters.getApiLevel())
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 dc513c3..5c0e207 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
@@ -24,7 +24,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNoHorizontalClassMergingAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java index e357250..671f740 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessOnMergedClassTest.java
@@ -26,7 +26,8 @@ .addInnerClasses(NonReboundFieldAccessOnMergedClassTestClasses.class) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .addHorizontallyMergedClassesInspector( inspector -> { if (enableHorizontalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java index 3afcfb2..b6183a3 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/NonReboundFieldAccessWithMergedTypeTest.java
@@ -27,7 +27,8 @@ .addInnerClasses(NonReboundFieldAccessWithMergedTypeTestClasses.class) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .addHorizontallyMergedClassesInspector( inspector -> { if (enableHorizontalClassMerging) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/OverlappingConstructorsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/OverlappingConstructorsTest.java index fbd587d..c97e384 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/OverlappingConstructorsTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/OverlappingConstructorsTest.java
@@ -25,7 +25,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class)
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 a1b0951..2732240 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
@@ -28,7 +28,8 @@ .addProgramClasses(B.class) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .allowAccessModification(false) .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 cdaeee7..4d7c0c4 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
@@ -27,7 +27,8 @@ .addProgramClasses(D.class) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .allowAccessModification(false) .enableInliningAnnotations() .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberReferenceTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberReferenceTest.java index 4a22fe3..2f01f90 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberReferenceTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassMemberReferenceTest.java
@@ -28,7 +28,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .noMinification() .enableInliningAnnotations() .enableNeverClassInliningAnnotations()
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 444d573..1e09a5a 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
@@ -23,7 +23,8 @@ .addKeepMainRule(Main.class) .addKeepRules("-keepclassmembers class " + B.class.getTypeName() + " { void foo(); }") .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassTest.java index a2b36f3..0ba06e6 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PinnedClassTest.java
@@ -23,7 +23,8 @@ .addKeepMainRule(Main.class) .addKeepClassRules(B.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexListTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexListTest.java index 4a50200..e3e1b85 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexListTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexListTest.java
@@ -48,7 +48,7 @@ .addMainDexListClasses(A.class, Main.class) .addOptionsModification( options -> { - options.enableHorizontalClassMerging = enableHorizontalClassMerging; + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging); options.minimalMainDex = true; }) .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexTracingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexTracingTest.java index 9e81bc6..6a31a5e 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexTracingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PreventMergeMainDexTracingTest.java
@@ -48,7 +48,7 @@ .addMainDexClassRules(Main.class) .addOptionsModification( options -> { - options.enableHorizontalClassMerging = enableHorizontalClassMerging; + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging); options.minimalMainDex = true; }) .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndInterfaceMethodCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndInterfaceMethodCollisionTest.java index af37199..4553469 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndInterfaceMethodCollisionTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndInterfaceMethodCollisionTest.java
@@ -25,7 +25,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .addHorizontallyMergedClassesInspector( HorizontallyMergedClassesInspector::assertNoClassesMerged) .enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndStaticMethodCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndStaticMethodCollisionTest.java index df03d8a..3c7b799 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndStaticMethodCollisionTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/PrivateAndStaticMethodCollisionTest.java
@@ -22,7 +22,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .addHorizontallyMergedClassesInspectorIf( enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class)) .enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ReferencedInAnnotationTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ReferencedInAnnotationTest.java index 620f948..385d782 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ReferencedInAnnotationTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ReferencedInAnnotationTest.java
@@ -38,7 +38,8 @@ .addKeepMainRule(TestClass.class) .addKeepClassAndMembersRules(Annotation.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .addKeepRuntimeVisibleAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapFieldTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapFieldTest.java index 33c8a85..168d6ee 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapFieldTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapFieldTest.java
@@ -24,7 +24,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java index a531860..c640ffc 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/RemapMethodTest.java
@@ -24,7 +24,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ServiceLoaderParentTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ServiceLoaderParentTest.java index 4e89fb8..5eab9c9 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ServiceLoaderParentTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ServiceLoaderParentTest.java
@@ -38,7 +38,8 @@ Origin.unknown())) .enableNoVerticalClassMergingAnnotations() .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) .assertSuccess()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ServiceLoaderTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ServiceLoaderTest.java index ee6dc18..f9ae4bd 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ServiceLoaderTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ServiceLoaderTest.java
@@ -36,7 +36,8 @@ "META-INF/services/" + A.class.getTypeName(), Origin.unknown())) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class) .assertSuccess()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndInterfaceMethodCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndInterfaceMethodCollisionTest.java index ebbfd86..e76dd447 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndInterfaceMethodCollisionTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndInterfaceMethodCollisionTest.java
@@ -25,7 +25,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .addHorizontallyMergedClassesInspector( HorizontallyMergedClassesInspector::assertNoClassesMerged) .enableInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndVirtualMethodCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndVirtualMethodCollisionTest.java index edca180..35a15e5 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndVirtualMethodCollisionTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/StaticAndVirtualMethodCollisionTest.java
@@ -23,7 +23,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .addHorizontallyMergedClassesInspectorIf( enableHorizontalClassMerging, inspector -> inspector.assertMergedInto(B.class, A.class)) .enableInliningAnnotations() @@ -36,10 +37,10 @@ static class Main { public static void main(String[] args) { - new A().foo(); + A.foo(); new A().bar(); new B().foo(); - new B().bar(); + B.bar(); } }
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/SuperConstructorCallsVirtualMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/SuperConstructorCallsVirtualMethodTest.java index 119913b..ecbd84b 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/SuperConstructorCallsVirtualMethodTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/SuperConstructorCallsVirtualMethodTest.java
@@ -25,7 +25,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
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 e5c77a3..6f458b7 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
@@ -26,7 +26,7 @@ .addKeepMainRule(Main.class) .addOptionsModification( options -> { - options.enableHorizontalClassMerging = enableHorizontalClassMerging; + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging); }) .enableInliningAnnotations() .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticConstructorArgumentsMerged.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticConstructorArgumentsMerged.java index 59ea0b2..6567764 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticConstructorArgumentsMerged.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/SyntheticConstructorArgumentsMerged.java
@@ -26,7 +26,7 @@ .addKeepMainRule(Main.class) .addOptionsModification( options -> { - options.enableHorizontalClassMerging = enableHorizontalClassMerging; + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging); }) .enableInliningAnnotations() .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerCollisionTest.java index b603f66..97e41db 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerCollisionTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerCollisionTest.java
@@ -26,7 +26,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoVerticalClassMergingAnnotations()
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 a5c5555..c7a7d9c 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
@@ -30,7 +30,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java index f1301a0..946e113 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceCollisionTest.java
@@ -36,7 +36,8 @@ .addKeepMainRule(Main.class) .noMinification() .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java index 2f6895a..1d3290a 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceFixedCollisionTest.java
@@ -38,7 +38,8 @@ .addKeepMainRule(Main.class) .noMinification() .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceImplementedByParentTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceImplementedByParentTest.java index b6a320c..afee32c 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceImplementedByParentTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerInterfaceImplementedByParentTest.java
@@ -34,7 +34,8 @@ .addKeepMainRule(Main.class) .noMinification() .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java index aad0efa..b68f291 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/TreeFixerSubClassCollisionTest.java
@@ -29,7 +29,8 @@ .addKeepMainRule(Main.class) .noMinification() .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticalMergingPreoptimizedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticalMergingPreoptimizedTest.java index 7731d5e..b1c0b4a 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticalMergingPreoptimizedTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticalMergingPreoptimizedTest.java
@@ -28,7 +28,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByCheckCastTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByCheckCastTest.java index 9ecbf5a..366800a 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByCheckCastTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByCheckCastTest.java
@@ -23,7 +23,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class)
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByInstanceOfTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByInstanceOfTest.java index bba42bb..c8ad21d 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByInstanceOfTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VerticallyMergedClassDistinguishedByInstanceOfTest.java
@@ -23,7 +23,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Main.class)
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 e40ff51..a40b387 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
@@ -27,7 +27,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableNoHorizontalClassMergingAnnotations() .enableInliningAnnotations() .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfFinalAndNonFinalMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfFinalAndNonFinalMethodTest.java index f9eff69..6399f3e 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfFinalAndNonFinalMethodTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfFinalAndNonFinalMethodTest.java
@@ -28,7 +28,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(TestClass.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfPublicizedMethodsTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfPublicizedMethodsTest.java index 8178138..5e23840 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfPublicizedMethodsTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/VirtualMethodMergingOfPublicizedMethodsTest.java
@@ -25,7 +25,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(TestClass.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .allowAccessModification() .enableInliningAnnotations() .enableNeverClassInliningAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/NotOverlappingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/NotOverlappingTest.java index 8742e67..e405aa9 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/NotOverlappingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/NotOverlappingTest.java
@@ -23,7 +23,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java index 87f4dc3..e456600 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideAbstractMethodWithDefaultTest.java
@@ -25,7 +25,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNoVerticalClassMergingAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java index dd8183d..0e3a098 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultMethodTest.java
@@ -26,7 +26,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNoVerticalClassMergingAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java index bd41f8f..fa2dcb2 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideDefaultOnSuperMethodTest.java
@@ -27,7 +27,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNoUnusedInterfaceRemovalAnnotations() .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java index fe0bc23..526aa00 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
@@ -25,7 +25,8 @@ .addInnerClasses(getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoVerticalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideParentCollisionTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideParentCollisionTest.java index 94b643a..639cf19 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideParentCollisionTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideParentCollisionTest.java
@@ -27,7 +27,8 @@ .addInnerClasses(this.getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/SuperMethodMergedTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/SuperMethodMergedTest.java index 228223f..44973bc 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/SuperMethodMergedTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/SuperMethodMergedTest.java
@@ -26,7 +26,8 @@ .addInnerClasses(this.getClass()) .addKeepMainRule(Main.class) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .enableNeverClassInliningAnnotations() .enableNoVerticalClassMergingAnnotations()
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 2fb36cd..8dc4e01 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
@@ -253,6 +253,7 @@ testForR8(parameters.getBackend()) .addKeepRules(getProguardConfig(EXAMPLE_KEEP)) .addOptionsModification(this::configure) + .addOptionsModification(options -> options.enableValuePropagation = false) .addOptionsModification( options -> options.testing.validInliningReasons = ImmutableSet.of(Reason.FORCE)) @@ -276,10 +277,11 @@ assertThat(clazzSubject.field("java.lang.String", "name" + suffix), isPresent()); assertThat(clazzSubject.field("java.lang.String", "name" + suffix + "2"), isPresent()); - // The direct method "constructor$classmerging$ConflictInGeneratedNameTest$A" is processed after - // the method "<init>" is renamed to exactly that name. Therefore the conflict should have been - // resolved by appending [suffix] to it. - assertThat(clazzSubject.method("void", "constructor" + suffix + suffix, EMPTY), isPresent()); + // The direct method "$r8$constructor$classmerging$ConflictInGeneratedNameTest$A" is processed + // after the method "<init>" is renamed to exactly that name. Therefore the conflict should have + // been resolved by appending [suffix] to it. + assertThat( + clazzSubject.method("void", "$r8$constructor" + suffix + suffix, EMPTY), isPresent()); // There should be two foo's. assertThat(clazzSubject.method("void", "foo", EMPTY), isPresent());
diff --git a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java index ae1c81c..deb97d8 100644 --- a/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java +++ b/src/test/java/com/android/tools/r8/debug/KotlinStdLibCompilationTest.java
@@ -3,16 +3,18 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.debug; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationFailedException; import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestDiagnosticMessages; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersBuilder; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper; +import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -22,21 +24,25 @@ public class KotlinStdLibCompilationTest extends TestBase { private final TestParameters parameters; + private final KotlinCompiler kotlinc; - @Parameters(name = "{0}") - public static TestParametersCollection setup() { - return TestParametersBuilder.builder().withAllRuntimes().withAllApiLevels().build(); + @Parameters(name = "{0}, kotlinc: {1}") + public static List<Object[]> setup() { + return buildParameters( + TestParametersBuilder.builder().withAllRuntimes().withAllApiLevels().build(), + getKotlinCompilers()); } - public KotlinStdLibCompilationTest(TestParameters parameters) { + public KotlinStdLibCompilationTest(TestParameters parameters, KotlinCompiler kotlinc) { this.parameters = parameters; + this.kotlinc = kotlinc; } @Test public void testD8() throws CompilationFailedException { assumeTrue(parameters.isDexRuntime()); testForD8() - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .setMinApi(parameters.getApiLevel()) .compileWithExpectedDiagnostics(TestDiagnosticMessages::assertNoMessages); } @@ -44,7 +50,7 @@ @Test public void testR8() throws CompilationFailedException { testForR8(parameters.getBackend()) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .noMinification() .noTreeShaking() .addKeepAllAttributes()
diff --git a/src/test/java/com/android/tools/r8/debuginfo/CannonicalizeWithInline.java b/src/test/java/com/android/tools/r8/debuginfo/CanonicalizeWithInline.java similarity index 61% rename from src/test/java/com/android/tools/r8/debuginfo/CannonicalizeWithInline.java rename to src/test/java/com/android/tools/r8/debuginfo/CanonicalizeWithInline.java index ff58696..395f29f 100644 --- a/src/test/java/com/android/tools/r8/debuginfo/CannonicalizeWithInline.java +++ b/src/test/java/com/android/tools/r8/debuginfo/CanonicalizeWithInline.java
@@ -3,6 +3,8 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.debuginfo; +import com.android.tools.r8.AssumeMayHaveSideEffects; +import com.android.tools.r8.NeverInline; import com.android.tools.r8.OutputMode; import com.android.tools.r8.R8TestCompileResult; import com.android.tools.r8.TestBase; @@ -15,7 +17,7 @@ import org.junit.Assert; import org.junit.Test; -public class CannonicalizeWithInline extends TestBase { +public class CanonicalizeWithInline extends TestBase { private int getNumberOfDebugInfos(Path file) throws IOException { DexSection[] dexSections = DexParser.parseMapFrom(file); @@ -28,25 +30,23 @@ } @Test - public void testCannonicalize() throws Exception { - Class clazzA = ClassA.class; - Class clazzB = ClassB.class; + public void testCanonicalize() throws Exception { + Class<?> clazzA = ClassA.class; + Class<?> clazzB = ClassB.class; - R8TestCompileResult result = testForR8(Backend.DEX) - .addProgramClasses(clazzA, clazzB) - .addKeepRules( - "-keepattributes SourceFile,LineNumberTable", - "-keep class ** {\n" + - "public void call(int);\n" + - "}" - ) - // String concatenation optimization will remove dead builders in foobar. - .addOptionsModification(o -> o.enableStringConcatenationOptimization = false) - .compile(); + R8TestCompileResult result = + testForR8(Backend.DEX) + .addProgramClasses(clazzA, clazzB) + .addKeepRules( + "-keepattributes SourceFile,LineNumberTable", + "-keep class ** {\n" + "public void call(int);\n" + "}") + .enableInliningAnnotations() + .enableSideEffectAnnotations() + .compile(); Path classesPath = temp.getRoot().toPath(); result.app.write(classesPath, OutputMode.DexIndexed); - int numberOfDebugInfos = getNumberOfDebugInfos( - Paths.get(temp.getRoot().getCanonicalPath(), "classes.dex")); + int numberOfDebugInfos = + getNumberOfDebugInfos(Paths.get(temp.getRoot().getCanonicalPath(), "classes.dex")); Assert.assertEquals(1, numberOfDebugInfos); } @@ -57,11 +57,17 @@ public static class ClassA { public void call(int a) { - foobar(a); + foobar(a); } private String foobar(int a) { - String s = "aFoobar" + a; + return doSomething(a); + } + + @AssumeMayHaveSideEffects + @NeverInline + private String doSomething(int a) { + String s = "bFoobar" + a; return s; } } @@ -73,6 +79,12 @@ } private String foobar(int a) { + return doSomething(a); + } + + @AssumeMayHaveSideEffects + @NeverInline + private String doSomething(int a) { String s = "bFoobar" + a; return s; }
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ImplementedInterfacesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ImplementedInterfacesTest.java new file mode 100644 index 0000000..b5ab3b5 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ImplementedInterfacesTest.java
@@ -0,0 +1,85 @@ +// 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.desugar.desugaredlibrary; + +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertTrue; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.Spliterator; +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 ImplementedInterfacesTest extends DesugaredLibraryTestBase { + + private final TestParameters parameters; + private final boolean canUseDefaultAndStaticInterfaceMethods; + + @Parameters(name = "{0}") + public static List<Object[]> data() { + return buildParameters( + getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build()); + } + + public ImplementedInterfacesTest(TestParameters parameters) { + this.parameters = parameters; + this.canUseDefaultAndStaticInterfaceMethods = + parameters + .getApiLevel() + .isGreaterThanOrEqualTo(apiLevelWithDefaultInterfaceMethodsSupport()); + } + + private String desugaredJavaTypeNameFor(Class<?> clazz) { + return clazz.getTypeName().replace("java.", "j$."); + } + + private void checkInterfaces(CodeInspector inspector) { + ClassSubject clazz = inspector.clazz(MultipleInterfaces.class); + assertThat(clazz, isPresent()); + assertTrue(clazz.isImplementing(Serializable.class)); + assertTrue(clazz.isImplementing(Set.class)); + assertTrue(clazz.isImplementing(List.class)); + assertFalse(clazz.isImplementing(Collection.class)); + assertFalse(clazz.isImplementing(Iterable.class)); + if (!canUseDefaultAndStaticInterfaceMethods) { + assertFalse(clazz.isImplementing(desugaredJavaTypeNameFor(Serializable.class))); + assertTrue(clazz.isImplementing(desugaredJavaTypeNameFor(Set.class))); + assertTrue(clazz.isImplementing(desugaredJavaTypeNameFor(List.class))); + assertFalse(clazz.isImplementing(desugaredJavaTypeNameFor(Collection.class))); + assertFalse(clazz.isImplementing(desugaredJavaTypeNameFor(Iterable.class))); + } + } + + @Test + public void testInterfaces() throws Exception { + KeepRuleConsumer keepRuleConsumer = createKeepRuleConsumer(parameters); + testForD8(parameters.getBackend()) + .addInnerClasses(ImplementedInterfacesTest.class) + .setMinApi(parameters.getApiLevel()) + .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer) + .compile() + .inspect(this::checkInterfaces); + } + + abstract static class MultipleInterfaces<T> implements List<T>, Serializable, Set<T> { + + // Disambiguate between default methods List.spliterator() and Set.spliterator() + @Override + public Spliterator<T> spliterator() { + return Set.super.spliterator(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java index ebf503a..89748a7 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/kotlin/KotlinMetadataTest.java
@@ -4,7 +4,8 @@ package com.android.tools.r8.desugar.desugaredlibrary.kotlin; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.KotlinTestBase.getCompileMemoizer; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; @@ -15,6 +16,8 @@ import com.android.tools.r8.D8TestRunResult; import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; +import com.android.tools.r8.KotlinTestBase.KotlinCompileMemoizer; import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.R8TestCompileResult; import com.android.tools.r8.R8TestRunResult; @@ -24,20 +27,15 @@ import com.android.tools.r8.desugar.desugaredlibrary.DesugaredLibraryTestBase; import com.android.tools.r8.kotlin.KotlinMetadataWriter; import com.android.tools.r8.shaking.ProguardKeepAttributes; -import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.FileUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import java.io.File; -import java.nio.file.Path; import java.nio.file.Paths; -import java.util.HashMap; import java.util.List; -import java.util.Map; import kotlinx.metadata.jvm.KotlinClassMetadata; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -50,50 +48,44 @@ private final TestParameters parameters; private final boolean shrinkDesugaredLibrary; private final KotlinTargetVersion targetVersion; + private final KotlinCompiler kotlinCompiler; private static final String EXPECTED_OUTPUT = "Wuhuu, my special day is: 1997-8-29-2-14"; - @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}, target: {2}") + @Parameters(name = "{1}, shrinkDesugaredLibrary: {0}, target: {2}, kotlinc: {3}") public static List<Object[]> data() { return buildParameters( BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build(), - KotlinTargetVersion.values()); + KotlinTargetVersion.values(), + getKotlinCompilers()); } public KotlinMetadataTest( boolean shrinkDesugaredLibrary, TestParameters parameters, - KotlinTargetVersion targetVersion) { + KotlinTargetVersion targetVersion, + KotlinCompiler kotlinCompiler) { this.shrinkDesugaredLibrary = shrinkDesugaredLibrary; this.parameters = parameters; this.targetVersion = targetVersion; + this.kotlinCompiler = kotlinCompiler; } - private static Map<KotlinTargetVersion, Path> compiledJars = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - compiledJars.put( - targetVersion, - kotlinc(KOTLINC, targetVersion) - .addSourceFiles( - Paths.get( - ToolHelper.TESTS_DIR, - "java", - DescriptorUtils.getBinaryNameFromJavaType(PKG), - "Main" + FileUtils.KT_EXTENSION)) - .compile()); - } - } + private static KotlinCompileMemoizer compiledJars = + getCompileMemoizer( + Paths.get( + ToolHelper.TESTS_DIR, + "java", + DescriptorUtils.getBinaryNameFromJavaType(PKG), + "Main" + FileUtils.KT_EXTENSION)); @Test public void testCf() throws Exception { assumeTrue(parameters.getRuntime().isCf()); testForRuntime(parameters) - .addProgramFiles(compiledJars.get(targetVersion)) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(ToolHelper.getKotlinReflectJar()) + .addProgramFiles(compiledJars.getForConfiguration(kotlinCompiler, targetVersion)) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinCompiler)) + .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinCompiler)) .run(parameters.getRuntime(), PKG + ".MainKt") .assertSuccessWithOutputLines(EXPECTED_OUTPUT); } @@ -105,9 +97,9 @@ final File output = temp.newFile("output.zip"); final D8TestRunResult d8TestRunResult = testForD8() - .addProgramFiles(compiledJars.get(targetVersion)) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(ToolHelper.getKotlinReflectJar()) + .addProgramFiles(compiledJars.getForConfiguration(kotlinCompiler, targetVersion)) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinCompiler)) + .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinCompiler)) .setProgramConsumer(new ArchiveConsumer(output.toPath(), true)) .setMinApi(parameters.getApiLevel()) .enableCoreLibraryDesugaring(parameters.getApiLevel(), keepRuleConsumer) @@ -134,9 +126,9 @@ boolean desugarLibrary = parameters.isDexRuntime() && requiresAnyCoreLibDesugaring(parameters); final R8FullTestBuilder testBuilder = testForR8(parameters.getBackend()) - .addProgramFiles(compiledJars.get(targetVersion)) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(ToolHelper.getKotlinReflectJar()) + .addProgramFiles(compiledJars.getForConfiguration(kotlinCompiler, targetVersion)) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinCompiler)) + .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinCompiler)) .addKeepMainRule(PKG + ".MainKt") .addKeepAllClassesRule() .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
diff --git a/src/test/java/com/android/tools/r8/desugar/records/RecordsAttributeTest.java b/src/test/java/com/android/tools/r8/desugar/records/RecordsAttributeTest.java new file mode 100644 index 0000000..831c426 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/records/RecordsAttributeTest.java
@@ -0,0 +1,87 @@ +// 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.desugar.records; + +import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestRuntime.CfRuntime; +import com.android.tools.r8.examples.jdk15.Records; +import com.android.tools.r8.utils.AndroidApiLevel; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class RecordsAttributeTest extends TestBase { + + private final Backend backend; + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static List<Object[]> data() { + // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15). + return buildParameters( + getTestParameters().withCustomRuntime(CfRuntime.getCheckedInJdk15()).build(), + Backend.values()); + } + + public RecordsAttributeTest(TestParameters parameters, Backend backend) { + this.parameters = parameters; + this.backend = backend; + } + + @Test + public void testJvm() throws Exception { + assumeFalse(parameters.isNoneRuntime()); + assumeTrue(backend == Backend.CF); + testForJvm() + .addRunClasspathFiles(Records.jar()) + .addVmArguments("--enable-preview") + .run(parameters.getRuntime(), Records.Main.typeName()) + .assertSuccessWithOutputLines("Jane Doe", "42"); + } + + @Test + public void testD8() throws Exception { + assertThrows( + CompilationFailedException.class, + () -> { + testForD8(backend) + .addProgramClassFileData(Records.Main.bytes(), Records.Main$Person.bytes()) + .setMinApi(AndroidApiLevel.B) + .compileWithExpectedDiagnostics( + diagnostics -> { + diagnostics.assertErrorThatMatches( + diagnosticMessage(containsString("Records are not supported"))); + }); + }); + } + + @Test + public void testR8() throws Exception { + assertThrows( + CompilationFailedException.class, + () -> { + testForR8(backend) + .addProgramClassFileData(Records.Main.bytes(), Records.Main$Person.bytes()) + .setMinApi(AndroidApiLevel.B) + .addKeepMainRule(Records.Main.typeName()) + .compileWithExpectedDiagnostics( + diagnostics -> { + diagnostics.assertErrorThatMatches( + diagnosticMessage(containsString("Records are not supported"))); + }); + }); + } +}
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java new file mode 100644 index 0000000..48b29e7 --- /dev/null +++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
@@ -0,0 +1,83 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.desugar.sealed; + +import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestRuntime; +import com.android.tools.r8.examples.jdk15.Sealed; +import com.android.tools.r8.utils.AndroidApiLevel; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class SealedAttributeTest extends TestBase { + + private final Backend backend; + + @Parameters(name = "{0}") + public static List<Object[]> data() { + // TODO(b/174431251): This should be replaced with .withCfRuntimes(start = jdk15). + return buildParameters( + getTestParameters().withCustomRuntime(TestRuntime.getCheckedInJdk15()).build(), + Backend.values()); + } + + public SealedAttributeTest(TestParameters parameters, Backend backend) { + this.backend = backend; + } + + @Test + public void testJvm() throws Exception { + assumeTrue(backend == Backend.CF); + testForJvm() + .addRunClasspathFiles(Sealed.jar()) + .addVmArguments("--enable-preview") + .run(TestRuntime.getCheckedInJdk15(), Sealed.Main.typeName()) + .assertSuccessWithOutputLines("R8 compiler", "D8 compiler"); + } + + @Test + public void testD8() throws Exception { + assertThrows( + CompilationFailedException.class, + () -> { + testForD8(backend) + .addProgramFiles(Sealed.jar()) + .setMinApi(AndroidApiLevel.B) + .compileWithExpectedDiagnostics( + diagnostics -> { + diagnostics.assertErrorThatMatches( + diagnosticMessage(containsString("Sealed classes are not supported"))); + }); + }); + } + + @Test + public void testR8() throws Exception { + assertThrows( + CompilationFailedException.class, + () -> { + testForR8(backend) + .addProgramFiles(Sealed.jar()) + .setMinApi(AndroidApiLevel.B) + .addKeepMainRule(Sealed.Main.typeName()) + .compileWithExpectedDiagnostics( + diagnostics -> { + diagnostics.assertErrorThatMatches( + diagnosticMessage(containsString("Sealed classes are not supported"))); + }); + }); + } +}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/EnumWithNonDefaultForwardingConstructorTest.java b/src/test/java/com/android/tools/r8/enumunboxing/EnumWithNonDefaultForwardingConstructorTest.java index 92b0b54..926a7b0 100644 --- a/src/test/java/com/android/tools/r8/enumunboxing/EnumWithNonDefaultForwardingConstructorTest.java +++ b/src/test/java/com/android/tools/r8/enumunboxing/EnumWithNonDefaultForwardingConstructorTest.java
@@ -54,8 +54,7 @@ .setMinApi(parameters.getApiLevel()) .compile() .run(parameters.getRuntime(), TestClass.class) - // TODO(b/160939354): Should succeed with 42. - .assertSuccessWithOutputLines(enableEnumUnboxing ? "0" : "42"); + .assertSuccessWithOutputLines("42"); } private void addProgramClasses(TestBuilder<?, ?> builder) throws Exception {
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java index 1bb9c1e..a4f280c 100644 --- a/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java +++ b/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java
@@ -4,21 +4,20 @@ package com.android.tools.r8.enumunboxing.kotlin; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.KotlinTestBase.getCompileMemoizer; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; +import com.android.tools.r8.KotlinTestBase.KotlinCompileMemoizer; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.enumunboxing.EnumUnboxingTestBase; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.DescriptorUtils; -import java.nio.file.Path; import java.nio.file.Paths; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -31,51 +30,45 @@ private final boolean enumValueOptimization; private final EnumKeepRules enumKeepRules; private final KotlinTargetVersion targetVersion; + private final KotlinCompiler kotlinCompiler; private static final String PKG = SimpleKotlinEnumUnboxingTest.class.getPackage().getName(); - private static Map<KotlinTargetVersion, Path> jars = new HashMap<>(); + private static final KotlinCompileMemoizer jars = + getCompileMemoizer( + Paths.get( + ToolHelper.TESTS_DIR, + "java", + DescriptorUtils.getBinaryNameFromJavaType(PKG), + "Main.kt")); - @Parameters(name = "{0}, valueOpt: {1}, keep: {2}, kotlin targetVersion: {3}") + @Parameters(name = "{0}, valueOpt: {1}, keep: {2}, kotlin targetVersion: {3}, kotlinc: {4}") public static List<Object[]> enumUnboxingTestParameters() { return buildParameters( getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values(), getAllEnumKeepRules(), - KotlinTargetVersion.values()); + KotlinTargetVersion.values(), + getKotlinCompilers()); } public SimpleKotlinEnumUnboxingTest( TestParameters parameters, boolean enumValueOptimization, EnumKeepRules enumKeepRules, - KotlinTargetVersion targetVersion) { + KotlinTargetVersion targetVersion, + KotlinCompiler kotlinCompiler) { this.parameters = parameters; this.enumValueOptimization = enumValueOptimization; this.enumKeepRules = enumKeepRules; this.targetVersion = targetVersion; - } - - @BeforeClass - public static void createLibJar() throws Exception { - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - jars.put( - targetVersion, - kotlinc(KOTLINC, targetVersion) - .addSourceFiles( - Paths.get( - ToolHelper.TESTS_DIR, - "java", - DescriptorUtils.getBinaryNameFromJavaType(PKG), - "Main.kt")) - .compile()); - } + this.kotlinCompiler = kotlinCompiler; } @Test public void testEnumUnboxing() throws Exception { assumeTrue(parameters.isDexRuntime()); testForR8(parameters.getBackend()) - .addProgramFiles(jars.get(targetVersion)) + .addProgramFiles(jars.getForConfiguration(kotlinCompiler, targetVersion)) .addKeepMainRule(PKG + ".MainKt") .addKeepRules(enumKeepRules.getKeepRules()) .addKeepRuntimeVisibleAnnotations()
diff --git a/src/test/java/com/android/tools/r8/examples/JavaExampleClassProxy.java b/src/test/java/com/android/tools/r8/examples/JavaExampleClassProxy.java new file mode 100644 index 0000000..6bbbfd1 --- /dev/null +++ b/src/test/java/com/android/tools/r8/examples/JavaExampleClassProxy.java
@@ -0,0 +1,53 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.examples; + +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.utils.DescriptorUtils; +import com.android.tools.r8.utils.StringUtils; +import com.google.common.io.ByteStreams; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.zip.ZipFile; + +public class JavaExampleClassProxy { + + private final String examplesFolder; + private final String binaryName; + + public JavaExampleClassProxy(String examples, String binaryName) { + this.examplesFolder = examples; + this.binaryName = binaryName; + } + + public static Path examplesJar(String examplesFolder) { + return Paths.get(ToolHelper.BUILD_DIR, "test", examplesFolder + ".jar"); + } + + public byte[] bytes() { + Path examplePath = examplesJar(examplesFolder); + if (!Files.exists(examplePath)) { + throw new RuntimeException( + "Could not find path " + + examplePath + + ". Build " + + examplesFolder + + " by running tools/gradle.py build" + + StringUtils.capitalize(examplesFolder)); + } + try (ZipFile zipFile = new ZipFile(examplePath.toFile())) { + return ByteStreams.toByteArray( + zipFile.getInputStream(zipFile.getEntry(binaryName + ".class"))); + } catch (IOException e) { + throw new RuntimeException("Could not read zip-entry from " + examplePath.toString(), e); + } + } + + public String typeName() { + return DescriptorUtils.getJavaTypeFromBinaryName(binaryName); + } +}
diff --git a/src/test/java/com/android/tools/r8/examples/jdk15/Records.java b/src/test/java/com/android/tools/r8/examples/jdk15/Records.java new file mode 100644 index 0000000..9eafc56 --- /dev/null +++ b/src/test/java/com/android/tools/r8/examples/jdk15/Records.java
@@ -0,0 +1,22 @@ +// 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.examples.jdk15; + +import com.android.tools.r8.examples.JavaExampleClassProxy; +import java.nio.file.Path; + +public class Records { + + private static final String EXAMPLE_FILE = "examplesJava15/records"; + + public static final JavaExampleClassProxy Main = + new JavaExampleClassProxy(EXAMPLE_FILE, "records/Main"); + public static final JavaExampleClassProxy Main$Person = + new JavaExampleClassProxy(EXAMPLE_FILE, "records/Main$Person"); + + public static Path jar() { + return JavaExampleClassProxy.examplesJar(EXAMPLE_FILE); + } +}
diff --git a/src/test/java/com/android/tools/r8/examples/jdk15/Sealed.java b/src/test/java/com/android/tools/r8/examples/jdk15/Sealed.java new file mode 100644 index 0000000..1450ee8 --- /dev/null +++ b/src/test/java/com/android/tools/r8/examples/jdk15/Sealed.java
@@ -0,0 +1,26 @@ +// 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.examples.jdk15; + +import com.android.tools.r8.examples.JavaExampleClassProxy; +import java.nio.file.Path; + +public class Sealed { + + private static final String EXAMPLE_FILE = "examplesJava15/sealed"; + + public static final JavaExampleClassProxy Compiler = + new JavaExampleClassProxy(EXAMPLE_FILE, "sealed/Compiler"); + public static final JavaExampleClassProxy R8Compiler = + new JavaExampleClassProxy(EXAMPLE_FILE, "sealed/R8Compiler"); + public static final JavaExampleClassProxy D8Compiler = + new JavaExampleClassProxy(EXAMPLE_FILE, "sealed/D8Compiler"); + public static final JavaExampleClassProxy Main = + new JavaExampleClassProxy(EXAMPLE_FILE, "sealed/Main"); + + public static Path jar() { + return JavaExampleClassProxy.examplesJar(EXAMPLE_FILE); + } +}
diff --git a/src/test/java/com/android/tools/r8/graph/DexTypeTest.java b/src/test/java/com/android/tools/r8/graph/DexTypeTest.java index 6b2d73e..82ee9e8 100644 --- a/src/test/java/com/android/tools/r8/graph/DexTypeTest.java +++ b/src/test/java/com/android/tools/r8/graph/DexTypeTest.java
@@ -28,7 +28,7 @@ new ApplicationReader( AndroidApp.builder() .addLibraryFiles(ToolHelper.getDefaultAndroidJar()) - .addLibraryFiles(ToolHelper.getKotlinStdlibJar()) + .addLibraryFiles(ToolHelper.getKotlinStdlibJar(ToolHelper.getKotlinC_1_3_72())) .build(), options, Timing.empty())
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/DevirtualizeWithCatchHandlersTest.java b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/DevirtualizeWithCatchHandlersTest.java new file mode 100644 index 0000000..a126f0f --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/DevirtualizeWithCatchHandlersTest.java
@@ -0,0 +1,88 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize.devirtualize; + +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.Parameters; + +@RunWith(Parameterized.class) +public class DevirtualizeWithCatchHandlersTest extends TestBase { + + private final TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return TestBase.getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public DevirtualizeWithCatchHandlersTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .addKeepRules( + // Disable uninstantiated type optimization for m(). + "-keepclassmembers class " + Uninstantiated.class.getTypeName() + " {", + " " + Uninstantiated.class.getTypeName() + " get();", + "}") + .enableInliningAnnotations() + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("A"); + } + + static class Main { + + public static void main(String[] args) { + try { + test(); + } catch (Exception e) { + System.out.println("Dead!"); + } + } + + static synchronized void test() { + I which = System.currentTimeMillis() > 0 ? new A() : Uninstantiated.get(); + which.m(); + } + } + + interface I { + + void m(); + } + + static class A implements I { + + @NeverInline + @Override + public void m() { + System.out.println("A"); + } + } + + static class Uninstantiated implements I { + + static Uninstantiated get() { + return null; + } + + @Override + public void m() { + throw new RuntimeException(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/PrivateOverridePublicizerDevirtualizerTest.java b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/PrivateOverridePublicizerDevirtualizerTest.java index bef0cd5..86739dc 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/PrivateOverridePublicizerDevirtualizerTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/devirtualize/PrivateOverridePublicizerDevirtualizerTest.java
@@ -5,13 +5,11 @@ package com.android.tools.r8.ir.optimize.devirtualize; 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.NeverClassInline; import com.android.tools.r8.NeverInline; import com.android.tools.r8.NoVerticalClassMerging; -import com.android.tools.r8.R8TestRunResult; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; @@ -48,33 +46,25 @@ @Test public void testR8() throws Exception { - R8TestRunResult runResult = - testForR8(parameters.getBackend()) - .addInnerClasses(PrivateOverridePublicizerDevirtualizerTest.class) - .enableInliningAnnotations() - .enableNoVerticalClassMergingAnnotations() - .enableNeverClassInliningAnnotations() - .addKeepMainRule(Main.class) - .allowAccessModification() - .noMinification() - .setMinApi(parameters.getApiLevel()) - .compile() - .inspect( - inspector -> { - ClassSubject classA = inspector.clazz(A.class); - assertThat(classA, isPresent()); - MethodSubject fooA = classA.uniqueMethodWithName("foo"); - // TODO(b/173812804): This should not be removed. - assertThat(fooA, not(isPresent())); - }) - .run(parameters.getRuntime(), Main.class); - if (parameters.isDexRuntime()) { - // TODO(b/173812804): This should not fail verification - runResult.assertFailureWithErrorThatThrows(VerifyError.class); - } else { - // TODO(b/173812804): This should have been A::foo, B::foo. - runResult.assertSuccessWithOutputLines("B::foo", "B::foo"); - } + testForR8(parameters.getBackend()) + .addInnerClasses(PrivateOverridePublicizerDevirtualizerTest.class) + .enableInliningAnnotations() + .enableNoVerticalClassMergingAnnotations() + .enableNeverClassInliningAnnotations() + .addKeepMainRule(Main.class) + .allowAccessModification() + .noMinification() + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect( + inspector -> { + ClassSubject classA = inspector.clazz(A.class); + assertThat(classA, isPresent()); + MethodSubject fooA = classA.uniqueMethodWithName("foo"); + assertThat(fooA, isPresent()); + }) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines(EXPECTED); } @NeverClassInline
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynthesizedLambdaClass.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynthesizedLambdaClass.java index 117a18c..1bd8db6 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynthesizedLambdaClass.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineSynthesizedLambdaClass.java
@@ -23,7 +23,7 @@ @Parameterized.Parameters(name = "{0}") public static TestParametersCollection data() { - return getTestParameters().withDexRuntimes().build(); + return getTestParameters().withDexRuntimes().withAllApiLevels().build(); } public InlineSynthesizedLambdaClass(TestParameters parameters) { @@ -42,7 +42,7 @@ .addKeepMainRule(Lambda.class) .allowAccessModification() .noMinification() - .setMinApi(parameters.getRuntime()) + .setMinApi(parameters.getApiLevel()) .run(parameters.getRuntime(), Lambda.class) .assertSuccessWithOutput(javaOutput) .inspector();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SyntheticInitClassPositionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SyntheticInitClassPositionTest.java new file mode 100644 index 0000000..1dc11c0 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SyntheticInitClassPositionTest.java
@@ -0,0 +1,80 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize.inliner; + +import static com.android.tools.r8.naming.retrace.StackTrace.isSame; +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.TestRuntime.CfRuntime; +import com.android.tools.r8.naming.retrace.StackTrace; +import org.junit.Before; +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 SyntheticInitClassPositionTest extends TestBase { + + private final TestParameters parameters; + private StackTrace expectedStackTrace; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return TestBase.getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + @Before + public void setup() throws Exception { + // Get the expected stack trace by running on the JVM. + expectedStackTrace = + testForJvm() + .addTestClasspath() + .run(CfRuntime.getSystemRuntime(), Main.class) + .assertFailure() + .map(StackTrace::extractFromJvm); + } + + public SyntheticInitClassPositionTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .addKeepAttributeLineNumberTable() + .addKeepAttributeSourceFile() + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), Main.class) + .assertFailureWithErrorThatThrows(ExceptionInInitializerError.class) + .inspectStackTrace(stackTrace -> assertThat(stackTrace, isSame(expectedStackTrace))); + } + + static class Main { + + public static void main(String[] args) { + A.m(); + } + } + + static class A { + + static { + if (true) { + throw new RuntimeException(); + } + } + + static void m() { + System.out.println("Hello world!"); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SyntheticInlineNullCheckPositionTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SyntheticInlineNullCheckPositionTest.java new file mode 100644 index 0000000..4eadc56 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SyntheticInlineNullCheckPositionTest.java
@@ -0,0 +1,99 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize.inliner; + +import static com.android.tools.r8.naming.retrace.StackTrace.isSame; +import static com.android.tools.r8.naming.retrace.StackTrace.isSameExceptForSpecificLineNumber; +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.TestRuntime.CfRuntime; +import com.android.tools.r8.naming.retrace.StackTrace; +import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine; +import java.util.Objects; +import org.junit.Before; +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 SyntheticInlineNullCheckPositionTest extends TestBase { + + private static final StackTraceLine REQUIRE_NON_NULL_LINE = + StackTraceLine.builder() + .setClassName(Objects.class.getTypeName()) + .setMethodName("requireNonNull") + .setFileName("Objects.java") + .build(); + + private final TestParameters parameters; + private StackTrace expectedStackTraceWithGetClass; + private StackTrace expectedStackTraceWithRequireNonNull; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return TestBase.getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + @Before + public void setup() throws Exception { + // Get the expected stack trace by running on the JVM. + StackTrace actualStackTrace = + testForJvm() + .addTestClasspath() + .run(CfRuntime.getSystemRuntime(), Main.class) + .assertFailure() + .map(StackTrace::extractFromJvm); + expectedStackTraceWithGetClass = actualStackTrace; + expectedStackTraceWithRequireNonNull = + StackTrace.builder().add(REQUIRE_NON_NULL_LINE).add(actualStackTrace).build(); + } + + public SyntheticInlineNullCheckPositionTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .addKeepAttributeLineNumberTable() + .addKeepAttributeSourceFile() + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), Main.class) + .assertFailureWithErrorThatThrows(NullPointerException.class) + .inspectStackTrace( + stackTrace -> { + if (canUseRequireNonNull(parameters)) { + assertThat( + stackTrace, + isSameExceptForSpecificLineNumber( + expectedStackTraceWithRequireNonNull, REQUIRE_NON_NULL_LINE)); + } else { + assertThat(stackTrace, isSame(expectedStackTraceWithGetClass)); + } + }); + } + + static class Main { + + public static void main(String[] args) { + A nullable = System.currentTimeMillis() < 0 ? new A() : null; + nullable.m(); + } + } + + static class A { + + void m() { + System.out.println("Hello world!"); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java index 8bdea50..a63addb 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/lambda/LambdaMethodInliningTest.java
@@ -43,6 +43,8 @@ .enableInliningAnnotations() .enableNoVerticalClassMergingAnnotations() .addOptionsModification(options -> options.enableClassInlining = false) + // TODO(b/173398086): Horizontal class merging breaks uniqueMethodWithName(). + .noMinification() .setMinApi(parameters.getApiLevel()) .compile() .inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderFromCharSequenceWithAppendObjectTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderFromCharSequenceWithAppendObjectTest.java new file mode 100644 index 0000000..8f1f58b --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderFromCharSequenceWithAppendObjectTest.java
@@ -0,0 +1,85 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize.string; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.ToolHelper.DexVm.Version; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class UnusedStringBuilderFromCharSequenceWithAppendObjectTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public UnusedStringBuilderFromCharSequenceWithAppendObjectTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .setMinApi(parameters.getApiLevel()) + .compile() + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLinesIf( + parameters.isCfRuntime() + || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V7_0_0), + "CustomCharSequence.length()", + "CustomCharSequence.length()", + "CustomCharSequence.length()", + "CustomCharSequence.charAt(0)") + .assertSuccessWithOutputLinesIf( + parameters.isDexRuntime() + && parameters.getDexRuntimeVersion().isOlderThan(Version.V7_0_0), + "CustomCharSequence.toString()"); + } + + static class Main { + + public static void main(String[] args) { + new StringBuilder(new CustomCharSequence()); + } + } + + static class CustomCharSequence implements CharSequence { + + @Override + public int length() { + System.out.println("CustomCharSequence.length()"); + return 1; + } + + @Override + public char charAt(int i) { + if (i != 0) { + throw new RuntimeException(); + } + System.out.println("CustomCharSequence.charAt(0)"); + return 'A'; + } + + @Override + public CharSequence subSequence(int i, int i1) { + throw new RuntimeException(); + } + + @Override + public String toString() { + System.out.println("CustomCharSequence.toString()"); + return "A"; + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderWithAppendDefinitelyNullObjectTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderWithAppendDefinitelyNullObjectTest.java new file mode 100644 index 0000000..f5f615d --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderWithAppendDefinitelyNullObjectTest.java
@@ -0,0 +1,58 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize.string; + +import static com.android.tools.r8.utils.codeinspector.CodeMatchers.instantiatesClass; +import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithName; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class UnusedStringBuilderWithAppendDefinitelyNullObjectTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public UnusedStringBuilderWithAppendDefinitelyNullObjectTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect( + inspector -> { + MethodSubject mainMethod = inspector.clazz(Main.class).mainMethod(); + assertThat(mainMethod, not(instantiatesClass(StringBuilder.class))); + assertThat(mainMethod, not(invokesMethodWithName("toString"))); + }) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithEmptyOutput(); + } + + static class Main { + + public static void main(String[] args) { + Object o = null; + new StringBuilder().append(o).toString(); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderWithAppendMaybeNullObjectTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderWithAppendMaybeNullObjectTest.java new file mode 100644 index 0000000..78c0a10 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderWithAppendMaybeNullObjectTest.java
@@ -0,0 +1,68 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize.string; + +import static com.android.tools.r8.utils.codeinspector.CodeMatchers.instantiatesClass; +import static com.android.tools.r8.utils.codeinspector.Matchers.notIf; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class UnusedStringBuilderWithAppendMaybeNullObjectTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public UnusedStringBuilderWithAppendMaybeNullObjectTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect( + inspector -> { + MethodSubject mainMethod = inspector.clazz(Main.class).mainMethod(); + assertThat( + mainMethod, + notIf( + instantiatesClass(StringBuilder.class), + canUseJavaUtilObjects(parameters) || parameters.isDexRuntime())); + }) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithEmptyOutput(); + } + + static class Main { + + public static void main(String[] args) { + A a = System.currentTimeMillis() < 0 ? new A() : null; + new StringBuilder().append(a).toString(); + } + } + + static class A { + + @Override + public String toString() { + return "A"; + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderWithAppendObjectSideEffectTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderWithAppendObjectSideEffectTest.java new file mode 100644 index 0000000..934dfd2 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderWithAppendObjectSideEffectTest.java
@@ -0,0 +1,64 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize.string; + +import static com.android.tools.r8.utils.codeinspector.CodeMatchers.instantiatesClass; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class UnusedStringBuilderWithAppendObjectSideEffectTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public UnusedStringBuilderWithAppendObjectSideEffectTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect( + inspector -> { + MethodSubject mainMethod = inspector.clazz(Main.class).mainMethod(); + assertThat(mainMethod, not(instantiatesClass(StringBuilder.class))); + }) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines("A"); + } + + static class Main { + + public static void main(String[] args) { + new StringBuilder().append(new A()).toString(); + } + } + + static class A { + + @Override + public String toString() { + System.out.println("A"); + return "A"; + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderWithAppendObjectTest.java b/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderWithAppendObjectTest.java new file mode 100644 index 0000000..d8ed11c --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/string/UnusedStringBuilderWithAppendObjectTest.java
@@ -0,0 +1,68 @@ +// Copyright (c) 2020, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +package com.android.tools.r8.ir.optimize.string; + +import static com.android.tools.r8.utils.codeinspector.CodeMatchers.instantiatesClass; +import static com.android.tools.r8.utils.codeinspector.Matchers.notIf; +import static org.hamcrest.MatcherAssert.assertThat; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +@RunWith(Parameterized.class) +public class UnusedStringBuilderWithAppendObjectTest extends TestBase { + + private final TestParameters parameters; + + @Parameterized.Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withAllRuntimesAndApiLevels().build(); + } + + public UnusedStringBuilderWithAppendObjectTest(TestParameters parameters) { + this.parameters = parameters; + } + + @Test + public void test() throws Exception { + testForR8(parameters.getBackend()) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .setMinApi(parameters.getApiLevel()) + .compile() + .inspect( + inspector -> { + MethodSubject mainMethod = inspector.clazz(Main.class).mainMethod(); + assertThat( + mainMethod, + notIf( + instantiatesClass(StringBuilder.class), + canUseJavaUtilObjects(parameters) || parameters.isDexRuntime())); + }) + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithEmptyOutput(); + } + + static class Main { + + public static void main(String[] args) { + A a = System.currentTimeMillis() > 0 ? new A() : null; + new StringBuilder().append(a).toString(); + } + } + + static class A { + + @Override + public String toString() { + return "A"; + } + } +}
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 cbbd4f0..a779afb 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
@@ -13,6 +13,7 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +@SuppressWarnings("unchecked") @RunWith(Parameterized.class) public class SwitchMapInvalidOrdinalTest extends TestBase { private final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java b/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java index d8d7c85..8f7c579 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/templates/CfUtilityMethodsForCodeOptimizationsTemplates.java
@@ -6,6 +6,12 @@ public class CfUtilityMethodsForCodeOptimizationsTemplates { + public static void toStringIfNotNull(Object o) { + if (o != null) { + o.toString(); + } + } + public static void throwClassCastExceptionIfNotNull(Object o) { if (o != null) { throw new ClassCastException();
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 fa7b74e..dff06df 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
@@ -73,6 +73,12 @@ } @Override + public void replaceCurrentInstructionWithConstString( + AppView<?> appView, IRCode code, DexString value) { + throw new Unimplemented(); + } + + @Override public void replaceCurrentInstructionWithStaticGet( AppView<?> appView, IRCode code, DexField field, Set<Value> affectedValues) { throw new Unimplemented(); @@ -151,6 +157,12 @@ } @Override + public BasicBlock splitCopyCatchHandlers( + IRCode code, ListIterator<BasicBlock> blockIterator, InternalOptions options) { + throw new Unimplemented(); + } + + @Override public BasicBlock inlineInvoke( AppView<?> appView, IRCode code,
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 f76758c..0abfe16 100644 --- a/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java +++ b/src/test/java/com/android/tools/r8/kotlin/AbstractR8KotlinTestBase.java
@@ -14,6 +14,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.KotlinTestBase; import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.R8TestRunResult; @@ -51,13 +52,16 @@ private final List<Path> extraClasspath = new ArrayList<>(); // Some tests defined in subclasses, e.g., Metadata tests, don't care about access relaxation. - protected AbstractR8KotlinTestBase(KotlinTargetVersion kotlinTargetVersion) { - this(kotlinTargetVersion, false); + protected AbstractR8KotlinTestBase( + KotlinTargetVersion kotlinTargetVersion, KotlinCompiler kotlinc) { + this(kotlinTargetVersion, kotlinc, false); } protected AbstractR8KotlinTestBase( - KotlinTargetVersion kotlinTargetVersion, boolean allowAccessModification) { - super(kotlinTargetVersion); + KotlinTargetVersion kotlinTargetVersion, + KotlinCompiler kotlinc, + boolean allowAccessModification) { + super(kotlinTargetVersion, kotlinc); this.allowAccessModification = allowAccessModification; } @@ -238,9 +242,14 @@ throws Exception { Assume.assumeTrue(ToolHelper.artSupported() || ToolHelper.compareAgaintsGoldenFiles()); + Path kotlinJarFile = + getCompileMemoizer(getKotlinFilesInResource(folder), folder) + .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect()) + .getForConfiguration(kotlinc, targetVersion); + // Build classpath for compilation (and java execution) classpath.clear(); - classpath.add(getKotlinJarFile(folder)); + classpath.add(kotlinJarFile); classpath.add(getJavaJarFile(folder)); classpath.addAll(extraClasspath);
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 2bb0867..a949213 100644 --- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.kotlin; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; @@ -12,6 +13,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.R8TestRunResult; import com.android.tools.r8.ThrowableConsumer; @@ -43,14 +45,15 @@ @RunWith(Parameterized.class) public class KotlinClassInlinerTest extends AbstractR8KotlinTestBase { - @Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}") + @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}") public static Collection<Object[]> data() { - return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values()); + return buildParameters( + KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values()); } public KotlinClassInlinerTest( - KotlinTargetVersion targetVersion, boolean allowAccessModification) { - super(targetVersion, allowAccessModification); + KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) { + super(targetVersion, kotlinc, allowAccessModification); } private static boolean isLambda(DexClass clazz) { @@ -332,7 +335,7 @@ // condition. options.testing.addCallEdgesForLibraryInvokes = true; - options.enableHorizontalClassMergingOfKotlinLambdas = false; + options.horizontalClassMergerOptions().disableKotlinLambdaMerging(); }) .apply(configuration)); }
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java index bf74d90..ec1793d 100644 --- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassStaticizerTest.java
@@ -4,12 +4,14 @@ package com.android.tools.r8.kotlin; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.R8TestRunResult; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.utils.BooleanUtils; @@ -24,14 +26,15 @@ @RunWith(Parameterized.class) public class KotlinClassStaticizerTest extends AbstractR8KotlinTestBase { - @Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}") + @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}") public static Collection<Object[]> data() { - return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values()); + return buildParameters( + KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values()); } public KotlinClassStaticizerTest( - KotlinTargetVersion targetVersion, boolean allowAccessModification) { - super(targetVersion, allowAccessModification); + KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) { + super(targetVersion, kotlinc, allowAccessModification); } @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java index 35e3109..7407d46 100644 --- a/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/KotlinDuplicateAnnotationTest.java
@@ -3,12 +3,14 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.utils.BooleanUtils; @@ -34,11 +36,12 @@ o.enableInlining = false; }; - @Parameterized.Parameters(name = "{0} target: {1}, allowAccessModification: {2}") + @Parameterized.Parameters(name = "{0} target: {1}, kotlinc: {2}, allowAccessModification: {3}") public static Collection<Object[]> data() { return buildParameters( getTestParameters().withAllRuntimes().build(), KotlinTargetVersion.values(), + getKotlinCompilers(), BooleanUtils.values()); } @@ -47,17 +50,22 @@ public KotlinDuplicateAnnotationTest( TestParameters parameters, KotlinTargetVersion targetVersion, + KotlinCompiler kotlinc, boolean allowAccessModification) { - super(targetVersion, allowAccessModification); + super(targetVersion, kotlinc, allowAccessModification); this.parameters = parameters; } + private static final KotlinCompileMemoizer compiledJars = + getCompileMemoizer(getKotlinFilesInResource(FOLDER), FOLDER) + .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect()); + @Test public void test_dex() { assumeTrue("test DEX", parameters.isDexRuntime()); try { testForR8(parameters.getBackend()) - .addProgramFiles(getKotlinJarFile(FOLDER)) + .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion)) .addKeepMainRule(MAIN) .addKeepRules(KEEP_RULES) .noMinification() @@ -74,7 +82,7 @@ public void test_cf() throws Exception { assumeTrue("test CF", parameters.isCfRuntime()); testForR8(parameters.getBackend()) - .addProgramFiles(getKotlinJarFile(FOLDER)) + .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion)) .addKeepMainRule(MAIN) .addKeepRules(KEEP_RULES) .noMinification()
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java index dc917ab..c683b1a 100644 --- a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
@@ -3,11 +3,13 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.utils.BooleanUtils; @@ -24,11 +26,12 @@ private static final String FOLDER = "intrinsics"; private static final String MAIN = FOLDER + ".InlineKt"; - @Parameterized.Parameters(name = "{0} target: {1}, allowAccessModification: {2}") + @Parameterized.Parameters(name = "{0} target: {1}, kotlinc: {2}, allowAccessModification: {3}") public static Collection<Object[]> data() { return buildParameters( getTestParameters().withAllRuntimes().build(), KotlinTargetVersion.values(), + getKotlinCompilers(), BooleanUtils.values()); } @@ -37,41 +40,46 @@ public KotlinIntrinsicsInlineTest( TestParameters parameters, KotlinTargetVersion targetVersion, + KotlinCompiler kotlinc, boolean allowAccessModification) { - super(targetVersion, allowAccessModification); + super(targetVersion, kotlinc, allowAccessModification); this.parameters = parameters; } + private static final KotlinCompileMemoizer compiledJars = + getCompileMemoizer(getKotlinFilesInResource(FOLDER), FOLDER) + .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect()); + @Test public void b139432507() throws Exception { testForR8(parameters.getBackend()) - .addProgramFiles(getKotlinJarFile(FOLDER)) - .addKeepRules(StringUtils.lines( - "-keepclasseswithmembers class " + MAIN + "{", - " public static *** *(...);", - "}")) + .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion)) + .addKeepRules( + StringUtils.lines( + "-keepclasseswithmembers class " + MAIN + "{", " public static *** *(...);", "}")) .allowAccessModification(allowAccessModification) .noMinification() .setMinApi(parameters.getRuntime()) .compile() - .inspect(inspector -> { - ClassSubject main = inspector.clazz(MAIN); - assertThat(main, isPresent()); + .inspect( + inspector -> { + ClassSubject main = inspector.clazz(MAIN); + assertThat(main, isPresent()); - // Note that isSupported itself has a parameter whose null check would be inlined - // with -allowaccessmodification. - MethodSubject isSupported = main.uniqueMethodWithName("isSupported"); - assertThat(isSupported, isPresent()); - assertEquals( - allowAccessModification ? 0 : 1, - countCall(isSupported, "checkParameterIsNotNull")); + // Note that isSupported itself has a parameter whose null check would be inlined + // with -allowaccessmodification. + MethodSubject isSupported = main.uniqueMethodWithName("isSupported"); + assertThat(isSupported, isPresent()); + assertEquals( + allowAccessModification ? 0 : 1, + countCall(isSupported, "checkParameterIsNotNull")); - // In general cases, null check won't be invoked only once or twice, hence no subtle - // situation in double inlining. - MethodSubject containsArray = main.uniqueMethodWithName("containsArray"); - assertThat(containsArray, isPresent()); - assertEquals(0, countCall(containsArray, "checkParameterIsNotNull")); - }); + // In general cases, null check won't be invoked only once or twice, hence no subtle + // situation in double inlining. + MethodSubject containsArray = main.uniqueMethodWithName("containsArray"); + assertThat(containsArray, isPresent()); + assertEquals(0, countCall(containsArray, "checkParameterIsNotNull")); + }); } @Test @@ -88,27 +96,29 @@ private void testSingle(String methodName) throws Exception { testForR8(parameters.getBackend()) - .addProgramFiles(getKotlinJarFile(FOLDER)) - .addKeepRules(StringUtils.lines( - "-keepclasseswithmembers class " + MAIN + "{", - " public static *** " + methodName + "(...);", - "}")) + .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion)) + .addKeepRules( + StringUtils.lines( + "-keepclasseswithmembers class " + MAIN + "{", + " public static *** " + methodName + "(...);", + "}")) .allowAccessModification(allowAccessModification) .noMinification() .setMinApi(parameters.getRuntime()) .compile() - .inspect(inspector -> { - ClassSubject main = inspector.clazz(MAIN); - assertThat(main, isPresent()); + .inspect( + inspector -> { + ClassSubject main = inspector.clazz(MAIN); + assertThat(main, isPresent()); - MethodSubject method = main.uniqueMethodWithName(methodName); - assertThat(method, isPresent()); - int arity = method.getMethod().method.getArity(); - // One from the method's own argument, if any, and - // Two from Array utils, `contains` and `indexOf`, if inlined with access relaxation. - assertEquals( - allowAccessModification ? 0 : arity + 2, - countCall(method, "checkParameterIsNotNull")); - }); + MethodSubject method = main.uniqueMethodWithName(methodName); + assertThat(method, isPresent()); + int arity = method.getMethod().method.getArity(); + // One from the method's own argument, if any, and + // Two from Array utils, `contains` and `indexOf`, if inlined with access relaxation. + assertEquals( + allowAccessModification ? 0 : arity + 2, + countCall(method, "checkParameterIsNotNull")); + }); } }
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java index 3c28a7d..b59d98c 100644 --- a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedArgumentsInLambdasTest.java
@@ -3,11 +3,13 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.MethodSubject; @@ -20,14 +22,15 @@ @RunWith(Parameterized.class) public class KotlinUnusedArgumentsInLambdasTest extends AbstractR8KotlinTestBase { - @Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}") + @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}") public static Collection<Object[]> data() { - return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values()); + return buildParameters( + KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values()); } public KotlinUnusedArgumentsInLambdasTest( - KotlinTargetVersion targetVersion, boolean allowAccessModification) { - super(targetVersion, allowAccessModification); + KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) { + super(targetVersion, kotlinc, allowAccessModification); } @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java index 10fe60e..c358325 100644 --- a/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/KotlinUnusedSingletonTest.java
@@ -3,12 +3,14 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; @@ -25,17 +27,18 @@ @RunWith(Parameterized.class) public class KotlinUnusedSingletonTest extends AbstractR8KotlinTestBase { - @Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}") + @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {1}") public static Collection<Object[]> data() { - return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values()); + return buildParameters( + KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values()); } private static final String printlnSignature = "void java.io.PrintStream.println(java.lang.Object)"; public KotlinUnusedSingletonTest( - KotlinTargetVersion targetVersion, boolean allowAccessModification) { - super(targetVersion, allowAccessModification); + KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) { + super(targetVersion, kotlinc, allowAccessModification); } @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java index e29373a..e90a8b6 100644 --- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinReflectionLibTest.java
@@ -3,6 +3,9 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; + +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.KotlinTestBase; import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.TestParameters; @@ -18,18 +21,21 @@ @RunWith(Parameterized.class) public class ProcessKotlinReflectionLibTest extends KotlinTestBase { - private final TestParameters parameters; - public ProcessKotlinReflectionLibTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); - this.parameters = parameters; - } + private final TestParameters parameters; @Parameterized.Parameters(name = "{0} target: {1}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withAllRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withAllRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); + } + + public ProcessKotlinReflectionLibTest( + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); + this.parameters = parameters; } private void test(Collection<String> rules) throws Exception { @@ -39,8 +45,9 @@ private void test( Collection<String> rules, ThrowableConsumer<R8FullTestBuilder> consumer) throws Exception { testForR8(parameters.getBackend()) - .addLibraryFiles(ToolHelper.getMostRecentAndroidJar(), ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(ToolHelper.getKotlinReflectJar()) + .addLibraryFiles( + ToolHelper.getMostRecentAndroidJar(), ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinc)) .addKeepRules(rules) .addKeepAttributes(ProguardKeepAttributes.SIGNATURE) .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
diff --git a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java index 195b536..7b3fd69 100644 --- a/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/ProcessKotlinStdlibTest.java
@@ -3,6 +3,9 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; + +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.KotlinTestBase; import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.TestParameters; @@ -20,15 +23,18 @@ public class ProcessKotlinStdlibTest extends KotlinTestBase { private final TestParameters parameters; - public ProcessKotlinStdlibTest(TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); - this.parameters = parameters; - } - @Parameterized.Parameters(name = "{0} target: {1}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withAllRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withAllRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); + } + + public ProcessKotlinStdlibTest( + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); + this.parameters = parameters; } private void test(Collection<String> rules, boolean expectInvalidFoo) throws Exception { @@ -41,7 +47,7 @@ ThrowableConsumer<R8FullTestBuilder> consumer) throws Exception { testForR8(parameters.getBackend()) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .addKeepRules(rules) .addKeepAttributes(ProguardKeepAttributes.SIGNATURE) .addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java index b4aeced..cd5d8b1 100644 --- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinAccessorTest.java
@@ -4,10 +4,12 @@ package com.android.tools.r8.kotlin; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.R8TestBuilder; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -65,14 +67,15 @@ .addProperty("property", JAVA_LANG_STRING, Visibility.PRIVATE) .addProperty("indirectPropertyGetter", JAVA_LANG_STRING, Visibility.PRIVATE); - @Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}") + @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}") public static Collection<Object[]> data() { - return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values()); + return buildParameters( + KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values()); } public R8KotlinAccessorTest( - KotlinTargetVersion targetVersion, boolean allowAccessModification) { - super(targetVersion, allowAccessModification); + KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) { + super(targetVersion, kotlinc, allowAccessModification); } @Test
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 ec0ee03..ef13161 100644 --- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinDataClassTest.java
@@ -4,6 +4,9 @@ package com.android.tools.r8.kotlin; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; + +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.graph.DexCode; import com.android.tools.r8.kotlin.TestKotlinClass.Visibility; @@ -42,14 +45,15 @@ private Consumer<InternalOptions> disableClassInliner = o -> o.enableClassInlining = false; - @Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}") + @Parameterized.Parameters(name = "target: {0}, kotlinc: {2}, allowAccessModification: {1}") public static Collection<Object[]> data() { - return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values()); + return buildParameters( + KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values()); } public R8KotlinDataClassTest( - KotlinTargetVersion targetVersion, boolean allowAccessModification) { - super(targetVersion, allowAccessModification); + KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) { + super(targetVersion, kotlinc, allowAccessModification); } @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java index 770d6cc..ea12c12 100644 --- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinIntrinsicsTest.java
@@ -4,6 +4,9 @@ package com.android.tools.r8.kotlin; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; + +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.naming.MemberNaming.MethodSignature; import com.android.tools.r8.utils.BooleanUtils; @@ -23,14 +26,15 @@ private static final TestKotlinDataClass KOTLIN_INTRINSICS_CLASS = new TestKotlinDataClass("kotlin.jvm.internal.Intrinsics"); - @Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}") + @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}") public static Collection<Object[]> data() { - return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values()); + return buildParameters( + KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values()); } public R8KotlinIntrinsicsTest( - KotlinTargetVersion targetVersion, boolean allowAccessModification) { - super(targetVersion, allowAccessModification); + KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) { + super(targetVersion, kotlinc, allowAccessModification); } @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java index 30511bb..e04585c 100644 --- a/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/R8KotlinPropertiesTest.java
@@ -4,8 +4,10 @@ package com.android.tools.r8.kotlin; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.kotlin.TestKotlinClass.Visibility; import com.android.tools.r8.naming.MemberNaming; @@ -93,14 +95,15 @@ o.enableClassStaticizer = false; }; - @Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}") + @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {1}") public static Collection<Object[]> data() { - return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values()); + return buildParameters( + KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values()); } public R8KotlinPropertiesTest( - KotlinTargetVersion targetVersion, boolean allowAccessModification) { - super(targetVersion, allowAccessModification); + KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) { + super(targetVersion, kotlinc, allowAccessModification); } @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java index d56d0f0..70fd115 100644 --- a/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/SimplifyIfNotNullKotlinTest.java
@@ -3,8 +3,10 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static org.junit.Assert.assertEquals; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.naming.MemberNaming.MethodSignature; import com.android.tools.r8.utils.BooleanUtils; @@ -21,14 +23,15 @@ private static final String FOLDER = "non_null"; private static final String STRING = "java.lang.String"; - @Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}") + @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}") public static Collection<Object[]> data() { - return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values()); + return buildParameters( + KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values()); } public SimplifyIfNotNullKotlinTest( - KotlinTargetVersion targetVersion, boolean allowAccessModification) { - super(targetVersion, allowAccessModification); + KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) { + super(targetVersion, kotlinc, allowAccessModification); } @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java index 6df1462..9ae5aed 100644 --- a/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java +++ b/src/test/java/com/android/tools/r8/kotlin/coroutines/KotlinxCoroutinesTestRunner.java
@@ -4,9 +4,10 @@ package com.android.tools.r8.kotlin.coroutines; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static org.junit.Assert.assertEquals; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -49,16 +50,19 @@ private Set<String> notWorkingTests = Sets.newHashSet("kotlinx.coroutines.test.TestDispatchersTest"); - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } private final TestParameters parameters; - public KotlinxCoroutinesTestRunner(TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + public KotlinxCoroutinesTestRunner( + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } @@ -88,7 +92,7 @@ } private Path compileTestSources(Path baseJar) throws Exception { - return kotlinc(KOTLINC, targetVersion) + return kotlinc(kotlinc, targetVersion) .addArguments( "-Xuse-experimental=kotlinx.coroutines.InternalCoroutinesApi", "-Xuse-experimental=kotlinx.coroutines.ObsoleteCoroutinesApi",
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java index 1aef013..7103667 100644 --- a/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/lambda/JStyleKotlinLambdaMergingWithEnumUnboxingTest.java
@@ -4,17 +4,20 @@ package com.android.tools.r8.kotlin.lambda; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; + +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; 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; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.ir.optimize.lambda.kotlin.JStyleLambdaGroupIdFactory; import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory; import com.android.tools.r8.kotlin.lambda.JStyleKotlinLambdaMergingWithEnumUnboxingTest.Main.EnumUnboxingCandidate; +import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -24,14 +27,19 @@ public class JStyleKotlinLambdaMergingWithEnumUnboxingTest extends TestBase { private final TestParameters parameters; + private final KotlinCompiler kotlinc; - @Parameters(name = "{0}") - public static TestParametersCollection data() { - return TestBase.getTestParameters().withDexRuntimes().withAllApiLevels().build(); + @Parameters(name = "{0}, kotlinc: {1}") + public static List<Object[]> data() { + return buildParameters( + TestBase.getTestParameters().withDexRuntimes().withAllApiLevels().build(), + getKotlinCompilers()); } - public JStyleKotlinLambdaMergingWithEnumUnboxingTest(TestParameters parameters) { + public JStyleKotlinLambdaMergingWithEnumUnboxingTest( + TestParameters parameters, KotlinCompiler kotlinc) { this.parameters = parameters; + this.kotlinc = kotlinc; } @Test @@ -39,7 +47,7 @@ testForR8(parameters.getBackend()) .addInnerClasses(getClass()) .addDefaultRuntimeLibrary(parameters) - .addLibraryFiles(ToolHelper.getKotlinStdlibJar()) + .addLibraryFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .addKeepMainRule(Main.class) .addOptionsModification( options ->
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java index 6edeebc..b9baae8 100644 --- a/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KStyleKotlinLambdaMergingWithEnumUnboxingTest.java
@@ -4,17 +4,20 @@ package com.android.tools.r8.kotlin.lambda; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; + +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; 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; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.ir.optimize.lambda.kotlin.KStyleLambdaGroupIdFactory; import com.android.tools.r8.ir.optimize.lambda.kotlin.KotlinLambdaGroupIdFactory; import com.android.tools.r8.kotlin.lambda.KStyleKotlinLambdaMergingWithEnumUnboxingTest.Main.EnumUnboxingCandidate; +import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -24,21 +27,26 @@ public class KStyleKotlinLambdaMergingWithEnumUnboxingTest extends TestBase { private final TestParameters parameters; + private final KotlinCompiler kotlinc; - @Parameters(name = "{0}") - public static TestParametersCollection data() { - return TestBase.getTestParameters().withDexRuntimes().withAllApiLevels().build(); + @Parameters(name = "{0}, kotlinc: {1}") + public static List<Object[]> data() { + return buildParameters( + TestBase.getTestParameters().withDexRuntimes().withAllApiLevels().build(), + getKotlinCompilers()); } - public KStyleKotlinLambdaMergingWithEnumUnboxingTest(TestParameters parameters) { + public KStyleKotlinLambdaMergingWithEnumUnboxingTest( + TestParameters parameters, KotlinCompiler kotlinc) { this.parameters = parameters; + this.kotlinc = kotlinc; } @Test public void test() throws Exception { testForR8(parameters.getBackend()) .addInnerClasses(getClass()) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .addKeepMainRule(Main.class) .addOptionsModification( options ->
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java index f28f68b..1b6d4c0 100644 --- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergerValidationTest.java
@@ -3,10 +3,11 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.lambda; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRuntime; import com.android.tools.r8.TestRuntime.CfRuntime; @@ -25,15 +26,17 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withAllRuntimesAndApiLevels().build(), KotlinTargetVersion.values()); + getTestParameters().withAllRuntimesAndApiLevels().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public KotlinLambdaMergerValidationTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion, false); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc, false); this.parameters = parameters; } @@ -46,17 +49,17 @@ CfRuntime cfRuntime = parameters.isCfRuntime() ? parameters.getRuntime().asCf() : TestRuntime.getCheckedInJdk9(); Path ktClasses = - kotlinc(cfRuntime, KOTLINC, targetVersion) + kotlinc(cfRuntime, kotlinc, targetVersion) .addSourceFiles(getKotlinFileInTest(folder, "b143165163")) .compile(); testForR8(parameters.getBackend()) .addLibraryFiles(ToolHelper.getJava8RuntimeJar()) - .addLibraryFiles(ToolHelper.getKotlinStdlibJar()) + .addLibraryFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .addProgramFiles(ktClasses) .addKeepMainRule("**.B143165163Kt") .setMinApi(parameters.getApiLevel()) .compile() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar()) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .run(parameters.getRuntime(), pkg + ".B143165163Kt") .assertSuccessWithOutputLines("outer foo bar", "outer foo default"); } @@ -70,11 +73,11 @@ CfRuntime cfRuntime = parameters.isCfRuntime() ? parameters.getRuntime().asCf() : TestRuntime.getCheckedInJdk9(); Path ktClasses = - kotlinc(cfRuntime, KOTLINC, targetVersion) + kotlinc(cfRuntime, kotlinc, targetVersion) .addSourceFiles(getKotlinFileInTest(folder, "b143165163")) .compile(); testForR8(parameters.getBackend()) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .addProgramFiles(ktClasses) .addKeepMainRule("**.B143165163Kt") .allowDiagnosticWarningMessages()
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java index 3a795be..e7c4762 100644 --- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingDebugTest.java
@@ -3,13 +3,15 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.lambda; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static org.hamcrest.CoreMatchers.equalTo; import com.android.tools.r8.CompilationMode; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase; +import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -23,20 +25,25 @@ private static final String MAIN_CLASS = "reprocess_merged_lambdas_kstyle.MainKt"; @Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withDexRuntimes().withAllApiLevels().build(); + public static List<Object[]> data() { + return buildParameters( + getTestParameters().withDexRuntimes().withAllApiLevels().build(), getKotlinCompilers()); } - public KotlinLambdaMergingDebugTest(TestParameters parameters) { - super(KotlinTargetVersion.JAVA_6); + public KotlinLambdaMergingDebugTest(TestParameters parameters, KotlinCompiler kotlinc) { + super(KotlinTargetVersion.JAVA_6, kotlinc); this.parameters = parameters; } + private static final KotlinCompileMemoizer compiledJars = + getCompileMemoizer(getKotlinFilesInResource(FOLDER), FOLDER) + .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect()); + @Test public void testMergingKStyleLambdasAndReprocessingInDebug() throws Exception { testForR8(parameters.getBackend()) .setMode(CompilationMode.DEBUG) - .addProgramFiles(getKotlinJarFile(FOLDER)) + .addProgramFiles(compiledJars.getForConfiguration(kotlinc, KotlinTargetVersion.JAVA_6)) .addProgramFiles(getJavaJarFile(FOLDER)) .setMinApi(parameters.getApiLevel()) .addKeepMainRule(MAIN_CLASS)
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java index 4f3ea88..b9e0546 100644 --- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingTest.java
@@ -4,12 +4,14 @@ package com.android.tools.r8.kotlin.lambda; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.google.common.base.Predicates.alwaysTrue; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.graph.DexClass; import com.android.tools.r8.graph.DexType; @@ -40,23 +42,29 @@ // Ensure that enclosing method and inner class attributes are kept even on classes that are // not explicitly mentioned by a keep rule. options.forceProguardCompatibility = true; - options.enableHorizontalClassMergingOfKotlinLambdas = false; + options.horizontalClassMergerOptions().disableKotlinLambdaMerging(); } private final boolean enableUnusedInterfaceRemoval; @Parameterized.Parameters( - name = "target: {0}, allow access modification: {1}, unused interface removal: {2}") + name = + "target: {0}, kotlinc: {1}, allow access modification: {2}, unused interface removal:" + + " {3}") public static Collection<Object[]> data() { return buildParameters( - KotlinTargetVersion.values(), BooleanUtils.values(), BooleanUtils.values()); + KotlinTargetVersion.values(), + getKotlinCompilers(), + BooleanUtils.values(), + BooleanUtils.values()); } public KotlinLambdaMergingTest( KotlinTargetVersion targetVersion, + KotlinCompiler kotlinc, boolean allowAccessModification, boolean enableUnusedInterfaceRemoval) { - super(targetVersion, allowAccessModification); + super(targetVersion, kotlinc, allowAccessModification); this.enableUnusedInterfaceRemoval = enableUnusedInterfaceRemoval; }
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java index d6e8c53..b1b38b9 100644 --- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithReprocessingTest.java
@@ -3,6 +3,9 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.lambda; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; + +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase; import com.android.tools.r8.utils.BooleanUtils; @@ -14,14 +17,15 @@ @RunWith(Parameterized.class) public class KotlinLambdaMergingWithReprocessingTest extends AbstractR8KotlinTestBase { - @Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}") + @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}") public static Collection<Object[]> data() { - return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values()); + return buildParameters( + KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values()); } public KotlinLambdaMergingWithReprocessingTest( - KotlinTargetVersion targetVersion, boolean allowAccessModification) { - super(targetVersion, allowAccessModification); + KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) { + super(targetVersion, kotlinc, allowAccessModification); } @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java index 127c1e3..1dcdcfb 100644 --- a/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/lambda/KotlinLambdaMergingWithSmallInliningBudgetTest.java
@@ -3,6 +3,9 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.lambda; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; + +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase; import com.android.tools.r8.utils.BooleanUtils; @@ -14,14 +17,15 @@ @RunWith(Parameterized.class) public class KotlinLambdaMergingWithSmallInliningBudgetTest extends AbstractR8KotlinTestBase { - @Parameterized.Parameters(name = "target: {0}, allowAccessModification: {1}") + @Parameterized.Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}") public static Collection<Object[]> data() { - return buildParameters(KotlinTargetVersion.values(), BooleanUtils.values()); + return buildParameters( + KotlinTargetVersion.values(), getKotlinCompilers(), BooleanUtils.values()); } public KotlinLambdaMergingWithSmallInliningBudgetTest( - KotlinTargetVersion targetVersion, boolean allowAccessModification) { - super(targetVersion, allowAccessModification); + KotlinTargetVersion targetVersion, KotlinCompiler kotlinc, boolean allowAccessModification) { + super(targetVersion, kotlinc, allowAccessModification); } @Test
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java index d764d5f..0dd8a62 100644 --- a/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java +++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b148525512/B148525512.java
@@ -4,7 +4,7 @@ package com.android.tools.r8.kotlin.lambda.b148525512; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertTrue; @@ -12,6 +12,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.KotlinTestBase; import com.android.tools.r8.R8TestCompileResult; import com.android.tools.r8.TestParameters; @@ -22,16 +23,13 @@ import com.android.tools.r8.utils.codeinspector.FoundClassSubject; import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; -import org.junit.BeforeClass; -import org.junit.ClassRule; import org.junit.Test; -import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -44,43 +42,45 @@ private static final String featureKtClassNamet = kotlinTestClassesPackage + ".FeatureKt"; private static final String baseClassName = kotlinTestClassesPackage + ".Base"; - private static final Map<KotlinTargetVersion, Path> kotlinBaseClasses = new HashMap<>(); - private static final Map<KotlinTargetVersion, Path> kotlinFeatureClasses = new HashMap<>(); + private static final KotlinCompileMemoizer kotlinBaseClasses = + getCompileMemoizer(getKotlinFileInTestPackage(pkg, "base")) + .configure( + kotlinCompilerTool -> kotlinCompilerTool.addClasspathFiles(getFeatureApiPath())); + private static final KotlinCompileMemoizer kotlinFeatureClasses = + getCompileMemoizer(getKotlinFileInTestPackage(pkg, "feature")) + .configure( + kotlinCompilerTool -> { + // Compile the feature Kotlin code with the base classes on classpath. + kotlinCompilerTool.addClasspathFiles( + kotlinBaseClasses.getForConfiguration( + kotlinCompilerTool.getCompiler(), kotlinCompilerTool.getTargetVersion())); + }); private final TestParameters parameters; - @Parameterized.Parameters(name = "{0},{1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( getTestParameters().withDexRuntimes().withAllApiLevels().build(), - KotlinTargetVersion.values()); + KotlinTargetVersion.values(), + getKotlinCompilers()); } - public B148525512(TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + public B148525512( + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - @ClassRule public static TemporaryFolder classTemp = new TemporaryFolder(); - - @BeforeClass - public static void compileKotlin() throws Exception { - // Compile the base Kotlin with the FeatureAPI Java class on classpath. - Path featureApiJar = classTemp.newFile("feature_api.jar").toPath(); - writeClassesToJar(featureApiJar, FeatureAPI.class); - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path ktBaseClasses = - kotlinc(KOTLINC, targetVersion) - .addClasspathFiles(featureApiJar) - .addSourceFiles(getKotlinFileInTestPackage(pkg, "base")) - .compile(); - kotlinBaseClasses.put(targetVersion, ktBaseClasses); - // Compile the feature Kotlin code with the base classes on classpath. - Path ktFeatureClasses = - kotlinc(KOTLINC, targetVersion) - .addClasspathFiles(ktBaseClasses) - .addSourceFiles(getKotlinFileInTestPackage(pkg, "feature")) - .compile(); - kotlinFeatureClasses.put(targetVersion, ktFeatureClasses); + private static Path getFeatureApiPath() { + try { + Path featureApiJar = getStaticTemp().getRoot().toPath().resolve("feature_api.jar"); + if (Files.exists(featureApiJar)) { + return featureApiJar; + } + writeClassesToJar(featureApiJar, FeatureAPI.class); + return featureApiJar; + } catch (IOException e) { + throw new RuntimeException(e); } } @@ -118,15 +118,15 @@ Path featureCode = temp.newFile("feature.zip").toPath(); R8TestCompileResult compileResult = testForR8(parameters.getBackend()) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(kotlinBaseClasses.get(targetVersion)) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(kotlinBaseClasses.getForConfiguration(kotlinc, targetVersion)) .addProgramClasses(FeatureAPI.class) .addKeepMainRule(baseKtClassName) .addKeepClassAndMembersRules(baseClassName) .addKeepClassAndMembersRules(featureKtClassNamet) .addKeepClassAndMembersRules(FeatureAPI.class) .addOptionsModification( - options -> options.enableHorizontalClassMergingOfKotlinLambdas = false) + options -> options.horizontalClassMergerOptions().disableKotlinLambdaMerging()) .setMinApi(parameters.getApiLevel()) .noMinification() // The check cast inspection above relies on original names. .addFeatureSplit( @@ -134,7 +134,8 @@ builder .addProgramResourceProvider( ArchiveResourceProvider.fromArchive( - kotlinFeatureClasses.get(targetVersion), true)) + kotlinFeatureClasses.getForConfiguration(kotlinc, targetVersion), + true)) .setProgramConsumer(new ArchiveConsumer(featureCode, false)) .build()) .allowDiagnosticWarningMessages()
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java index 73b42ed..57cdae7 100644 --- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaGroupGCLimitTest.java
@@ -8,15 +8,17 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import com.android.tools.r8.CompilationFailedException; import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.R8TestCompileResult; import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.ProcessResult; +import com.android.tools.r8.utils.BooleanUtils; +import com.android.tools.r8.utils.InternalOptions.HorizontalClassMergerOptions; import com.android.tools.r8.utils.codeinspector.FoundClassSubject; import java.io.IOException; import java.nio.file.Path; @@ -32,16 +34,20 @@ @RunWith(Parameterized.class) public class LambdaGroupGCLimitTest extends TestBase { + private final boolean enableHorizontalClassMergingOfKotlinLambdas; private final TestParameters parameters; private final int LAMBDA_HOLDER_LIMIT = 50; private final int LAMBDAS_PER_CLASS_LIMIT = 100; - @Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withDexRuntimes().withAllApiLevels().build(); + @Parameters(name = "{1}, horizontal class merging: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withDexRuntimes().withAllApiLevels().build()); } - public LambdaGroupGCLimitTest(TestParameters parameters) { + public LambdaGroupGCLimitTest( + boolean enableHorizontalClassMergingOfKotlinLambdas, TestParameters parameters) { + this.enableHorizontalClassMergingOfKotlinLambdas = enableHorizontalClassMergingOfKotlinLambdas; this.parameters = parameters; } @@ -50,7 +56,12 @@ String PKG_NAME = LambdaGroupGCLimitTest.class.getPackage().getName(); R8FullTestBuilder testBuilder = testForR8(parameters.getBackend()) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(ToolHelper.getKotlinC_1_3_72())) + .addOptionsModification( + options -> + options + .horizontalClassMergerOptions() + .enableKotlinLambdaMergingIf(enableHorizontalClassMergingOfKotlinLambdas)) .setMinApi(parameters.getApiLevel()) .noMinification(); Path classFiles = temp.newFile("classes.jar").toPath(); @@ -63,7 +74,27 @@ testBuilder.addKeepClassAndMembersRules(PKG_NAME + ".MainKt" + mainId); } writeClassFileDataToJar(classFiles, classFileData); - R8TestCompileResult compileResult = testBuilder.addProgramFiles(classFiles).compile(); + R8TestCompileResult compileResult = + testBuilder + .addProgramFiles(classFiles) + .addHorizontallyMergedClassesInspector( + inspector -> { + if (enableHorizontalClassMergingOfKotlinLambdas) { + HorizontalClassMergerOptions defaultHorizontalClassMergerOptions = + new HorizontalClassMergerOptions(); + assertEquals(4833, inspector.getSources().size()); + assertEquals(167, inspector.getTargets().size()); + assertTrue( + inspector.getMergeGroups().stream() + .allMatch( + mergeGroup -> + mergeGroup.size() + <= defaultHorizontalClassMergerOptions.getMaxGroupSize())); + } else { + inspector.assertNoClassesMerged(); + } + }) + .compile(); Path path = compileResult.writeToZip(); compileResult .run(parameters.getRuntime(), PKG_NAME + ".MainKt0") @@ -74,7 +105,9 @@ codeInspector.allClasses().stream() .filter(c -> c.getFinalName().contains("LambdaGroup")) .collect(Collectors.toList()); - assertEquals(1, lambdaGroups.size()); + assertEquals( + 1 - BooleanUtils.intValue(enableHorizontalClassMergingOfKotlinLambdas), + lambdaGroups.size()); }); Path oatFile = temp.newFile("out.oat").toPath(); ProcessResult processResult =
diff --git a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java index 3926dea..c261c25 100644 --- a/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/lambda/b159688129/LambdaSplitByCodeCorrectnessTest.java
@@ -4,10 +4,11 @@ package com.android.tools.r8.kotlin.lambda.b159688129; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertEquals; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestRuntime; import com.android.tools.r8.TestRuntime.CfRuntime; @@ -33,17 +34,21 @@ private final KotlinTargetVersion targetVersion; private final boolean splitGroup; - @Parameters(name = "{0}, targetVersion: {1}, splitGroup: {2}") + @Parameters(name = "{0}, kotlinc: {2} targetVersion: {1}, splitGroup: {3}") public static List<Object[]> data() { return buildParameters( getTestParameters().withDexRuntimes().withAllApiLevels().build(), KotlinTargetVersion.values(), + getKotlinCompilers(), BooleanUtils.values()); } public LambdaSplitByCodeCorrectnessTest( - TestParameters parameters, KotlinTargetVersion targetVersion, boolean splitGroup) { - super(targetVersion); + TestParameters parameters, + KotlinTargetVersion targetVersion, + KotlinCompiler kotlinc, + boolean splitGroup) { + super(targetVersion, kotlinc); this.parameters = parameters; this.targetVersion = targetVersion; this.splitGroup = splitGroup; @@ -56,12 +61,14 @@ CfRuntime cfRuntime = parameters.isCfRuntime() ? parameters.getRuntime().asCf() : TestRuntime.getCheckedInJdk9(); Path ktClasses = - kotlinc(cfRuntime, KOTLINC, targetVersion) + kotlinc(cfRuntime, kotlinc, targetVersion) .addSourceFiles(getKotlinFileInTest(folder, "Simple")) .compile(); testForR8(parameters.getBackend()) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .addProgramFiles(ktClasses) + .addOptionsModification( + options -> options.horizontalClassMergerOptions().disableKotlinLambdaMerging()) .setMinApi(parameters.getApiLevel()) .addKeepMainRule(PKG_NAME + ".SimpleKt") .applyIf( @@ -70,8 +77,7 @@ b.addOptionsModification( internalOptions -> // Setting verificationSizeLimitInBytesOverride = 1 will force a a chain - // having - // only a single implementation method in each. + // having only a single implementation method in each. internalOptions.testing.verificationSizeLimitInBytesOverride = splitGroup ? 1 : -1)) .noMinification()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java index 99aaf1b..e158fc7 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/KotlinMetadataTestBase.java
@@ -8,6 +8,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertNull; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase; import com.android.tools.r8.kotlin.KotlinMetadataWriter; @@ -21,8 +22,8 @@ public abstract class KotlinMetadataTestBase extends AbstractR8KotlinTestBase { - public KotlinMetadataTestBase(KotlinTargetVersion targetVersion) { - super(targetVersion); + public KotlinMetadataTestBase(KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); } static final String PKG = KotlinMetadataTestBase.class.getPackage().getName(); @@ -40,8 +41,8 @@ static final String KT_FUNCTION1 = "Lkotlin/Function1;"; static final String KT_COMPARABLE = "Lkotlin/Comparable;"; - public void assertEqualMetadata(CodeInspector originalInspector, CodeInspector rewrittenInspector) - throws Exception { + public void assertEqualMetadata( + CodeInspector originalInspector, CodeInspector rewrittenInspector) { for (FoundClassSubject clazzSubject : originalInspector.allClasses()) { ClassSubject r8Clazz = rewrittenInspector.clazz(clazzSubject.getOriginalName()); assertThat(r8Clazz, isPresent());
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java index 76c2894..31b489a 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrimitiveTypeRewriteTest.java
@@ -4,13 +4,14 @@ package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import com.android.tools.r8.JvmTestRunResult; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -19,9 +20,6 @@ import com.android.tools.r8.utils.codeinspector.ClassSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -34,44 +32,36 @@ private static final String PKG_LIB = PKG + ".primitive_type_rewrite_lib"; private static final String PKG_APP = PKG + ".primitive_type_rewrite_app"; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, compiler: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataPrimitiveTypeRewriteTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static Map<KotlinTargetVersion, Path> libJars = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path baseLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles( - getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib")) - .compile(); - libJars.put(targetVersion, baseLibJar); - } - } + private static final KotlinCompileMemoizer libJars = + getCompileMemoizer( + getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib")); @Test public void smokeTest() throws Exception { - Path libJar = libJars.get(targetVersion); + Path libJar = libJars.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG_APP + ".MainKt") .assertSuccessWithOutputLines(EXPECTED); @@ -90,7 +80,9 @@ private void runTest(boolean keepUnit) throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addProgramFiles(ToolHelper.getKotlinStdlibJar(), libJars.get(targetVersion)) + .addProgramFiles( + ToolHelper.getKotlinStdlibJar(kotlinc), + libJars.getForConfiguration(kotlinc, targetVersion)) .addKeepAllClassesRuleWithAllowObfuscation() .addKeepRules("-keep class " + PKG_LIB + ".LibKt { *; }") .addKeepRules("-keep class kotlin.Metadata { *; }") @@ -107,7 +99,7 @@ equalTo("Resource 'META-INF/MANIFEST.MF' already exists.")) .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) @@ -115,7 +107,7 @@ .compile(); final JvmTestRunResult runResult = testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG_APP + ".MainKt"); if (keepUnit) {
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java index 77134d8..a048351 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataPrunedFieldsTest.java
@@ -4,23 +4,20 @@ package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.kotlin.metadata.metadata_pruned_fields.Main; import com.android.tools.r8.shaking.ProguardKeepAttributes; import com.android.tools.r8.utils.codeinspector.ClassSubject; -import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -31,36 +28,28 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withAllRuntimesAndApiLevels().build(), KotlinTargetVersion.values()); + getTestParameters().withAllRuntimesAndApiLevels().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } - public MetadataPrunedFieldsTest(TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + public MetadataPrunedFieldsTest( + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static Map<KotlinTargetVersion, Path> libJars = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String baseLibFolder = PKG_PREFIX + "/metadata_pruned_fields"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path baseLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(baseLibFolder, "Methods")) - .compile(); - libJars.put(targetVersion, baseLibJar); - } - } + private static final KotlinCompileMemoizer libJars = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/metadata_pruned_fields", "Methods")); @Test public void testR8() throws Exception { testForR8(parameters.getBackend()) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(libJars.get(targetVersion)) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion)) .addProgramClassFileData(Main.dump()) .addKeepRules("-keep class " + PKG + ".metadata_pruned_fields.MethodsKt { *; }") .addKeepRules("-keep class kotlin.Metadata { *** pn(); }")
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java index 549f834..ea9ed33 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAllowAccessModificationTest.java
@@ -4,12 +4,13 @@ package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static junit.framework.TestCase.assertEquals; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.StringContains.containsString; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -21,9 +22,6 @@ import com.android.tools.r8.utils.codeinspector.MethodSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -54,53 +52,40 @@ "staticPrivate", "staticInternal"); - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteAllowAccessModificationTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static Map<KotlinTargetVersion, Path> libJars = new HashMap<>(); - private static Map<KotlinTargetVersion, Path> libReferenceJars = new HashMap<>(); + private static final KotlinCompileMemoizer libJars = + getCompileMemoizer( + getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib")); + private static final KotlinCompileMemoizer libReferenceJars = + getCompileMemoizer( + getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib_reference")); private final TestParameters parameters; - @BeforeClass - public static void createLibJar() throws Exception { - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - libJars.put( - targetVersion, - kotlinc(KOTLINC, targetVersion) - .addSourceFiles( - getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib")) - .compile()); - libReferenceJars.put( - targetVersion, - kotlinc(KOTLINC, targetVersion) - .addSourceFiles( - getKotlinFileInTest( - DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib_reference")) - .compile()); - } - } - @Test public void smokeTest() throws Exception { - Path libJar = libReferenceJars.get(targetVersion); + Path libJar = libReferenceJars.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG_APP + ".MainKt") .assertSuccessWithOutput(EXPECTED); @@ -113,7 +98,7 @@ // running with R8, the output should be binary compatible with libReference. Path libJar = testForR8(parameters.getBackend()) - .addProgramFiles(libJars.get(targetVersion)) + .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion)) .addKeepRules("-keepclassmembers,allowaccessmodification class **.Lib { *; }") .addKeepRules("-keep,allowaccessmodification,allowobfuscation class **.Lib { *; }") .addKeepRules("-keepclassmembers,allowaccessmodification class **.Lib$Comp { *; }") @@ -134,7 +119,7 @@ .inspect(this::inspect) .writeToZip(); ProcessResult mainResult = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) @@ -144,7 +129,7 @@ assertThat(mainResult.stderr, containsString("cannot access 'LibReference'")); } - private void inspect(CodeInspector inspector) throws Exception { + private void inspect(CodeInspector inspector) { // TODO(b/154348683): Assert equality between LibReference and Lib. // assertEqualMetadata(new CodeInspector(libReferenceJars.get(targetVersion)), inspector); ClassSubject lib = inspector.clazz(PKG_LIB + ".Lib");
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java index 0b65d77..428103f 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnnotationTest.java
@@ -4,13 +4,14 @@ package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -22,12 +23,10 @@ import com.android.tools.r8.utils.codeinspector.KmTypeAliasSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.Map; import kotlinx.metadata.KmAnnotation; import kotlinx.metadata.KmAnnotationArgument; import kotlinx.metadata.KmAnnotationArgument.ArrayValue; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -59,38 +58,30 @@ private static final String FOO_ORIGINAL_NAME = PKG_LIB + ".Foo"; private static final String FOO_FINAL_NAME = "a.b.c"; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteAnnotationTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static Map<KotlinTargetVersion, Path> libJars = new HashMap<>(); + private static final KotlinCompileMemoizer libJars = + getCompileMemoizer( + getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib")); private final TestParameters parameters; - @BeforeClass - public static void createLibJar() throws Exception { - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path baseLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles( - getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib")) - .compile(); - libJars.put(targetVersion, baseLibJar); - } - } - @Test public void smokeTest() throws Exception { - Path libJar = libJars.get(targetVersion); + Path libJar = libJars.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) @@ -98,7 +89,7 @@ .compile(); testForJvm() .addRunClasspathFiles( - ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar) + ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG_APP + ".MainKt") .assertSuccessWithOutput(EXPECTED); @@ -108,7 +99,7 @@ public void testMetadataForLib() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addProgramFiles(libJars.get(targetVersion)) + .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion)) /// Keep the annotations .addKeepClassAndMembersRules(PKG_LIB + ".AnnoWithClassAndEnum") .addKeepClassAndMembersRules(PKG_LIB + ".AnnoWithClassArr") @@ -134,14 +125,14 @@ .inspect(this::inspect) .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) .compile(); testForJvm() .addRunClasspathFiles( - ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar) + ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar) .addProgramFiles(output) .run(parameters.getRuntime(), PKG_APP + ".MainKt") .assertSuccessWithOutput(EXPECTED.replace(FOO_ORIGINAL_NAME, FOO_FINAL_NAME));
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java index 14cbe0b..0ff40ff 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteAnonymousTest.java
@@ -4,10 +4,11 @@ package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed; import static org.hamcrest.MatcherAssert.assertThat; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -17,9 +18,6 @@ import com.android.tools.r8.utils.codeinspector.CodeInspector; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -29,45 +27,36 @@ private final String EXPECTED = "foo"; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteAnonymousTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static Map<KotlinTargetVersion, Path> libJars = new HashMap<>(); + private static final KotlinCompileMemoizer libJars = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/anonymous_lib", "lib")); private final TestParameters parameters; - @BeforeClass - public static void createLibJar() throws Exception { - String baseLibFolder = PKG_PREFIX + "/anonymous_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path baseLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(baseLibFolder, "lib")) - .compile(); - libJars.put(targetVersion, baseLibJar); - } - } - @Test public void smokeTest() throws Exception { - Path libJar = libJars.get(targetVersion); + Path libJar = libJars.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/anonymous_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".anonymous_app.MainKt") .assertSuccessWithOutputLines(EXPECTED); @@ -77,8 +66,8 @@ public void testMetadataForLib() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(libJars.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion)) .addKeepAllClassesRuleWithAllowObfuscation() .addKeepRules("-keep class " + PKG + ".anonymous_lib.Test$A { *; }") .addKeepRules("-keep class " + PKG + ".anonymous_lib.Test { *; }") @@ -91,13 +80,13 @@ .inspect(this::inspect) .writeToZip(); Path main = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/anonymous_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(main) .run(parameters.getRuntime(), PKG + ".anonymous_app.MainKt") .assertSuccessWithOutputLines(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java index 06d2636..2c2ae83 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteBoxedTypesTest.java
@@ -4,13 +4,14 @@ package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertNotNull; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertNull; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -23,12 +24,9 @@ import java.io.IOException; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.ExecutionException; import kotlinx.metadata.jvm.KotlinClassHeader; import kotlinx.metadata.jvm.KotlinClassMetadata; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -39,44 +37,35 @@ private final String EXPECTED = StringUtils.lines("false", "0", "a", "0.042", "0.42", "42", "442", "1", "2", "42", "42"); - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteBoxedTypesTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static Map<KotlinTargetVersion, Path> libJars = new HashMap<>(); + private static final KotlinCompileMemoizer libJars = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/box_primitives_lib", "lib")); private final TestParameters parameters; - @BeforeClass - public static void createLibJar() throws Exception { - String baseLibFolder = PKG_PREFIX + "/box_primitives_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path baseLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(baseLibFolder, "lib")) - .compile(); - libJars.put(targetVersion, baseLibJar); - } - } - @Test public void smokeTest() throws Exception { - Path libJar = libJars.get(targetVersion); + Path libJar = libJars.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/box_primitives_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".box_primitives_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -84,9 +73,9 @@ @Test public void smokeTestReflection() throws Exception { - Path libJar = libJars.get(targetVersion); + Path libJar = libJars.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/box_primitives_app", "main_reflect")) .setOutputPath(temp.newFolder().toPath()) @@ -94,7 +83,7 @@ testForJvm() .addVmArguments("-ea") .addRunClasspathFiles( - ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar) + ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".box_primitives_app.Main_reflectKt") .assertSuccessWithOutput(EXPECTED); @@ -104,8 +93,8 @@ public void testMetadataForLib() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(libJars.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion)) .addKeepAllClassesRule() .addKeepAttributes( ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS, @@ -116,14 +105,14 @@ .inspect(this::inspect) .writeToZip(); Path main = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/box_primitives_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() .addRunClasspathFiles( - ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar) + ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar) .addClasspath(main) .run(parameters.getRuntime(), PKG + ".box_primitives_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -132,7 +121,8 @@ private void inspect(CodeInspector inspector) throws IOException, ExecutionException { // Since this has a keep-all classes rule, we should just assert that the meta-data is equal to // the original one. - CodeInspector stdLibInspector = new CodeInspector(libJars.get(targetVersion)); + CodeInspector stdLibInspector = + new CodeInspector(libJars.getForConfiguration(kotlinc, targetVersion)); for (FoundClassSubject clazzSubject : stdLibInspector.allClasses()) { ClassSubject r8Clazz = inspector.clazz(clazzSubject.getOriginalName()); assertThat(r8Clazz, isPresent()); @@ -159,8 +149,8 @@ public void testMetadataForReflect() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(libJars.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion)) .addKeepAllClassesRule() .addKeepAttributes( ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS, @@ -170,7 +160,7 @@ .compile() .writeToZip(); Path main = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/box_primitives_app", "main_reflect")) .setOutputPath(temp.newFolder().toPath()) @@ -178,7 +168,7 @@ testForJvm() .addVmArguments("-ea") .addRunClasspathFiles( - ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar) + ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar) .addClasspath(main) .run(parameters.getRuntime(), PKG + ".box_primitives_app.Main_reflectKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java index 1ebdddb..56fd483 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineAnonFunctionTest.java
@@ -4,8 +4,9 @@ package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -13,9 +14,6 @@ import com.android.tools.r8.utils.StringUtils; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -27,45 +25,37 @@ private static final String PKG_LIB = PKG + ".crossinline_anon_lib"; private static final String PKG_APP = PKG + ".crossinline_anon_app"; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteCrossinlineAnonFunctionTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static Map<KotlinTargetVersion, Path> libJars = new HashMap<>(); + private static final KotlinCompileMemoizer libJars = + getCompileMemoizer( + getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib")); private final TestParameters parameters; - @BeforeClass - public static void createLibJar() throws Exception { - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path baseLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles( - getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib")) - .compile(); - libJars.put(targetVersion, baseLibJar); - } - } - @Test public void smokeTest() throws Exception { - Path libJar = libJars.get(targetVersion); + Path libJar = libJars.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG_APP + ".MainKt") .assertSuccessWithOutput(EXPECTED); @@ -75,19 +65,19 @@ public void testMetadataForLib() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addProgramFiles(libJars.get(targetVersion)) + .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion)) .addKeepAllClassesRule() .addKeepAllAttributes() .compile() .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addProgramFiles(output) .run(parameters.getRuntime(), PKG_APP + ".MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java index ce26b77..b8c5ac2 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteCrossinlineConcreteFunctionTest.java
@@ -4,8 +4,9 @@ package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -13,9 +14,6 @@ import com.android.tools.r8.utils.StringUtils; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -27,45 +25,37 @@ private static final String PKG_LIB = PKG + ".crossinline_concrete_lib"; private static final String PKG_APP = PKG + ".crossinline_concrete_app"; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteCrossinlineConcreteFunctionTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static Map<KotlinTargetVersion, Path> libJars = new HashMap<>(); + private static final KotlinCompileMemoizer libJars = + getCompileMemoizer( + getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib")); private final TestParameters parameters; - @BeforeClass - public static void createLibJar() throws Exception { - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path baseLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles( - getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib")) - .compile(); - libJars.put(targetVersion, baseLibJar); - } - } - @Test public void smokeTest() throws Exception { - Path libJar = libJars.get(targetVersion); + Path libJar = libJars.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG_APP + ".MainKt") .assertSuccessWithOutput(EXPECTED); @@ -75,20 +65,20 @@ public void testMetadataForLib() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addProgramFiles(libJars.get(targetVersion)) + .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion)) .addKeepAllClassesRule() .addKeepAllAttributes() .compile() .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG_APP + ".MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java index badbf02..d71248e 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
@@ -4,8 +4,9 @@ package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -15,9 +16,6 @@ import com.android.tools.r8.utils.codeinspector.CodeInspector; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -35,38 +33,31 @@ "null", "New value has been read in CustomDelegate from 'x'"); - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteDelegatedPropertyTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } private final TestParameters parameters; - private static Map<KotlinTargetVersion, Path> jars = new HashMap<>(); - - @BeforeClass - public static void createJar() throws Exception { - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path baseLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles( - getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) - .compile(); - jars.put(targetVersion, baseLibJar); - } - } + private static final KotlinCompileMemoizer jars = + getCompileMemoizer( + getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")); @Test public void smokeTest() throws Exception { testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar()) - .addClasspath(jars.get(targetVersion)) + .addRunClasspathFiles( + ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc)) + .addClasspath(jars.getForConfiguration(kotlinc, targetVersion)) .run(parameters.getRuntime(), PKG_APP + ".MainKt") .assertSuccessWithOutput(EXPECTED_MAIN); } @@ -76,17 +67,20 @@ public void testMetadataForLib() throws Exception { Path outputJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(jars.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(jars.getForConfiguration(kotlinc, targetVersion)) .addKeepAllClassesRule() .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) .compile() .inspect( inspector -> - assertEqualMetadata(new CodeInspector(jars.get(targetVersion)), inspector)) + assertEqualMetadata( + new CodeInspector(jars.getForConfiguration(kotlinc, targetVersion)), + inspector)) .writeToZip(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar()) + .addRunClasspathFiles( + ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc)) .addClasspath(outputJar) .run(parameters.getRuntime(), PKG_APP + ".MainKt") .assertSuccessWithOutput(EXPECTED_MAIN);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java index a8fcf61..15112f6 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDependentKeep.java
@@ -4,10 +4,12 @@ package com.android.tools.r8.kotlin.metadata; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -25,24 +27,26 @@ @RunWith(Parameterized.class) public class MetadataRewriteDependentKeep extends KotlinMetadataTestBase { - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } private final TestParameters parameters; public MetadataRewriteDependentKeep( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } @Test public void testR8() throws CompilationFailedException, IOException, ExecutionException { testForR8(parameters.getBackend()) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .setMinApi(parameters.getApiLevel()) .addKeepKotlinMetadata() .addKeepRules(StringUtils.joinLines("-if class *.Metadata", "-keep class <1>.io.** { *; }"))
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java index 788e64b..fb4dab9 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDoNotEmitValuesIfEmpty.java
@@ -4,9 +4,11 @@ package com.android.tools.r8.kotlin.metadata; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -27,24 +29,26 @@ private final Set<String> nullableFieldKeys = Sets.newHashSet("pn", "xs", "xi"); - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } private final TestParameters parameters; public MetadataRewriteDoNotEmitValuesIfEmpty( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } @Test public void testKotlinStdLib() throws Exception { testForR8(parameters.getBackend()) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .setMinApi(parameters.getApiLevel()) .addKeepAllClassesRule() .addKeepKotlinMetadata()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java index d5b9fd0..ecaea79 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteFlexibleUpperBoundTest.java
@@ -4,7 +4,7 @@ package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed; @@ -12,6 +12,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -25,12 +26,9 @@ import java.io.IOException; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.concurrent.ExecutionException; import kotlinx.metadata.KmFlexibleTypeUpperBound; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -41,44 +39,35 @@ private final String EXPECTED = StringUtils.lines("B.foo(): 42"); private final String PKG_LIB = PKG + ".flexible_upper_bound_lib"; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteFlexibleUpperBoundTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static Map<KotlinTargetVersion, Path> libJars = new HashMap<>(); + private static final KotlinCompileMemoizer libJars = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/flexible_upper_bound_lib", "lib")); private final TestParameters parameters; - @BeforeClass - public static void createLibJar() throws Exception { - String baseLibFolder = PKG_PREFIX + "/flexible_upper_bound_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path baseLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(baseLibFolder, "lib")) - .compile(); - libJars.put(targetVersion, baseLibJar); - } - } - @Test public void smokeTest() throws Exception { - Path libJar = libJars.get(targetVersion); + Path libJar = libJars.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/flexible_upper_bound_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".flexible_upper_bound_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -88,8 +77,8 @@ public void testMetadataForLib() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(libJars.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion)) // Allow renaming A to ensure that we rename in the flexible upper bound type. .addKeepRules("-keep,allowobfuscation class " + PKG_LIB + ".A { *; }") .addKeepRules("-keep class " + PKG_LIB + ".B { *; }") @@ -103,14 +92,14 @@ .inspect(this::inspect) .writeToZip(); Path main = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/flexible_upper_bound_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() .addRunClasspathFiles( - ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar) + ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar) .addClasspath(main) .run(parameters.getRuntime(), PKG + ".flexible_upper_bound_app.MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java index 2103f0d..09d3af7 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInClasspathTypeTest.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; @@ -11,6 +11,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -23,10 +24,7 @@ import com.android.tools.r8.utils.codeinspector.KmPackageSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -37,54 +35,45 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInClasspathTypeTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> baseLibJarMap = new HashMap<>(); - private static final Map<KotlinTargetVersion, Path> extLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String baseLibFolder = PKG_PREFIX + "/classpath_lib_base"; - String extLibFolder = PKG_PREFIX + "/classpath_lib_ext"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path baseLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(baseLibFolder, "itf")) - .compile(); - Path extLibJar = - kotlinc(KOTLINC, targetVersion) - .addClasspathFiles(baseLibJar) - .addSourceFiles(getKotlinFileInTest(extLibFolder, "impl")) - .compile(); - baseLibJarMap.put(targetVersion, baseLibJar); - extLibJarMap.put(targetVersion, extLibJar); - } - } + private static final KotlinCompileMemoizer baseLibJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/classpath_lib_base", "itf")); + private static final KotlinCompileMemoizer extLibJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/classpath_lib_ext", "impl")) + .configure( + kotlinCompilerTool -> { + kotlinCompilerTool.addClasspathFiles( + baseLibJarMap.getForConfiguration( + kotlinCompilerTool.getCompiler(), kotlinCompilerTool.getTargetVersion())); + }); @Test public void smokeTest() throws Exception { - Path baseLibJar = baseLibJarMap.get(targetVersion); - Path extLibJar = extLibJarMap.get(targetVersion); + Path baseLibJar = baseLibJarMap.getForConfiguration(kotlinc, targetVersion); + Path extLibJar = extLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(baseLibJar, extLibJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/classpath_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar, extLibJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), baseLibJar, extLibJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".classpath_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -92,11 +81,11 @@ @Test public void testMetadataInClasspathType_renamed() throws Exception { - Path baseLibJar = baseLibJarMap.get(targetVersion); + Path baseLibJar = baseLibJarMap.getForConfiguration(kotlinc, targetVersion); Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(baseLibJar, ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(extLibJarMap.get(targetVersion)) + .addClasspathFiles(baseLibJar, ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(extLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep the Extra class and its interface (which has the method). .addKeepRules("-keep class **.Extra") // Keep Super, but allow minification. @@ -110,14 +99,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(baseLibJar, libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/classpath_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJar, libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), baseLibJar, libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".classpath_app.MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java index 4c49518..d025713 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed; @@ -12,6 +12,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -25,10 +26,7 @@ import com.android.tools.r8.utils.codeinspector.MethodSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -46,45 +44,36 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInCompanionTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static Map<KotlinTargetVersion, Path> companionLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String companionLibFolder = PKG_PREFIX + "/companion_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path companionLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(companionLibFolder, "lib")) - .compile(); - companionLibJarMap.put(targetVersion, companionLibJar); - } - } + private static final KotlinCompileMemoizer companionLibJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/companion_lib", "lib")); @Test public void smokeTest() throws Exception { - Path libJar = companionLibJarMap.get(targetVersion); + Path libJar = companionLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/companion_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".companion_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -94,8 +83,8 @@ public void testMetadataInCompanion_kept() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(companionLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(companionLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep everything .addKeepRules("-keep class **.companion_lib.** { *; }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) @@ -109,14 +98,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/companion_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".companion_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -126,8 +115,8 @@ public void testMetadataInCompanion_renamed() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(companionLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(companionLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep the B class and its interface (which has the doStuff method). .addKeepRules("-keep class **.B") // Property in companion with @JvmField is defined in the host class, without accessors. @@ -151,14 +140,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/companion_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".companion_app.MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java index 4f8b4a4..ac363d4 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionFunctionTest.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; @@ -13,6 +13,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -28,10 +29,7 @@ import com.android.tools.r8.utils.codeinspector.KmValueParameterSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -43,45 +41,36 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInExtensionFunctionTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> extLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String extLibFolder = PKG_PREFIX + "/extension_function_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path extLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(extLibFolder, "B")) - .compile(); - extLibJarMap.put(targetVersion, extLibJar); - } - } + private static final KotlinCompileMemoizer extLibJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/extension_function_lib", "B")); @Test public void smokeTest() throws Exception { - Path libJar = extLibJarMap.get(targetVersion); + Path libJar = extLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_function_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".extension_function_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -92,7 +81,7 @@ public void testMetadataInExtensionFunction_merged() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addProgramFiles(extLibJarMap.get(targetVersion)) + .addProgramFiles(extLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep the B class and its interface (which has the doStuff method). .addKeepRules("-keep class **.B") .addKeepRules("-keep class **.I { <methods>; }") @@ -108,14 +97,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_function_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".extension_function_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -143,8 +132,8 @@ public void testMetadataInExtensionFunction_renamed() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(extLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(extLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep the B class and its interface (which has the doStuff method). .addKeepRules("-keep class **.B") .addKeepRules("-keep class **.I { <methods>; }") @@ -162,14 +151,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_function_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".extension_function_app.MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java index 9e81bf2..327ec34 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInExtensionPropertyTest.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction; import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; @@ -14,6 +14,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -27,10 +28,7 @@ import com.android.tools.r8.utils.codeinspector.KmPropertySubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -42,45 +40,36 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInExtensionPropertyTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> extLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String extLibFolder = PKG_PREFIX + "/extension_property_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path extLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(extLibFolder, "B")) - .compile(); - extLibJarMap.put(targetVersion, extLibJar); - } - } + private static final KotlinCompileMemoizer extLibJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/extension_property_lib", "B")); @Test public void smokeTest() throws Exception { - Path libJar = extLibJarMap.get(targetVersion); + Path libJar = extLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_property_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".extension_property_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -91,7 +80,7 @@ public void testMetadataInExtensionProperty_merged() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addProgramFiles(extLibJarMap.get(targetVersion)) + .addProgramFiles(extLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep the B class and its interface (which has the doStuff method). .addKeepRules("-keep class **.B") .addKeepRules("-keep class **.I { <methods>; }") @@ -104,14 +93,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_property_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".extension_property_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -151,8 +140,8 @@ public void testMetadataInExtensionProperty_renamed() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(extLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(extLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep the B class and its interface (which has the doStuff method). .addKeepRules("-keep class **.B") .addKeepRules("-keep class **.I { <methods>; }") @@ -167,14 +156,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/extension_property_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".extension_property_app.MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java index dcb6dbe..91ed797 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionTest.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; @@ -13,6 +13,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -26,10 +27,7 @@ import com.android.tools.r8.utils.codeinspector.MethodSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -41,45 +39,36 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0} target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInFunctionTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> funLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String funLibFolder = PKG_PREFIX + "/function_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path funLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(funLibFolder, "B")) - .compile(); - funLibJarMap.put(targetVersion, funLibJar); - } - } + private static final KotlinCompileMemoizer funLibJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/function_lib", "B")); @Test public void smokeTest() throws Exception { - Path libJar = funLibJarMap.get(targetVersion); + Path libJar = funLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/function_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".function_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -90,7 +79,7 @@ public void testMetadataInFunction_merged() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addProgramFiles(funLibJarMap.get(targetVersion)) + .addProgramFiles(funLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep the B class and its interface (which has the doStuff method). .addKeepRules("-keep class **.B") .addKeepRules("-keep class **.I { <methods>; }") @@ -102,14 +91,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/function_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".function_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -148,8 +137,8 @@ public void testMetadataInFunction_renamed() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(funLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(funLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep the B class and its interface (which has the doStuff method). .addKeepRules("-keep class **.B") .addKeepRules("-keep class **.I { <methods>; }") @@ -163,14 +152,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/function_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".function_app.MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java index 4cd9b6a..c9210a1 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithDefaultValueTest.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; @@ -12,6 +12,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -27,10 +28,7 @@ import com.android.tools.r8.utils.codeinspector.MethodSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -41,45 +39,36 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInFunctionWithDefaultValueTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static Map<KotlinTargetVersion, Path> defaultValueLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String default_valueLibFolder = PKG_PREFIX + "/default_value_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path default_valueLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(default_valueLibFolder, "lib")) - .compile(); - defaultValueLibJarMap.put(targetVersion, default_valueLibJar); - } - } + private static final KotlinCompileMemoizer defaultValueLibJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/default_value_lib", "lib")); @Test public void smokeTest() throws Exception { - Path libJar = defaultValueLibJarMap.get(targetVersion); + Path libJar = defaultValueLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/default_value_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".default_value_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -89,8 +78,9 @@ public void testMetadataInFunctionWithDefaultValue() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addLibraryFiles(ToolHelper.getJava8RuntimeJar(), ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(defaultValueLibJarMap.get(targetVersion)) + .addLibraryFiles( + ToolHelper.getJava8RuntimeJar(), ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(defaultValueLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep LibKt and applyMap function, along with applyMap$default .addKeepRules("-keep class **.LibKt { *** applyMap*(...); }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) @@ -102,14 +92,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/default_value_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".default_value_app.MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java index 0914a23..a65c4b9 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInFunctionWithVarargTest.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; @@ -13,6 +13,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -28,10 +29,7 @@ import com.android.tools.r8.utils.codeinspector.MethodSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -42,45 +40,36 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInFunctionWithVarargTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static Map<KotlinTargetVersion, Path> varargLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String varargLibFolder = PKG_PREFIX + "/vararg_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path varargLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(varargLibFolder, "lib")) - .compile(); - varargLibJarMap.put(targetVersion, varargLibJar); - } - } + private static final KotlinCompileMemoizer varargLibJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/vararg_lib", "lib")); @Test public void smokeTest() throws Exception { - Path libJar = varargLibJarMap.get(targetVersion); + Path libJar = varargLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/vararg_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".vararg_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -90,8 +79,8 @@ public void testMetadataInFunctionWithVararg() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(varargLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(varargLibJarMap.getForConfiguration(kotlinc, targetVersion)) // keep SomeClass#foo, since there is a method reference in the app. .addKeepRules("-keep class **.SomeClass { *** foo(...); }") // Keep LibKt, along with bar function. @@ -105,14 +94,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/vararg_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".vararg_app.MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java index 159bf66..e635091 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInLibraryTypeTest.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; import static org.hamcrest.CoreMatchers.anyOf; @@ -11,6 +11,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -21,9 +22,6 @@ import com.android.tools.r8.utils.codeinspector.KmPackageSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -34,54 +32,51 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInLibraryTypeTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> baseLibJarMap = new HashMap<>(); - private static final Map<KotlinTargetVersion, Path> extLibJarMap = new HashMap<>(); - private static final Map<KotlinTargetVersion, Path> appJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String baseLibFolder = PKG_PREFIX + "/libtype_lib_base"; - String extLibFolder = PKG_PREFIX + "/libtype_lib_ext"; - String appFolder = PKG_PREFIX + "/libtype_app"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path baseLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(baseLibFolder, "base")) - .compile(); - Path extLibJar = - kotlinc(KOTLINC, targetVersion) - .addClasspathFiles(baseLibJar) - .addSourceFiles(getKotlinFileInTest(extLibFolder, "ext")) - .compile(); - Path appJar = - kotlinc(KOTLINC, targetVersion) - .addClasspathFiles(baseLibJar) - .addClasspathFiles(extLibJar) - .addSourceFiles(getKotlinFileInTest(appFolder, "main")) - .compile(); - baseLibJarMap.put(targetVersion, baseLibJar); - extLibJarMap.put(targetVersion, extLibJar); - appJarMap.put(targetVersion, appJar); - } - } + private static final KotlinCompileMemoizer baseLibJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/libtype_lib_base", "base")); + private static final KotlinCompileMemoizer extLibJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/libtype_lib_ext", "ext")) + .configure( + kotlinCompilerTool -> { + kotlinCompilerTool.addClasspathFiles( + baseLibJarMap.getForConfiguration( + kotlinCompilerTool.getCompiler(), kotlinCompilerTool.getTargetVersion())); + }); + private static final KotlinCompileMemoizer appJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/libtype_app", "main")) + .configure( + kotlinCompilerTool -> { + kotlinCompilerTool.addClasspathFiles( + baseLibJarMap.getForConfiguration( + kotlinCompilerTool.getCompiler(), kotlinCompilerTool.getTargetVersion())); + kotlinCompilerTool.addClasspathFiles( + extLibJarMap.getForConfiguration( + kotlinCompilerTool.getCompiler(), kotlinCompilerTool.getTargetVersion())); + }); @Test public void smokeTest() throws Exception { testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJarMap.get(targetVersion)) - .addClasspath(extLibJarMap.get(targetVersion), appJarMap.get(targetVersion)) + .addRunClasspathFiles( + ToolHelper.getKotlinStdlibJar(kotlinc), + baseLibJarMap.getForConfiguration(kotlinc, targetVersion)) + .addClasspath( + extLibJarMap.getForConfiguration(kotlinc, targetVersion), + appJarMap.getForConfiguration(kotlinc, targetVersion)) .run(parameters.getRuntime(), PKG + ".libtype_app.MainKt") .assertSuccessWithOutput(EXPECTED); } @@ -93,7 +88,9 @@ testForR8(parameters.getBackend()) // Intentionally not providing baseLibJar as lib file nor classpath file. .addClasspathFiles() - .addProgramFiles(extLibJarMap.get(targetVersion), appJarMap.get(targetVersion)) + .addProgramFiles( + extLibJarMap.getForConfiguration(kotlinc, targetVersion), + appJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep Ext extension method which requires metadata to be called with Kotlin syntax // from other kotlin code. .addKeepRules("-keep class **.ExtKt { <methods>; }") @@ -112,7 +109,9 @@ .writeToZip(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), baseLibJarMap.get(targetVersion)) + .addRunClasspathFiles( + ToolHelper.getKotlinStdlibJar(kotlinc), + baseLibJarMap.getForConfiguration(kotlinc, targetVersion)) .addClasspath(out) .run(parameters.getRuntime(), main) .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java index 45a419d..8adeb48 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInMultifileClassTest.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; @@ -16,6 +16,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -31,11 +32,8 @@ import com.android.tools.r8.utils.codeinspector.MethodSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; import kotlinx.metadata.jvm.KotlinClassMetadata; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -46,47 +44,38 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInMultifileClassTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> multifileLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String multifileLibFolder = PKG_PREFIX + "/multifileclass_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path multifileLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles( - getKotlinFileInTest(multifileLibFolder, "signed"), - getKotlinFileInTest(multifileLibFolder, "unsigned")) - .compile(); - multifileLibJarMap.put(targetVersion, multifileLibJar); - } - } + private static final KotlinCompileMemoizer multifileLibJarMap = + getCompileMemoizer( + getKotlinFileInTest(PKG_PREFIX + "/multifileclass_lib", "signed"), + getKotlinFileInTest(PKG_PREFIX + "/multifileclass_lib", "unsigned")); @Test public void smokeTest() throws Exception { - Path libJar = multifileLibJarMap.get(targetVersion); + Path libJar = multifileLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/multifileclass_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".multifileclass_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -96,8 +85,8 @@ public void testMetadataInMultifileClass_merged() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(multifileLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(multifileLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep UtilKt#comma*Join*(). Let R8 optimize (inline) others, such as joinOf*(String). .addKeepRules("-keep class **.UtilKt") .addKeepRules("-keepclassmembers class * { ** comma*Join*(...); }") @@ -107,7 +96,7 @@ .writeToZip(); ProcessResult kotlinTestCompileResult = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/multifileclass_app", "main")) .setOutputPath(temp.newFolder().toPath()) @@ -137,8 +126,8 @@ public void testMetadataInMultifileClass_renamed() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(multifileLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(multifileLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep UtilKt#comma*Join*(). .addKeepRules("-keep class **.UtilKt") .addKeepRules("-keep,allowobfuscation class **.UtilKt__SignedKt") @@ -151,7 +140,7 @@ .writeToZip(); ProcessResult kotlinTestCompileResult = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/multifileclass_app", "main")) .setOutputPath(temp.newFolder().toPath())
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java index eada4da..8bb4603 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInNestedClassTest.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; @@ -12,6 +12,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -22,9 +23,6 @@ import com.android.tools.r8.utils.codeinspector.KmClassSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -36,45 +34,36 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInNestedClassTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> nestedLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String nestedLibFolder = PKG_PREFIX + "/nested_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path nestedLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(nestedLibFolder, "lib")) - .compile(); - nestedLibJarMap.put(targetVersion, nestedLibJar); - } - } + private static final KotlinCompileMemoizer nestedLibJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/nested_lib", "lib")); @Test public void smokeTest() throws Exception { - Path libJar = nestedLibJarMap.get(targetVersion); + Path libJar = nestedLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/nested_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".nested_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -84,8 +73,8 @@ public void testMetadataInNestedClass() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(nestedLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(nestedLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep the Outer class and delegations. .addKeepRules("-keep class **.Outer { <init>(...); *** delegate*(...); }") // Keep Inner to check the hierarchy. @@ -100,14 +89,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/nested_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".nested_app.MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java index b4ae665..a2a85c3 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInParameterTypeTest.java
@@ -3,13 +3,14 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -20,10 +21,7 @@ import com.android.tools.r8.utils.codeinspector.KmClassSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -34,45 +32,36 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInParameterTypeTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> parameterTypeLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String parameterTypeLibFolder = PKG_PREFIX + "/parametertype_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path parameterTypeLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(parameterTypeLibFolder, "lib")) - .compile(); - parameterTypeLibJarMap.put(targetVersion, parameterTypeLibJar); - } - } + private static final KotlinCompileMemoizer parameterTypeLibJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/parametertype_lib", "lib")); @Test public void smokeTest() throws Exception { - Path libJar = parameterTypeLibJarMap.get(targetVersion); + Path libJar = parameterTypeLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/parametertype_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".parametertype_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -82,8 +71,8 @@ public void testMetadataInParameterType_renamed() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(parameterTypeLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(parameterTypeLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep non-private members of Impl .addKeepRules("-keep public class **.Impl { !private *; }") // Keep Itf, but allow minification. @@ -94,14 +83,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/parametertype_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".parametertype_app.MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java index c479093..d012f10 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTest.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionProperty; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; @@ -14,6 +14,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -27,9 +28,6 @@ import com.android.tools.r8.utils.codeinspector.MethodSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -40,38 +38,29 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInPropertyTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> propertyTypeLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String propertyTypeLibFolder = PKG_PREFIX + "/fragile_property_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path propertyTypeLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(propertyTypeLibFolder, "lib")) - .compile(); - propertyTypeLibJarMap.put(targetVersion, propertyTypeLibJar); - } - } + private static final KotlinCompileMemoizer propertyTypeLibJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/fragile_property_lib", "lib")); @Test public void smokeTest_getterApp() throws Exception { - Path libJar = propertyTypeLibJarMap.get(targetVersion); + Path libJar = propertyTypeLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(PKG_PREFIX + "/fragile_property_only_getter", "getter_user")) @@ -79,7 +68,7 @@ .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".fragile_property_only_getter.Getter_userKt") .assertSuccessWithOutput(EXPECTED_GETTER); @@ -89,8 +78,8 @@ public void testMetadataInProperty_getterOnly() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(propertyTypeLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(propertyTypeLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep property getters .addKeepRules("-keep class **.Person { <init>(...); }") .addKeepRules("-keepclassmembers class **.Person { *** get*(); }") @@ -100,7 +89,7 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(PKG_PREFIX + "/fragile_property_only_getter", "getter_user")) @@ -108,7 +97,7 @@ .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".fragile_property_only_getter.Getter_userKt") .assertSuccessWithOutput(EXPECTED_GETTER); @@ -159,10 +148,10 @@ @Test public void smokeTest_setterApp() throws Exception { - Path libJar = propertyTypeLibJarMap.get(targetVersion); + Path libJar = propertyTypeLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(PKG_PREFIX + "/fragile_property_only_setter", "setter_user")) @@ -170,7 +159,7 @@ .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".fragile_property_only_setter.Setter_userKt") .assertSuccessWithOutputLines(); @@ -180,8 +169,8 @@ public void testMetadataInProperty_setterOnly() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(propertyTypeLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(propertyTypeLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep property setters (and users) .addKeepRules("-keep class **.Person { <init>(...); }") .addKeepRules("-keepclassmembers class **.Person { void set*(...); }") @@ -195,7 +184,7 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(PKG_PREFIX + "/fragile_property_only_setter", "setter_user")) @@ -203,7 +192,7 @@ .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".fragile_property_only_setter.Setter_userKt") .assertSuccessWithOutputLines();
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java index eb9ff61..9641f64 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInPropertyTypeTest.java
@@ -3,13 +3,14 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -20,10 +21,7 @@ import com.android.tools.r8.utils.codeinspector.KmClassSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -34,45 +32,36 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInPropertyTypeTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> propertyTypeLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String propertyTypeLibFolder = PKG_PREFIX + "/propertytype_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path propertyTypeLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(propertyTypeLibFolder, "lib")) - .compile(); - propertyTypeLibJarMap.put(targetVersion, propertyTypeLibJar); - } - } + private static final KotlinCompileMemoizer propertyTypeLibJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/propertytype_lib", "lib")); @Test public void smokeTest() throws Exception { - Path libJar = propertyTypeLibJarMap.get(targetVersion); + Path libJar = propertyTypeLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/propertytype_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".propertytype_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -82,8 +71,8 @@ public void testMetadataInProperty_renamed() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(propertyTypeLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(propertyTypeLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep non-private members of Impl .addKeepRules("-keep public class **.Impl { !private *; }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) @@ -92,14 +81,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/propertytype_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".propertytype_app.MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java index 8be744d..a4a3a68 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInRenamedTypeTest.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromKotlinClassifier; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; @@ -12,6 +12,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -20,11 +21,7 @@ import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; import com.android.tools.r8.utils.codeinspector.KmClassSubject; -import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -36,47 +33,39 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withAllRuntimesAndApiLevels().build(), KotlinTargetVersion.values()); + getTestParameters().withAllRuntimesAndApiLevels().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInRenamedTypeTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> annoJarMap = new HashMap<>(); - private static final Map<KotlinTargetVersion, Path> inputJarMap = new HashMap<>(); - - @BeforeClass - public static void createInputJar() throws Exception { - String inputFolder = PKG_PREFIX + "/anno"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path annoJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(inputFolder, "Anno")) - .compile(); - Path inputJar = - kotlinc(KOTLINC, targetVersion) - .addClasspathFiles(annoJar) - .addSourceFiles(getKotlinFileInTest(inputFolder, "main")) - .compile(); - annoJarMap.put(targetVersion, annoJar); - inputJarMap.put(targetVersion, inputJar); - } - } + private static final KotlinCompileMemoizer annoJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/anno", "Anno")); + private static final KotlinCompileMemoizer inputJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/anno", "main")) + .configure( + kotlinCompilerTool -> { + kotlinCompilerTool.addClasspathFiles( + annoJarMap.getForConfiguration( + kotlinCompilerTool.getCompiler(), kotlinCompilerTool.getTargetVersion())); + }); @Test public void testR8_kotlinStdlibAsLib() throws Exception { testForR8(parameters.getBackend()) .addLibraryFiles( - annoJarMap.get(targetVersion), + annoJarMap.getForConfiguration(kotlinc, targetVersion), ToolHelper.getJava8RuntimeJar(), - ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(inputJarMap.get(targetVersion)) + ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(inputJarMap.getForConfiguration(kotlinc, targetVersion)) .addKeepRules(OBFUSCATE_RENAMED, KEEP_KEPT) .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) .compile() @@ -86,8 +75,10 @@ @Test public void testR8_kotlinStdlibAsClassPath() throws Exception { testForR8(parameters.getBackend()) - .addClasspathFiles(annoJarMap.get(targetVersion), ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(inputJarMap.get(targetVersion)) + .addClasspathFiles( + annoJarMap.getForConfiguration(kotlinc, targetVersion), + ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(inputJarMap.getForConfiguration(kotlinc, targetVersion)) .addKeepRules(OBFUSCATE_RENAMED, KEEP_KEPT) .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) .compile() @@ -97,8 +88,10 @@ @Test public void testR8_kotlinStdlibAsProgramFile() throws Exception { testForR8(parameters.getBackend()) - .addProgramFiles(annoJarMap.get(targetVersion), ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(inputJarMap.get(targetVersion)) + .addProgramFiles( + annoJarMap.getForConfiguration(kotlinc, targetVersion), + ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(inputJarMap.getForConfiguration(kotlinc, targetVersion)) .addKeepRules(OBFUSCATE_RENAMED, KEEP_KEPT) .addKeepRules("-keep class **.Anno") .addKeepKotlinMetadata()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java index 47b0b82..9783f3b 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInReturnTypeTest.java
@@ -3,13 +3,14 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -20,10 +21,7 @@ import com.android.tools.r8.utils.codeinspector.KmClassSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -34,45 +32,36 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInReturnTypeTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> returnTypeLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String returnTypeLibFolder = PKG_PREFIX + "/returntype_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path returnTypeLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(returnTypeLibFolder, "lib")) - .compile(); - returnTypeLibJarMap.put(targetVersion, returnTypeLibJar); - } - } + private static final KotlinCompileMemoizer returnTypeLibJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/returntype_lib", "lib")); @Test public void smokeTest() throws Exception { - Path libJar = returnTypeLibJarMap.get(targetVersion); + Path libJar = returnTypeLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/returntype_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".returntype_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -82,8 +71,8 @@ public void testMetadataInReturnType_renamed() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(returnTypeLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(returnTypeLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep non-private members of Impl .addKeepRules("-keep public class **.Impl { !private *; }") // Keep Itf, but allow minification. @@ -94,14 +83,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/returntype_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".returntype_app.MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassNestedTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassNestedTest.java index 3fb7b23..f33fc30 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassNestedTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassNestedTest.java
@@ -3,8 +3,9 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -13,9 +14,6 @@ import com.android.tools.r8.utils.StringUtils; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -33,37 +31,29 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInSealedClassNestedTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> sealedLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path sealedLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles( - getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "nested")) - .compile(); - sealedLibJarMap.put(targetVersion, sealedLibJar); - } - } + private static final KotlinCompileMemoizer sealedLibJarMap = + getCompileMemoizer( + getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "nested")); @Test public void smokeTest() throws Exception { - Path libJar = sealedLibJarMap.get(targetVersion); + Path libJar = sealedLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) @@ -71,7 +61,7 @@ .compile(); testForJvm() .addRunClasspathFiles( - ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar) + ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG_APP + ".MainKt") .assertSuccessWithOutput(EXPECTED); @@ -81,8 +71,8 @@ public void testMetadataInSealedClass_nested() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(sealedLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(sealedLibJarMap.getForConfiguration(kotlinc, targetVersion)) .addKeepAllClassesRule() .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) .addKeepAttributes( @@ -90,7 +80,7 @@ .compile() .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) @@ -98,7 +88,7 @@ .compile(); testForJvm() .addRunClasspathFiles( - ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar) + ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG_APP + ".MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java index 1bb1c44..2dbbc48 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSealedClassTest.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.DescriptorUtils.descriptorToJavaType; import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; @@ -15,6 +15,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -28,9 +29,6 @@ import com.android.tools.r8.utils.codeinspector.KmPackageSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -41,45 +39,36 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInSealedClassTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> sealedLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String sealedLibFolder = PKG_PREFIX + "/sealed_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path sealedLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(sealedLibFolder, "lib")) - .compile(); - sealedLibJarMap.put(targetVersion, sealedLibJar); - } - } + private static final KotlinCompileMemoizer sealedLibJarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/sealed_lib", "lib")); @Test public void smokeTest() throws Exception { - Path libJar = sealedLibJarMap.get(targetVersion); + Path libJar = sealedLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/sealed_app", "valid")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".sealed_app.ValidKt") .assertSuccessWithOutput(EXPECTED); @@ -89,8 +78,8 @@ public void testMetadataInSealedClass_valid() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(sealedLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(sealedLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep the Expr class .addKeepRules("-keep class **.Expr") // Keep the extension function @@ -103,14 +92,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/sealed_app", "valid")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".sealed_app.ValidKt") .assertSuccessWithOutput(EXPECTED); @@ -156,8 +145,8 @@ public void testMetadataInSealedClass_invalid() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(sealedLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(sealedLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep the Expr class .addKeepRules("-keep class **.Expr") // Keep the extension function @@ -168,7 +157,7 @@ .writeToZip(); ProcessResult kotlinTestCompileResult = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFilesWithNonKtExtension( temp, getFileInTest(PKG_PREFIX + "/sealed_app", "invalid.kt_txt"))
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java index 578a7ec..9b0da43 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInSuperTypeTest.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed; @@ -11,6 +11,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -21,10 +22,7 @@ import com.android.tools.r8.utils.codeinspector.KmClassSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -35,47 +33,38 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInSuperTypeTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> superTypeLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String superTypeLibFolder = PKG_PREFIX + "/supertype_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path superTypeLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles( - getKotlinFileInTest(superTypeLibFolder, "impl"), - getKotlinFileInTest(superTypeLibFolder + "/internal", "itf")) - .compile(); - superTypeLibJarMap.put(targetVersion, superTypeLibJar); - } - } + private static final KotlinCompileMemoizer superTypeLibJarMap = + getCompileMemoizer( + getKotlinFileInTest(PKG_PREFIX + "/supertype_lib", "impl"), + getKotlinFileInTest(PKG_PREFIX + "/supertype_lib" + "/internal", "itf")); @Test public void smokeTest() throws Exception { - Path libJar = superTypeLibJarMap.get(targetVersion); + Path libJar = superTypeLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/supertype_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".supertype_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -85,8 +74,8 @@ public void testMetadataInSupertype_merged() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(superTypeLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(superTypeLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep non-private members except for ones in `internal` definitions. .addKeepRules("-keep public class !**.internal.**, * { !private *; }") .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) @@ -95,14 +84,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/supertype_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".supertype_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -130,8 +119,8 @@ public void testMetadataInSupertype_renamed() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(superTypeLibJarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(superTypeLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep non-private members except for ones in `internal` definitions. .addKeepRules("-keep public class !**.internal.**, * { !private *; }") // Keep `internal` definitions, but allow minification. @@ -142,14 +131,14 @@ .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/supertype_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".supertype_app.MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java index ac4085f..edade6e 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeAliasTest.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isDexClass; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; @@ -13,6 +13,7 @@ import static junit.framework.TestCase.assertTrue; import static org.hamcrest.MatcherAssert.assertThat; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -30,9 +31,6 @@ import com.android.tools.r8.utils.codeinspector.MethodSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -63,40 +61,31 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInTypeAliasTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> typeAliasLibJarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String typeAliasLibFolder = PKG_PREFIX + "/typealias_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path typeAliasLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles( - getKotlinFileInTest(typeAliasLibFolder, "lib"), - getKotlinFileInTest(typeAliasLibFolder, "lib_ext")) - .compile(); - typeAliasLibJarMap.put(targetVersion, typeAliasLibJar); - } - } + private static final KotlinCompileMemoizer typeAliasLibJarMap = + getCompileMemoizer( + getKotlinFileInTest(PKG_PREFIX + "/typealias_lib", "lib"), + getKotlinFileInTest(PKG_PREFIX + "/typealias_lib", "lib_ext")); @Test public void smokeTest() throws Exception { - Path libJar = typeAliasLibJarMap.get(targetVersion); + Path libJar = typeAliasLibJarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typealias_app", "main")) .setOutputPath(temp.newFolder().toPath()) @@ -104,7 +93,7 @@ testForJvm() .addRunClasspathFiles( - ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar) + ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".typealias_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -116,8 +105,9 @@ String renamedSuperTypeName = "com.android.tools.r8.kotlin.metadata.typealias_lib.FooBar"; Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar()) - .addProgramFiles(typeAliasLibJarMap.get(targetVersion)) + .addClasspathFiles( + ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc)) + .addProgramFiles(typeAliasLibJarMap.getForConfiguration(kotlinc, targetVersion)) // Keep non-private members of Impl .addKeepRules("-keep class **.Impl { !private *; }") // Keep but allow obfuscation of types. @@ -144,14 +134,14 @@ .writeToZip(); Path appJar = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typealias_app", "main")) .compile(); testForJvm() .addRunClasspathFiles( - ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar) + ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar) .addClasspath(appJar) .run(parameters.getRuntime(), PKG + ".typealias_app.MainKt") .assertSuccessWithOutput(EXPECTED.replace(superTypeName, renamedSuperTypeName));
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java index 52ae0b3..788abf2 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInTypeArgumentsTest.java
@@ -3,7 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isDexClass; import static com.android.tools.r8.utils.codeinspector.Matchers.isExtensionFunction; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; @@ -12,6 +12,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -30,12 +31,9 @@ import com.android.tools.r8.utils.codeinspector.KmValueParameterSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; import kotlinx.metadata.KmClassifier.TypeParameter; import kotlinx.metadata.KmVariance; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -79,45 +77,36 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInTypeArgumentsTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static final Map<KotlinTargetVersion, Path> jarMap = new HashMap<>(); - - @BeforeClass - public static void createLibJar() throws Exception { - String typeAliasLibFolder = PKG_PREFIX + "/typeargument_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path typeAliasLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(typeAliasLibFolder, "lib")) - .compile(); - jarMap.put(targetVersion, typeAliasLibJar); - } - } + private static final KotlinCompileMemoizer jarMap = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/typeargument_lib", "lib")); @Test public void smokeTest() throws Exception { - Path libJar = jarMap.get(targetVersion); + Path libJar = jarMap.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typeargument_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".typeargument_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -127,8 +116,8 @@ public void testMetadataInTypeAliasWithR8() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(jarMap.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(jarMap.getForConfiguration(kotlinc, targetVersion)) // Keep ClassThatWillBeObfuscated, but allow minification. .addKeepRules("-keep,allowobfuscation class **ClassThatWillBeObfuscated") .addKeepRules("-keepclassmembers class **ClassThatWillBeObfuscated { *; }") @@ -148,12 +137,12 @@ .inspect(this::inspect) .writeToZip(); Path mainJar = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/typeargument_app", "main")) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(mainJar) .run(parameters.getRuntime(), PKG + ".typeargument_app.MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java index ffe5a51..133dd37 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInlinePropertyTest.java
@@ -4,11 +4,12 @@ package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertNull; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -21,13 +22,9 @@ import java.io.IOException; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ExecutionException; import junit.framework.TestCase; import kotlinx.metadata.jvm.KotlinClassHeader; import kotlinx.metadata.jvm.KotlinClassMetadata; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -36,46 +33,36 @@ public class MetadataRewriteInlinePropertyTest extends KotlinMetadataTestBase { private final String EXPECTED = StringUtils.lines("true", "false", "false", "true"); - private final String PKG_LIB = PKG + ".inline_property_lib"; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewriteInlinePropertyTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static Map<KotlinTargetVersion, Path> libJars = new HashMap<>(); + private static final KotlinCompileMemoizer libJars = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/inline_property_lib", "lib")); private final TestParameters parameters; - @BeforeClass - public static void createLibJar() throws Exception { - String baseLibFolder = PKG_PREFIX + "/inline_property_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path baseLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(baseLibFolder, "lib")) - .compile(); - libJars.put(targetVersion, baseLibJar); - } - } - @Test public void smokeTest() throws Exception { - Path libJar = libJars.get(targetVersion); + Path libJar = libJars.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/inline_property_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG + ".inline_property_app.MainKt") .assertSuccessWithOutput(EXPECTED); @@ -85,8 +72,8 @@ public void testMetadataForLib() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(libJars.get(targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion)) // Allow renaming A to ensure that we rename in the flexible upper bound type. .addKeepAllClassesRule() .addKeepAttributes( @@ -98,21 +85,22 @@ .inspect(this::inspect) .writeToZip(); Path main = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles(getKotlinFileInTest(PKG_PREFIX + "/inline_property_app", "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() .addRunClasspathFiles( - ToolHelper.getKotlinStdlibJar(), ToolHelper.getKotlinReflectJar(), libJar) + ToolHelper.getKotlinStdlibJar(kotlinc), ToolHelper.getKotlinReflectJar(kotlinc), libJar) .addClasspath(main) .run(parameters.getRuntime(), PKG + ".inline_property_app.MainKt") .assertSuccessWithOutput(EXPECTED); } - private void inspect(CodeInspector inspector) throws IOException, ExecutionException { - CodeInspector stdLibInspector = new CodeInspector(libJars.get(targetVersion)); + private void inspect(CodeInspector inspector) throws IOException { + CodeInspector stdLibInspector = + new CodeInspector(libJars.getForConfiguration(kotlinc, targetVersion)); for (FoundClassSubject clazzSubject : stdLibInspector.allClasses()) { ClassSubject r8Clazz = inspector.clazz(clazzSubject.getOriginalName()); assertThat(r8Clazz, isPresent());
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java index 2d3834b..83b8e62 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteJvmStaticTest.java
@@ -3,13 +3,13 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.kotlin.metadata.jvmstatic_app.MainJava; @@ -23,8 +23,7 @@ import com.android.tools.r8.utils.codeinspector.KmPropertySubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; import java.nio.file.Path; -import java.nio.file.Paths; -import org.junit.BeforeClass; +import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -39,39 +38,34 @@ private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") - public static TestParametersCollection data() { - return getTestParameters().withCfRuntimes().build(); + @Parameterized.Parameters(name = "{0}, kotlinc: {1}") + public static List<Object[]> data() { + return buildParameters(getTestParameters().withCfRuntimes().build(), getKotlinCompilers()); } - public MetadataRewriteJvmStaticTest(TestParameters parameters) { + public MetadataRewriteJvmStaticTest(TestParameters parameters, KotlinCompiler kotlinc) { // We are testing static methods on interfaces which requires java 8. - super(KotlinTargetVersion.JAVA_8); + super(KotlinTargetVersion.JAVA_8, kotlinc); this.parameters = parameters; } - private static Path kotlincLibJar = Paths.get(""); - - @BeforeClass - public static void createLibJar() throws Exception { - kotlincLibJar = - kotlinc(KOTLINC, KotlinTargetVersion.JAVA_8) - .addSourceFiles( - getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib")) - .compile(); - } + private static KotlinCompileMemoizer kotlincLibJar = + getCompileMemoizer( + getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib")); @Test public void smokeTest() throws Exception { Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) - .addClasspathFiles(kotlincLibJar) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) + .addClasspathFiles(kotlincLibJar.getForConfiguration(kotlinc, targetVersion)) .addSourceFiles( getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), kotlincLibJar) + .addRunClasspathFiles( + ToolHelper.getKotlinStdlibJar(kotlinc), + kotlincLibJar.getForConfiguration(kotlinc, targetVersion)) .addClasspath(output) .run(parameters.getRuntime(), PKG_APP + ".MainKt") .assertSuccessWithOutput(EXPECTED); @@ -80,7 +74,9 @@ @Test public void smokeTestJava() throws Exception { testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), kotlincLibJar) + .addRunClasspathFiles( + ToolHelper.getKotlinStdlibJar(kotlinc), + kotlincLibJar.getForConfiguration(kotlinc, targetVersion)) .addProgramClassFileData(MainJava.dump()) .run(parameters.getRuntime(), MainJava.class) .assertSuccessWithOutput(EXPECTED); @@ -90,7 +86,7 @@ public void testMetadata() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addProgramFiles(kotlincLibJar) + .addProgramFiles(kotlincLibJar.getForConfiguration(kotlinc, targetVersion)) .addKeepAllClassesRule() .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS) .compile() @@ -102,14 +98,14 @@ private void testKotlin(Path libJar) throws Exception { Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG_APP + ".MainKt") .assertSuccessWithOutput(EXPECTED); @@ -117,7 +113,7 @@ private void testJava(Path libJar) throws Exception { testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addProgramClassFileData(MainJava.dump()) .run(parameters.getRuntime(), MainJava.class) .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java index bb61374..40c7573 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepPathTest.java
@@ -4,12 +4,13 @@ package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestShrinkerBuilder; import com.android.tools.r8.ToolHelper; @@ -17,11 +18,7 @@ import com.android.tools.r8.utils.BooleanUtils; import com.android.tools.r8.utils.codeinspector.ClassSubject; import com.android.tools.r8.utils.codeinspector.CodeInspector; -import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -29,43 +26,36 @@ @RunWith(Parameterized.class) public class MetadataRewriteKeepPathTest extends KotlinMetadataTestBase { - @Parameterized.Parameters(name = "{0} target: {1}, keep: {2}") + @Parameterized.Parameters(name = "{0} target: {1}, kotlinc: {2}, keep: {3}") public static Collection<Object[]> data() { return buildParameters( getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values(), + getKotlinCompilers(), BooleanUtils.values()); } - private static Map<KotlinTargetVersion, Path> libJars = new HashMap<>(); + private static final KotlinCompileMemoizer libJars = + getCompileMemoizer(getKotlinFileInTest(PKG_PREFIX + "/box_primitives_lib", "lib")); private static final String LIB_CLASS_NAME = PKG + ".box_primitives_lib.Test"; private final TestParameters parameters; private final boolean keepMetadata; public MetadataRewriteKeepPathTest( - TestParameters parameters, KotlinTargetVersion targetVersion, boolean keepMetadata) { - super(targetVersion); + TestParameters parameters, + KotlinTargetVersion targetVersion, + KotlinCompiler kotlinc, + boolean keepMetadata) { + super(targetVersion, kotlinc); this.parameters = parameters; this.keepMetadata = keepMetadata; } - @BeforeClass - public static void createLibJar() throws Exception { - String baseLibFolder = PKG_PREFIX + "/box_primitives_lib"; - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path baseLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFileInTest(baseLibFolder, "lib")) - .compile(); - libJars.put(targetVersion, baseLibJar); - } - } - @Test public void testProgramPath() throws Exception { testForR8(parameters.getBackend()) - .addProgramFiles(libJars.get(targetVersion)) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion)) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .addKeepRules("-keep class " + LIB_CLASS_NAME) .applyIf(keepMetadata, TestShrinkerBuilder::addKeepKotlinMetadata) .addKeepRuntimeVisibleAnnotations() @@ -78,8 +68,8 @@ @Test public void testClassPathPath() throws Exception { testForR8(parameters.getBackend()) - .addProgramFiles(libJars.get(targetVersion)) - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .addKeepRules("-keep class " + LIB_CLASS_NAME) .addKeepRuntimeVisibleAnnotations() .compile() @@ -89,8 +79,8 @@ @Test public void testLibraryPath() throws Exception { testForR8(parameters.getBackend()) - .addProgramFiles(libJars.get(targetVersion)) - .addLibraryFiles(ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion)) + .addLibraryFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .addLibraryFiles(ToolHelper.getJava8RuntimeJar()) .addKeepRules("-keep class " + LIB_CLASS_NAME) .addKeepRuntimeVisibleAnnotations() @@ -101,7 +91,7 @@ @Test public void testMissing() throws Exception { testForR8(parameters.getBackend()) - .addProgramFiles(libJars.get(targetVersion)) + .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion)) .addKeepRules("-keep class " + LIB_CLASS_NAME) .addKeepRuntimeVisibleAnnotations() .compile()
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java index 0f80128..8c681bd 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteKeepTest.java
@@ -4,9 +4,11 @@ package com.android.tools.r8.kotlin.metadata; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -21,23 +23,26 @@ @RunWith(Parameterized.class) public class MetadataRewriteKeepTest extends KotlinMetadataTestBase { - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } private final TestParameters parameters; - public MetadataRewriteKeepTest(TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + public MetadataRewriteKeepTest( + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } @Test public void testR8() throws Exception { testForR8(parameters.getBackend()) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .setMinApi(parameters.getApiLevel()) .addKeepKotlinMetadata() .addKeepRules("-keep class kotlin.io.** { *; }") @@ -49,7 +54,7 @@ @Test public void testR8KeepIf() throws Exception { testForR8(parameters.getBackend()) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .setMinApi(parameters.getApiLevel()) .addKeepRules("-keep class kotlin.io.** { *; }") .addKeepRules("-if class *", "-keep class kotlin.Metadata { *; }")
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java index 71d888c..3e62729 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePassThroughTest.java
@@ -4,6 +4,9 @@ package com.android.tools.r8.kotlin.metadata; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; + +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -17,24 +20,26 @@ @RunWith(Parameterized.class) public class MetadataRewritePassThroughTest extends KotlinMetadataTestBase { - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } private final TestParameters parameters; public MetadataRewritePassThroughTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } @Test public void testKotlinStdLib() throws Exception { testForR8(parameters.getBackend()) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .setMinApi(parameters.getApiLevel()) .addKeepAllClassesRule() .addKeepKotlinMetadata() @@ -42,6 +47,7 @@ .compile() .inspect( inspector -> - assertEqualMetadata(new CodeInspector(ToolHelper.getKotlinStdlibJar()), inspector)); + assertEqualMetadata( + new CodeInspector(ToolHelper.getKotlinStdlibJar(kotlinc)), inspector)); } }
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java index 2a33484..39793cf 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewritePrunedObjectsTest.java
@@ -4,12 +4,13 @@ package com.android.tools.r8.kotlin.metadata; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static junit.framework.TestCase.assertEquals; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -20,9 +21,6 @@ import com.android.tools.r8.utils.codeinspector.KmClassSubject; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -34,45 +32,37 @@ private static final String PKG_LIB = PKG + ".pruned_lib"; private static final String PKG_APP = PKG + ".pruned_app"; - private static Map<KotlinTargetVersion, Path> libJars = new HashMap<>(); + private static final KotlinCompileMemoizer libJars = + getCompileMemoizer( + getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib")); private final TestParameters parameters; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withCfRuntimes().build(), KotlinTargetVersion.values()); + getTestParameters().withCfRuntimes().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } public MetadataRewritePrunedObjectsTest( - TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - @BeforeClass - public static void createLibJar() throws Exception { - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - Path baseLibJar = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles( - getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_LIB), "lib")) - .compile(); - libJars.put(targetVersion, baseLibJar); - } - } - @Test public void smokeTest() throws Exception { - Path libJar = libJars.get(targetVersion); + Path libJar = libJars.getForConfiguration(kotlinc, targetVersion); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) .setOutputPath(temp.newFolder().toPath()) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addClasspath(output) .run(parameters.getRuntime(), PKG_APP + ".MainKt") .assertSuccessWithOutput(EXPECTED); @@ -82,7 +72,7 @@ public void testMetadataForLib() throws Exception { Path libJar = testForR8(parameters.getBackend()) - .addProgramFiles(libJars.get(targetVersion)) + .addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion)) .addKeepRules("-keep class " + PKG_LIB + ".Sub { <init>(); *** kept(); }") .addKeepRuntimeVisibleAnnotations() .noMinification() @@ -90,13 +80,13 @@ .inspect(this::checkPruned) .writeToZip(); Path output = - kotlinc(parameters.getRuntime().asCf(), KOTLINC, targetVersion) + kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion) .addClasspathFiles(libJar) .addSourceFiles( getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main")) .compile(); testForJvm() - .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(), libJar) + .addRunClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc), libJar) .addProgramFiles(output) .run(parameters.getRuntime(), PKG_APP + ".MainKt") .assertSuccessWithOutput(EXPECTED);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java index dec2ff2..168cbc5 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataStripTest.java
@@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.kotlin.metadata; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed; @@ -10,6 +11,7 @@ import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.R8TestRunResult; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; @@ -27,28 +29,35 @@ public class MetadataStripTest extends KotlinMetadataTestBase { private final TestParameters parameters; + private static final String FOLDER = "lambdas_jstyle_runnable"; - @Parameterized.Parameters(name = "{0} target: {1}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static Collection<Object[]> data() { return buildParameters( - getTestParameters().withAllRuntimesAndApiLevels().build(), KotlinTargetVersion.values()); + getTestParameters().withAllRuntimesAndApiLevels().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } - public MetadataStripTest(TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + public MetadataStripTest( + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } + private static final KotlinCompileMemoizer compiledJars = + getCompileMemoizer(getKotlinFilesInResource(FOLDER), FOLDER) + .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect()); + @Test public void testJstyleRunnable() throws Exception { - final String folder = "lambdas_jstyle_runnable"; final String mainClassName = "lambdas_jstyle_runnable.MainKt"; final String implementer1ClassName = "lambdas_jstyle_runnable.Implementer1Kt"; R8TestRunResult result = testForR8(parameters.getBackend()) - .addProgramFiles(getKotlinJarFile(folder)) - .addProgramFiles(getJavaJarFile(folder)) - .addProgramFiles(ToolHelper.getKotlinReflectJar()) + .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion)) + .addProgramFiles(getJavaJarFile(FOLDER)) + .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinc)) .addKeepMainRule(mainClassName) .addKeepKotlinMetadata() .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java index e262f66..9cd2614 100644 --- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataVersionNumberBumpTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.kotlin.metadata; +import static com.android.tools.r8.ToolHelper.getKotlinC_1_3_72; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.objectweb.asm.Opcodes.ASM7; @@ -43,7 +44,7 @@ } public MetadataVersionNumberBumpTest(TestParameters parameters) { - super(KotlinTargetVersion.JAVA_8); + super(KotlinTargetVersion.JAVA_8, getKotlinC_1_3_72()); this.parameters = parameters; } @@ -86,7 +87,7 @@ private void rewriteMetadataVersion(Consumer<byte[]> rewrittenBytesConsumer, int[] newVersion) throws IOException { ZipUtils.iter( - ToolHelper.getKotlinStdlibJar().toString(), + ToolHelper.getKotlinStdlibJar(kotlinc).toString(), ((entry, input) -> { if (!entry.getName().endsWith(".class")) { return;
diff --git a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java index a2bb5df..53b9d9b 100644 --- a/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/reflection/KotlinReflectTest.java
@@ -4,12 +4,13 @@ package com.android.tools.r8.kotlin.reflection; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assume.assumeTrue; import com.android.tools.r8.DexIndexedConsumer.ArchiveConsumer; -import com.android.tools.r8.TestBase; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; +import com.android.tools.r8.KotlinTestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -17,60 +18,50 @@ import com.android.tools.r8.utils.DescriptorUtils; import com.android.tools.r8.utils.FileUtils; import java.io.File; -import java.nio.file.Path; import java.nio.file.Paths; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) -public class KotlinReflectTest extends TestBase { +public class KotlinReflectTest extends KotlinTestBase { private final TestParameters parameters; private final KotlinTargetVersion targetVersion; private static final String EXPECTED_OUTPUT = "Hello World!"; private static final String PKG = KotlinReflectTest.class.getPackage().getName(); - private static Map<KotlinTargetVersion, Path> compiledJars = new HashMap<>(); + private static final KotlinCompileMemoizer compiledJars = + getCompileMemoizer( + Paths.get( + ToolHelper.TESTS_DIR, + "java", + DescriptorUtils.getBinaryNameFromJavaType(PKG), + "SimpleReflect" + FileUtils.KT_EXTENSION)); - @Parameters(name = "{0}, target: {1}") + @Parameters(name = "{0}, target: {1}, kotlinc: {2}") public static List<Object[]> data() { return buildParameters( - getTestParameters().withAllRuntimesAndApiLevels().build(), KotlinTargetVersion.values()); + getTestParameters().withAllRuntimesAndApiLevels().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } - public KotlinReflectTest(TestParameters parameters, KotlinTargetVersion targetVersion) { + public KotlinReflectTest( + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; this.targetVersion = targetVersion; } - @BeforeClass - public static void createLibJar() throws Exception { - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - compiledJars.put( - targetVersion, - kotlinc(KOTLINC, targetVersion) - .addSourceFiles( - Paths.get( - ToolHelper.TESTS_DIR, - "java", - DescriptorUtils.getBinaryNameFromJavaType(PKG), - "SimpleReflect" + FileUtils.KT_EXTENSION)) - .compile()); - } - } - @Test public void testCf() throws Exception { assumeTrue(parameters.isCfRuntime()); testForJvm() - .addProgramFiles(compiledJars.get(targetVersion)) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(ToolHelper.getKotlinReflectJar()) + .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion)) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinc)) .run(parameters.getRuntime(), PKG + ".SimpleReflectKt") .assertSuccessWithOutputLines(EXPECTED_OUTPUT); } @@ -80,9 +71,9 @@ assumeTrue(parameters.isDexRuntime()); final File output = temp.newFile("output.zip"); testForD8(parameters.getBackend()) - .addProgramFiles(compiledJars.get(targetVersion)) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(ToolHelper.getKotlinReflectJar()) + .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion)) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinc)) .setProgramConsumer(new ArchiveConsumer(output.toPath(), true)) .setMinApi(parameters.getApiLevel()) .addOptionsModification( @@ -98,9 +89,9 @@ public void testR8() throws Exception { final File foo = temp.newFile("foo"); testForR8(parameters.getBackend()) - .addProgramFiles(compiledJars.get(targetVersion)) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(ToolHelper.getKotlinReflectJar()) + .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion)) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(ToolHelper.getKotlinReflectJar(kotlinc)) .setMinApi(parameters.getApiLevel()) .addKeepAllClassesRule() .addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
diff --git a/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java b/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java index 5731086..6b70ca2 100644 --- a/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java +++ b/src/test/java/com/android/tools/r8/kotlin/sealed/SealedClassTest.java
@@ -5,22 +5,21 @@ package com.android.tools.r8.kotlin.sealed; import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static org.hamcrest.CoreMatchers.containsString; import com.android.tools.r8.CompilationFailedException; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.KotlinTestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestRuntime; -import com.android.tools.r8.TestRuntime.CfRuntime; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import java.io.IOException; import java.nio.file.Path; +import java.util.Collection; import java.util.List; import java.util.concurrent.ExecutionException; -import java.util.function.BiFunction; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -37,30 +36,33 @@ @Parameters(name = "{0}") public static List<Object[]> data() { return buildParameters( - getTestParameters().withAllRuntimesAndApiLevels().build(), KotlinTargetVersion.values()); + getTestParameters().withAllRuntimesAndApiLevels().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } - public SealedClassTest(TestParameters parameters, KotlinTargetVersion targetVersion) { - super(targetVersion); + public SealedClassTest( + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static BiFunction<TestRuntime, KotlinTargetVersion, Path> compilationResults = - memoizeBiFunction(SealedClassTest::compileKotlinCode); + private static final KotlinCompileMemoizer compilationResults = + getCompileMemoizer(getKotlinSources()); - private static Path compileKotlinCode(TestRuntime runtime, KotlinTargetVersion targetVersion) - throws IOException { - CfRuntime cfRuntime = runtime.isCf() ? runtime.asCf() : TestRuntime.getCheckedInJdk9(); - return kotlinc(cfRuntime, getStaticTemp(), KOTLINC, targetVersion) - .addSourceFiles(getFilesInTestFolderRelativeToClass(SealedClassTest.class, "kt", ".kt")) - .compile(); + private static Collection<Path> getKotlinSources() { + try { + return getFilesInTestFolderRelativeToClass(SealedClassTest.class, "kt", ".kt"); + } catch (IOException e) { + throw new RuntimeException(e); + } } @Test public void testRuntime() throws ExecutionException, CompilationFailedException, IOException { testForRuntime(parameters) - .addProgramFiles(compilationResults.apply(parameters.getRuntime(), targetVersion)) - .addRunClasspathFiles(buildOnDexRuntime(parameters, ToolHelper.getKotlinStdlibJar())) + .addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion)) + .addRunClasspathFiles(buildOnDexRuntime(parameters, ToolHelper.getKotlinStdlibJar(kotlinc))) .run(parameters.getRuntime(), MAIN) .assertSuccessWithOutputLines(EXPECTED); } @@ -68,8 +70,8 @@ @Test public void testR8() throws ExecutionException, CompilationFailedException, IOException { testForR8(parameters.getBackend()) - .addProgramFiles(compilationResults.apply(parameters.getRuntime(), targetVersion)) - .addProgramFiles(buildOnDexRuntime(parameters, ToolHelper.getKotlinStdlibJar())) + .addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion)) + .addProgramFiles(buildOnDexRuntime(parameters, ToolHelper.getKotlinStdlibJar(kotlinc))) .setMinApi(parameters.getApiLevel()) .allowAccessModification() .allowDiagnosticWarningMessages(parameters.isCfRuntime())
diff --git a/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java b/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java index 9f2d940..ddc2da6 100644 --- a/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java +++ b/src/test/java/com/android/tools/r8/naming/AbstractR8KotlinNamingTestBase.java
@@ -7,6 +7,7 @@ import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed; import static org.hamcrest.MatcherAssert.assertThat; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.kotlin.AbstractR8KotlinTestBase; import com.android.tools.r8.naming.MemberNaming.MethodSignature; @@ -21,9 +22,10 @@ AbstractR8KotlinNamingTestBase( KotlinTargetVersion kotlinTargetVersion, + KotlinCompiler kotlinc, boolean allowAccessModification, boolean minification) { - super(kotlinTargetVersion, allowAccessModification); + super(kotlinTargetVersion, kotlinc, allowAccessModification); this.minification = minification; }
diff --git a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java index 73d9085..837459c 100644 --- a/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java +++ b/src/test/java/com/android/tools/r8/naming/EnumMinificationKotlinTest.java
@@ -3,11 +3,13 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.naming; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.KotlinTestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -31,26 +33,34 @@ private final TestParameters parameters; private final boolean minify; - @Parameterized.Parameters(name = "{0} target: {1} minify: {2}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}, minify: {3}") public static Collection<Object[]> data() { return buildParameters( getTestParameters().withAllRuntimesAndApiLevels().build(), KotlinTargetVersion.values(), + getKotlinCompilers(), BooleanUtils.values()); } public EnumMinificationKotlinTest( - TestParameters parameters, KotlinTargetVersion targetVersion, boolean minify) { - super(targetVersion); + TestParameters parameters, + KotlinTargetVersion targetVersion, + KotlinCompiler kotlinc, + boolean minify) { + super(targetVersion, kotlinc); this.parameters = parameters; this.minify = minify; } + private static final KotlinCompileMemoizer compiledJars = + getCompileMemoizer(getKotlinFilesInResource(FOLDER), FOLDER) + .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect()); + @Test public void b121221542() throws Exception { CodeInspector inspector = testForR8(parameters.getBackend()) - .addProgramFiles(getKotlinJarFile(FOLDER)) + .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion)) .addProgramFiles(getJavaJarFile(FOLDER)) .addKeepMainRule(MAIN_CLASS_NAME) .addKeepClassRulesWithAllowObfuscation(ENUM_CLASS_NAME)
diff --git a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java index 93a0639..70a9cb2 100644 --- a/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java +++ b/src/test/java/com/android/tools/r8/naming/KotlinIntrinsicsIdentifierTest.java
@@ -3,12 +3,14 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.naming; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.SingleTestRunResult; import com.android.tools.r8.TestCompileResult; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -38,17 +40,27 @@ public class KotlinIntrinsicsIdentifierTest extends AbstractR8KotlinNamingTestBase { private static final String FOLDER = "intrinsics_identifiers"; - @Parameters(name = "target: {0}, allowAccessModification: {1}, minification: {2}") + @Parameters(name = "target: {0}, kotlinc: {1}, allowAccessModification: {2}, minification: {3}") public static Collection<Object[]> data() { return buildParameters( - KotlinTargetVersion.values(), BooleanUtils.values(), BooleanUtils.values()); + KotlinTargetVersion.values(), + getKotlinCompilers(), + BooleanUtils.values(), + BooleanUtils.values()); } public KotlinIntrinsicsIdentifierTest( - KotlinTargetVersion targetVersion, boolean allowAccessModification, boolean minification) { - super(targetVersion, allowAccessModification, minification); + KotlinTargetVersion targetVersion, + KotlinCompiler kotlinc, + boolean allowAccessModification, + boolean minification) { + super(targetVersion, kotlinc, allowAccessModification, minification); } + private static final KotlinCompileMemoizer compiledJars = + getCompileMemoizer(getKotlinFilesInResource(FOLDER), FOLDER) + .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect()); + @Test public void test_example1() throws Exception { TestKotlinClass ex1 = new TestKotlinClass("intrinsics_identifiers.Example1Kt"); @@ -71,9 +83,9 @@ public void test_example3() throws Exception { TestKotlinClass ex3 = new TestKotlinClass("intrinsics_identifiers.Example3Kt"); String mainClassName = ex3.getClassName(); - TestCompileResult result = + TestCompileResult<?, ?> result = testForR8(Backend.DEX) - .addProgramFiles(getKotlinJarFile(FOLDER)) + .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion)) .addProgramFiles(getJavaJarFile(FOLDER)) .addKeepMainRule(mainClassName) .allowDiagnosticWarningMessages() @@ -124,9 +136,9 @@ String targetFieldName, String targetMethodName) throws Exception { String mainClassName = testMain.getClassName(); - SingleTestRunResult result = + SingleTestRunResult<?> result = testForR8(Backend.DEX) - .addProgramFiles(getKotlinJarFile(FOLDER)) + .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion)) .addProgramFiles(getJavaJarFile(FOLDER)) .enableProguardTestOptions() .addKeepMainRule(mainClassName)
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java index a64903b..668206c 100644 --- a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java +++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -16,6 +16,7 @@ import com.google.common.base.Equivalence; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -487,6 +488,53 @@ } } + // Equivalence comparing stack traces without taking the line number for a specific stack trace + // line into account. + public static class EquivalenceWithoutSpecificLineNumber extends StackTraceEquivalence { + + private StackTraceLine lineToIgnoreLineNumberFor; + + private EquivalenceWithoutSpecificLineNumber(StackTraceLine lineToIgnoreLineNumberFor) { + this.lineToIgnoreLineNumberFor = lineToIgnoreLineNumberFor; + } + + public static EquivalenceWithoutSpecificLineNumber create( + StackTraceLine lineToIgnoreLineNumberFor) { + return new EquivalenceWithoutSpecificLineNumber(lineToIgnoreLineNumberFor); + } + + public class LineEquivalence extends Equivalence<StackTraceLine> { + + private LineEquivalence() {} + + @Override + protected boolean doEquivalent(StackTraceLine a, StackTraceLine b) { + if (!a.className.equals(b.className) + || !a.methodName.equals(b.methodName) + || !a.fileName.equals(b.fileName)) { + return false; + } + if (a.lineNumber == b.lineNumber) { + return true; + } + return a.className.equals(lineToIgnoreLineNumberFor.className) + && a.methodName.equals(lineToIgnoreLineNumberFor.methodName) + && a.fileName.equals(lineToIgnoreLineNumberFor.fileName); + } + + @Override + protected int doHash(StackTraceLine stackTraceLine) { + return Objects.hash( + stackTraceLine.className, stackTraceLine.methodName, stackTraceLine.fileName); + } + } + + @Override + public Equivalence<StackTraceLine> getLineEquivalence() { + return new LineEquivalence(); + } + } + public static class StackTraceMatcherBase extends TypeSafeMatcher<StackTrace> { private final StackTrace expected; private final StackTraceEquivalence equivalence; @@ -568,4 +616,19 @@ public static Matcher<StackTrace> isSameExceptForFileNameAndLineNumber(StackTrace stackTrace) { return new StackTraceIgnoreFileNameAndLineNumberMatcher(stackTrace); } + + public static class StackTraceIgnoreSpecificLineNumberMatcher extends StackTraceMatcherBase { + private StackTraceIgnoreSpecificLineNumberMatcher( + StackTrace expected, StackTraceLine lineToIgnoreLineNumberFor) { + super( + expected, + EquivalenceWithoutSpecificLineNumber.create(lineToIgnoreLineNumberFor), + "(ignoring file name and line number)"); + } + } + + public static Matcher<StackTrace> isSameExceptForSpecificLineNumber( + StackTrace stackTrace, StackTraceLine lineToIgnoreLineNumberFor) { + return new StackTraceIgnoreSpecificLineNumberMatcher(stackTrace, lineToIgnoreLineNumberFor); + } }
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageProtectedInSeparatePackageTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageProtectedInSeparatePackageTest.java index a455d0e..a5e0ea3 100644 --- a/src/test/java/com/android/tools/r8/repackage/RepackageProtectedInSeparatePackageTest.java +++ b/src/test/java/com/android/tools/r8/repackage/RepackageProtectedInSeparatePackageTest.java
@@ -4,6 +4,7 @@ package com.android.tools.r8.repackage; +import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; import com.android.tools.r8.NeverClassInline; @@ -46,15 +47,14 @@ .compile() .inspect( inspector -> { - // TODO(b/173584786): We should not repackage Sub when generating CF. - assertThat(Sub.class, isRepackaged(inspector)); + if (parameters.isCfRuntime()) { + assertThat(Sub.class, not(isRepackaged(inspector))); + } else { + assertThat(Sub.class, isRepackaged(inspector)); + } }) - .run(parameters.getRuntime(), Main.class); - if (parameters.isCfRuntime()) { - runResult.assertFailureWithErrorThatThrows(VerifyError.class); - } else { - runResult.assertSuccessWithOutputLines(EXPECTED); - } + .run(parameters.getRuntime(), Main.class) + .assertSuccessWithOutputLines(EXPECTED); } public static class Base {
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java index 0beee59..274b990 100644 --- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java +++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionInSameFileRetraceTests.java
@@ -4,8 +4,8 @@ package com.android.tools.r8.retrace; import static com.android.tools.r8.Collectors.toSingle; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.containsLinePositions; import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineFrame; import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineStack; @@ -16,11 +16,9 @@ import com.android.tools.r8.CompilationFailedException; import com.android.tools.r8.CompilationMode; -import com.android.tools.r8.TestBase; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; +import com.android.tools.r8.KotlinTestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.TestRuntime; -import com.android.tools.r8.TestRuntime.CfRuntime; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.naming.retrace.StackTrace; @@ -30,15 +28,16 @@ import com.android.tools.r8.utils.codeinspector.MethodSubject; import java.io.IOException; import java.nio.file.Path; +import java.util.Collection; +import java.util.List; import java.util.concurrent.ExecutionException; -import java.util.function.Function; 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 KotlinInlineFunctionInSameFileRetraceTests extends TestBase { +public class KotlinInlineFunctionInSameFileRetraceTests extends KotlinTestBase { private static final String FILENAME_INLINE = "InlineFunctionsInSameFile.kt"; private static final String MAIN = "retrace.InlineFunctionsInSameFileKt"; @@ -46,30 +45,37 @@ private final TestParameters parameters; @Parameters(name = "{0}") - public static TestParametersCollection data() { - return getTestParameters().withAllRuntimesAndApiLevels().build(); + public static List<Object[]> data() { + // TODO(b/141817471): Extend with compilation modes. + return buildParameters( + getTestParameters().withAllRuntimesAndApiLevels().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } - public KotlinInlineFunctionInSameFileRetraceTests(TestParameters parameters) { + public KotlinInlineFunctionInSameFileRetraceTests( + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static Function<TestRuntime, Path> compilationResults = - memoizeFunction(KotlinInlineFunctionInSameFileRetraceTests::compileKotlinCode); + private static final KotlinCompileMemoizer compilationResults = + getCompileMemoizer(getKotlinSources()); - private static Path compileKotlinCode(TestRuntime runtime) throws IOException { - CfRuntime cfRuntime = runtime.isCf() ? runtime.asCf() : TestRuntime.getCheckedInJdk9(); - return kotlinc(cfRuntime, getStaticTemp(), KOTLINC, KotlinTargetVersion.JAVA_8) - .addSourceFiles( - getFilesInTestFolderRelativeToClass(KotlinInlineFunctionRetraceTest.class, "kt", ".kt")) - .compile(); + private static Collection<Path> getKotlinSources() { + try { + return getFilesInTestFolderRelativeToClass( + KotlinInlineFunctionRetraceTest.class, "kt", ".kt"); + } catch (IOException e) { + throw new RuntimeException(e); + } } @Test public void testRuntime() throws ExecutionException, CompilationFailedException, IOException { testForRuntime(parameters) - .addProgramFiles(compilationResults.apply(parameters.getRuntime())) - .addRunClasspathFiles(buildOnDexRuntime(parameters, ToolHelper.getKotlinStdlibJar())) + .addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion)) + .addRunClasspathFiles(buildOnDexRuntime(parameters, ToolHelper.getKotlinStdlibJar(kotlinc))) .run(parameters.getRuntime(), MAIN) .assertFailureWithErrorThatMatches(containsString("foo")) .assertFailureWithErrorThatMatches( @@ -80,11 +86,11 @@ @Test public void testRetraceKotlinInlineStaticFunction() throws ExecutionException, CompilationFailedException, IOException { - Path kotlinSources = compilationResults.apply(parameters.getRuntime()); + Path kotlinSources = compilationResults.getForConfiguration(kotlinc, targetVersion); CodeInspector kotlinInspector = new CodeInspector(kotlinSources); testForR8(parameters.getBackend()) - .addProgramFiles(compilationResults.apply(parameters.getRuntime())) - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion)) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) .addKeepAttributes("SourceFile", "LineNumberTable") .setMode(CompilationMode.RELEASE) .addKeepMainRule(MAIN)
diff --git a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java index 3b0538d..53d2a7c 100644 --- a/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java +++ b/src/test/java/com/android/tools/r8/retrace/KotlinInlineFunctionRetraceTest.java
@@ -5,8 +5,8 @@ package com.android.tools.r8.retrace; import static com.android.tools.r8.Collectors.toSingle; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; import static com.android.tools.r8.ToolHelper.getFilesInTestFolderRelativeToClass; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.containsLinePositions; import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineFrame; import static com.android.tools.r8.utils.codeinspector.Matchers.isInlineStack; @@ -17,11 +17,9 @@ import com.android.tools.r8.CompilationFailedException; import com.android.tools.r8.CompilationMode; -import com.android.tools.r8.TestBase; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; +import com.android.tools.r8.KotlinTestBase; import com.android.tools.r8.TestParameters; -import com.android.tools.r8.TestParametersCollection; -import com.android.tools.r8.TestRuntime; -import com.android.tools.r8.TestRuntime.CfRuntime; import com.android.tools.r8.ToolHelper; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; import com.android.tools.r8.naming.retrace.StackTrace; @@ -32,15 +30,16 @@ import com.android.tools.r8.utils.codeinspector.MethodSubject; import java.io.IOException; import java.nio.file.Path; +import java.util.Collection; +import java.util.List; import java.util.concurrent.ExecutionException; -import java.util.function.Function; 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 KotlinInlineFunctionRetraceTest extends TestBase { +public class KotlinInlineFunctionRetraceTest extends KotlinTestBase { private final TestParameters parameters; // TODO(b/151132660): Fix filename @@ -48,24 +47,30 @@ private static final String FILENAME_INLINE_INSTANCE = "InlineFunction.kt"; @Parameters(name = "{0}") - public static TestParametersCollection data() { + public static List<Object[]> data() { // TODO(b/141817471): Extend with compilation modes. - return getTestParameters().withAllRuntimesAndApiLevels().build(); + return buildParameters( + getTestParameters().withAllRuntimesAndApiLevels().build(), + KotlinTargetVersion.values(), + getKotlinCompilers()); } - public KotlinInlineFunctionRetraceTest(TestParameters parameters) { + public KotlinInlineFunctionRetraceTest( + TestParameters parameters, KotlinTargetVersion targetVersion, KotlinCompiler kotlinc) { + super(targetVersion, kotlinc); this.parameters = parameters; } - private static Function<TestRuntime, Path> compilationResults = - memoizeFunction(KotlinInlineFunctionRetraceTest::compileKotlinCode); + private static final KotlinCompileMemoizer compilationResults = + getCompileMemoizer(getKotlinSources()); - private static Path compileKotlinCode(TestRuntime runtime) throws IOException { - CfRuntime cfRuntime = runtime.isCf() ? runtime.asCf() : TestRuntime.getCheckedInJdk9(); - return kotlinc(cfRuntime, getStaticTemp(), KOTLINC, KotlinTargetVersion.JAVA_8) - .addSourceFiles( - getFilesInTestFolderRelativeToClass(KotlinInlineFunctionRetraceTest.class, "kt", ".kt")) - .compile(); + private static Collection<Path> getKotlinSources() { + try { + return getFilesInTestFolderRelativeToClass( + KotlinInlineFunctionRetraceTest.class, "kt", ".kt"); + } catch (IOException e) { + throw new RuntimeException(e); + } } private FoundMethodSubject inlineExceptionStatic(CodeInspector kotlinInspector) { @@ -85,8 +90,8 @@ @Test public void testRuntime() throws ExecutionException, CompilationFailedException, IOException { testForRuntime(parameters) - .addProgramFiles(compilationResults.apply(parameters.getRuntime())) - .addRunClasspathFiles(buildOnDexRuntime(parameters, ToolHelper.getKotlinStdlibJar())) + .addProgramFiles(compilationResults.getForConfiguration(kotlinc, targetVersion)) + .addRunClasspathFiles(buildOnDexRuntime(parameters, ToolHelper.getKotlinStdlibJar(kotlinc))) .run(parameters.getRuntime(), "retrace.MainKt") .assertFailureWithErrorThatMatches(containsString("inlineExceptionStatic")) .assertFailureWithErrorThatMatches(containsString("at retrace.MainKt.main(Main.kt:15)")); @@ -97,10 +102,10 @@ throws ExecutionException, CompilationFailedException, IOException { String main = "retrace.MainKt"; String mainFileName = "Main.kt"; - Path kotlinSources = compilationResults.apply(parameters.getRuntime()); + Path kotlinSources = compilationResults.getForConfiguration(kotlinc, targetVersion); CodeInspector kotlinInspector = new CodeInspector(kotlinSources); testForR8(parameters.getBackend()) - .addProgramFiles(kotlinSources, ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(kotlinSources, ToolHelper.getKotlinStdlibJar(kotlinc)) .addKeepAttributes("SourceFile", "LineNumberTable") .allowDiagnosticWarningMessages() .setMode(CompilationMode.RELEASE) @@ -127,10 +132,10 @@ throws ExecutionException, CompilationFailedException, IOException { String main = "retrace.MainInstanceKt"; String mainFileName = "MainInstance.kt"; - Path kotlinSources = compilationResults.apply(parameters.getRuntime()); + Path kotlinSources = compilationResults.getForConfiguration(kotlinc, targetVersion); CodeInspector kotlinInspector = new CodeInspector(kotlinSources); testForR8(parameters.getBackend()) - .addProgramFiles(kotlinSources, ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(kotlinSources, ToolHelper.getKotlinStdlibJar(kotlinc)) .addKeepAttributes("SourceFile", "LineNumberTable") .allowDiagnosticWarningMessages() .setMode(CompilationMode.RELEASE) @@ -160,10 +165,10 @@ throws ExecutionException, CompilationFailedException, IOException { String main = "retrace.MainNestedKt"; String mainFileName = "MainNested.kt"; - Path kotlinSources = compilationResults.apply(parameters.getRuntime()); + Path kotlinSources = compilationResults.getForConfiguration(kotlinc, targetVersion); CodeInspector kotlinInspector = new CodeInspector(kotlinSources); testForR8(parameters.getBackend()) - .addProgramFiles(kotlinSources, ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(kotlinSources, ToolHelper.getKotlinStdlibJar(kotlinc)) .addKeepAttributes("SourceFile", "LineNumberTable") .allowDiagnosticWarningMessages() .setMode(CompilationMode.RELEASE) @@ -192,10 +197,10 @@ throws ExecutionException, CompilationFailedException, IOException { String main = "retrace.MainNestedFirstLineKt"; String mainFileName = "MainNestedFirstLine.kt"; - Path kotlinSources = compilationResults.apply(parameters.getRuntime()); + Path kotlinSources = compilationResults.getForConfiguration(kotlinc, targetVersion); CodeInspector kotlinInspector = new CodeInspector(kotlinSources); testForR8(parameters.getBackend()) - .addProgramFiles(kotlinSources, ToolHelper.getKotlinStdlibJar()) + .addProgramFiles(kotlinSources, ToolHelper.getKotlinStdlibJar(kotlinc)) .addKeepAttributes("SourceFile", "LineNumberTable") .allowDiagnosticWarningMessages() .setMode(CompilationMode.RELEASE)
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java index 0be3ece..64ea164 100644 --- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java +++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationKotlinTest.java
@@ -4,7 +4,7 @@ package com.android.tools.r8.rewrite.assertions; -import static com.android.tools.r8.KotlinCompilerTool.KOTLINC; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.not; @@ -15,6 +15,7 @@ import com.android.tools.r8.AssertionsConfiguration; import com.android.tools.r8.D8TestBuilder; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.KotlinTestBase; import com.android.tools.r8.R8FullTestBuilder; import com.android.tools.r8.TestParameters; @@ -28,14 +29,11 @@ import com.android.tools.r8.utils.codeinspector.InstructionSubject; import com.android.tools.r8.utils.codeinspector.MethodSubject; import com.google.common.collect.ImmutableList; +import java.io.IOException; import java.nio.file.Path; import java.util.Collection; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Objects; import org.junit.Assume; -import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -47,51 +45,31 @@ @RunWith(Parameterized.class) public class AssertionConfigurationKotlinTest extends KotlinTestBase implements Opcodes { - private static class KotlinCompilationKey { - KotlinTargetVersion targetVersion; - boolean useJvmAssertions; - - private KotlinCompilationKey(KotlinTargetVersion targetVersion, boolean useJvmAssertions) { - this.targetVersion = targetVersion; - this.useJvmAssertions = useJvmAssertions; - } - - @Override - public int hashCode() { - return Objects.hash(targetVersion, useJvmAssertions); - } - - @Override - public boolean equals(Object other) { - if (other == null) { - return false; - } - if (getClass() != other.getClass()) { - return false; - } - KotlinCompilationKey kotlinCompilationKey = (KotlinCompilationKey) other; - return targetVersion == kotlinCompilationKey.targetVersion - && useJvmAssertions == kotlinCompilationKey.useJvmAssertions; - } - } - private static final Package pkg = AssertionConfigurationKotlinTest.class.getPackage(); private static final String kotlintestclasesPackage = pkg.getName() + ".kotlintestclasses"; private static final String testClassKt = kotlintestclasesPackage + ".TestClassKt"; private static final String class1 = kotlintestclasesPackage + ".Class1"; private static final String class2 = kotlintestclasesPackage + ".Class2"; - private static final Map<KotlinCompilationKey, Path> kotlinClasses = new HashMap<>(); + private static final KotlinCompileMemoizer kotlinWithJvmAssertions = + getCompileMemoizer(getKotlinFilesForPackage()) + .configure(kotlinCompilerTool -> kotlinCompilerTool.setUseJvmAssertions(true)); + private static final KotlinCompileMemoizer kotlinWithoutJvmAssertions = + getCompileMemoizer(getKotlinFilesForPackage()) + .configure(kotlinCompilerTool -> kotlinCompilerTool.setUseJvmAssertions(false)); + private final TestParameters parameters; private final boolean kotlinStdlibAsLibrary; private final boolean useJvmAssertions; - private final KotlinCompilationKey kotlinCompilationKey; + private final KotlinCompileMemoizer compiledForAssertions; - @Parameterized.Parameters(name = "{0}, {1}, kotlin-stdlib as library: {2}, -Xassertions=jvm: {3}") + @Parameterized.Parameters( + name = "{0}, target: {1}, kotlinc: {2}, kotlin-stdlib as library: {3}, -Xassertions=jvm: {4}") public static Collection<Object[]> data() { return buildParameters( getTestParameters().withAllRuntimesAndApiLevels().build(), KotlinTargetVersion.values(), + getKotlinCompilers(), BooleanUtils.values(), BooleanUtils.values()); } @@ -99,31 +77,27 @@ public AssertionConfigurationKotlinTest( TestParameters parameters, KotlinTargetVersion targetVersion, + KotlinCompiler kotlinc, boolean kotlinStdlibAsClasspath, boolean useJvmAssertions) { - super(targetVersion); + super(targetVersion, kotlinc); this.parameters = parameters; this.kotlinStdlibAsLibrary = kotlinStdlibAsClasspath; this.useJvmAssertions = useJvmAssertions; - this.kotlinCompilationKey = new KotlinCompilationKey(targetVersion, useJvmAssertions); + this.compiledForAssertions = + useJvmAssertions ? kotlinWithJvmAssertions : kotlinWithoutJvmAssertions; } - @BeforeClass - public static void compileKotlin() throws Exception { - for (KotlinTargetVersion targetVersion : KotlinTargetVersion.values()) { - for (boolean useJvmAssertions : BooleanUtils.values()) { - Path ktClasses = - kotlinc(KOTLINC, targetVersion) - .addSourceFiles(getKotlinFilesInTestPackage(pkg)) - .setUseJvmAssertions(useJvmAssertions) - .compile(); - kotlinClasses.put(new KotlinCompilationKey(targetVersion, useJvmAssertions), ktClasses); - } + private static List<Path> getKotlinFilesForPackage() { + try { + return getKotlinFilesInTestPackage(pkg); + } catch (IOException e) { + throw new RuntimeException(e); } } private Path kotlinStdlibLibraryForRuntime() throws Exception { - Path kotlinStdlibCf = ToolHelper.getKotlinStdlibJar(); + Path kotlinStdlibCf = ToolHelper.getKotlinStdlibJar(kotlinc); if (parameters.getRuntime().isCf()) { return kotlinStdlibCf; } @@ -144,8 +118,8 @@ throws Exception { if (kotlinStdlibAsLibrary) { testForD8() - .addClasspathFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(kotlinClasses.get(kotlinCompilationKey)) + .addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion)) .setMinApi(parameters.getApiLevel()) .apply(builderConsumer) .addRunClasspathFiles(kotlinStdlibLibraryForRuntime()) @@ -156,8 +130,8 @@ .assertSuccessWithOutputLines(outputLines); } else { testForD8() - .addProgramFiles(ToolHelper.getKotlinStdlibJar()) - .addProgramFiles(kotlinClasses.get(kotlinCompilationKey)) + .addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc)) + .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion)) .setMinApi(parameters.getApiLevel()) .apply(builderConsumer) .run( @@ -187,11 +161,11 @@ .applyIf( kotlinStdlibAsLibrary, b -> { - b.addClasspathFiles(ToolHelper.getKotlinStdlibJar()); + b.addClasspathFiles(ToolHelper.getKotlinStdlibJar(kotlinc)); b.addRunClasspathFiles(kotlinStdlibLibraryForRuntime()); }, - b -> b.addProgramFiles(ToolHelper.getKotlinStdlibJar())) - .addProgramFiles(kotlinClasses.get(kotlinCompilationKey)) + b -> b.addProgramFiles(ToolHelper.getKotlinStdlibJar(kotlinc))) + .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion)) .addKeepMainRule(testClassKt) .addKeepClassAndMembersRules(class1, class2) .setMinApi(parameters.getApiLevel()) @@ -476,7 +450,7 @@ public void testAssertionsForCfEnableWithStackMap() throws Exception { Assume.assumeTrue(parameters.isCfRuntime()); Assume.assumeTrue(useJvmAssertions); - Assume.assumeTrue(kotlinCompilationKey.targetVersion == KotlinTargetVersion.JAVA_8); + Assume.assumeTrue(targetVersion == KotlinTargetVersion.JAVA_8); // Compile time enabling or disabling assertions means the -ea flag has no effect. runR8Test( builder -> { @@ -554,7 +528,7 @@ Assume.assumeTrue(parameters.isDexRuntime()); testForD8() .addProgramClassFileData(dumpModifiedKotlinAssertions()) - .addProgramFiles(kotlinClasses.get(kotlinCompilationKey)) + .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion)) .setMinApi(parameters.getApiLevel()) .addAssertionsConfiguration(AssertionsConfiguration.Builder::passthroughAllAssertions) .run( @@ -563,7 +537,7 @@ .assertSuccessWithOutputLines(noAllAssertionsExpectedLines()); testForD8() .addProgramClassFileData(dumpModifiedKotlinAssertions()) - .addProgramFiles(kotlinClasses.get(kotlinCompilationKey)) + .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion)) .setMinApi(parameters.getApiLevel()) .addAssertionsConfiguration(AssertionsConfiguration.Builder::enableAllAssertions) .run( @@ -572,7 +546,7 @@ .assertSuccessWithOutputLines(allAssertionsExpectedLines()); testForD8() .addProgramClassFileData(dumpModifiedKotlinAssertions()) - .addProgramFiles(kotlinClasses.get(kotlinCompilationKey)) + .addProgramFiles(compiledForAssertions.getForConfiguration(kotlinc, targetVersion)) .setMinApi(parameters.getApiLevel()) .addAssertionsConfiguration(AssertionsConfiguration.Builder::disableAllAssertions) .run(
diff --git a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java index c975c82..9c358e8 100644 --- a/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java +++ b/src/test/java/com/android/tools/r8/shaking/annotations/ReflectiveAnnotationUseTest.java
@@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.shaking.annotations; +import static com.android.tools.r8.ToolHelper.getKotlinCompilers; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed; import static org.hamcrest.CoreMatchers.containsString; @@ -12,6 +13,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; +import com.android.tools.r8.KotlinCompilerTool.KotlinCompiler; import com.android.tools.r8.KotlinTestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.ToolHelper.KotlinTargetVersion; @@ -60,28 +62,37 @@ private final TestParameters parameters; private final boolean minify; - @Parameterized.Parameters(name = "{0} target: {1} minify: {2}") + @Parameterized.Parameters(name = "{0}, target: {1}, kotlinc: {2}, minify: {3}") public static Collection<Object[]> data() { return buildParameters( getTestParameters().withAllRuntimesAndApiLevels().build(), KotlinTargetVersion.values(), + getKotlinCompilers(), BooleanUtils.values()); } public ReflectiveAnnotationUseTest( - TestParameters parameters, KotlinTargetVersion targetVersion, boolean minify) { - super(targetVersion); + TestParameters parameters, + KotlinTargetVersion targetVersion, + KotlinCompiler kotlinc, + boolean minify) { + super(targetVersion, kotlinc); this.parameters = parameters; this.minify = minify; } + private static final KotlinCompileMemoizer compiledJars = + getCompileMemoizer(getKotlinFilesInResource(FOLDER), FOLDER) + .configure(kotlinCompilerTool -> kotlinCompilerTool.includeRuntime().noReflect()); + @Test public void b120951621_JVMOutput() throws Exception { assumeTrue("Only run JVM reference on CF runtimes", parameters.isCfRuntime()); - AndroidApp app = AndroidApp.builder() - .addProgramFile(getKotlinJarFile(FOLDER)) - .addProgramFile(getJavaJarFile(FOLDER)) - .build(); + AndroidApp app = + AndroidApp.builder() + .addProgramFile(compiledJars.getForConfiguration(kotlinc, targetVersion)) + .addProgramFile(getJavaJarFile(FOLDER)) + .build(); String result = runOnJava(app, MAIN_CLASS_NAME); assertEquals(JAVA_OUTPUT, result); } @@ -90,7 +101,7 @@ public void b120951621_keepAll() throws Exception { CodeInspector inspector = testForR8(parameters.getBackend()) - .addProgramFiles(getKotlinJarFile(FOLDER)) + .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion)) .addProgramFiles(getJavaJarFile(FOLDER)) .addKeepMainRule(MAIN_CLASS_NAME) .addKeepRules(KEEP_ANNOTATIONS) @@ -126,7 +137,7 @@ public void b120951621_partiallyKeep() throws Exception { CodeInspector inspector = testForR8(parameters.getBackend()) - .addProgramFiles(getKotlinJarFile(FOLDER)) + .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion)) .addProgramFiles(getJavaJarFile(FOLDER)) .addKeepMainRule(MAIN_CLASS_NAME) .addKeepRules(KEEP_ANNOTATIONS) @@ -166,7 +177,7 @@ public void b120951621_keepAnnotation() throws Exception { CodeInspector inspector = testForR8(parameters.getBackend()) - .addProgramFiles(getKotlinJarFile(FOLDER)) + .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion)) .addProgramFiles(getJavaJarFile(FOLDER)) .addKeepMainRule(MAIN_CLASS_NAME) .addKeepRules(KEEP_ANNOTATIONS) @@ -202,7 +213,7 @@ public void b120951621_noKeep() throws Exception { CodeInspector inspector = testForR8(parameters.getBackend()) - .addProgramFiles(getKotlinJarFile(FOLDER)) + .addProgramFiles(compiledJars.getForConfiguration(kotlinc, targetVersion)) .addProgramFiles(getJavaJarFile(FOLDER)) .addKeepMainRule(MAIN_CLASS_NAME) .allowDiagnosticWarningMessages()
diff --git a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java index 4f65b48..1c7f6ac 100644 --- a/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java +++ b/src/test/java/com/android/tools/r8/shaking/assumenosideeffects/AssumenosideeffectsPropagationTest.java
@@ -157,7 +157,8 @@ .addKeepMainRule(MAIN) .addKeepRules(config.getKeepRules()) .addOptionsModification( - options -> options.enableHorizontalClassMerging = enableHorizontalClassMerging) + options -> + options.horizontalClassMergerOptions().enableIf(enableHorizontalClassMerging)) .enableInliningAnnotations() .noMinification() .setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java index cbded10..25a8684 100644 --- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java +++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesCommandTest.java
@@ -3,6 +3,8 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.tracereferences; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.fail; @@ -524,12 +526,95 @@ Origin.unknown(), diagnosticsChecker) .build()); - assertEquals(0, diagnosticsChecker.errors.size()); assertEquals(1, diagnosticsChecker.warnings.size()); assertEquals(0, diagnosticsChecker.infos.size()); } + @Test + public void testMissingReference_errorToWarningStdErr() throws Throwable { + Path dir = temp.newFolder().toPath(); + Path targetJar = + zipWithTestClasses(dir.resolve("target.jar"), ImmutableList.of(OtherTarget.class)); + Path sourceJar = zipWithTestClasses(dir.resolve("source.jar"), ImmutableList.of(Source.class)); + PrintStream originalErr = System.err; + PrintStream originalOut = System.out; + ByteArrayOutputStream baosErr = new ByteArrayOutputStream(); + ByteArrayOutputStream baosOut = new ByteArrayOutputStream(); + try { + System.setErr(new PrintStream(baosErr)); + System.setOut(new PrintStream(baosOut)); + TraceReferences.run( + TraceReferencesCommand.parse( + new String[] { + "--check", + "--lib", + ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(), + "--target", + targetJar.toString(), + "--source", + sourceJar.toString(), + "--map-diagnostics:MissingDefinitionsDiagnostic", + "error", + "warning" + }, + Origin.unknown()) + .build()); + } finally { + System.setErr(originalErr); + System.setOut(originalOut); + } + + assertThat( + baosErr.toString(Charsets.UTF_8.name()), + containsString( + "Warning: Tracereferences found 1 classe(s), 1 field(s) and 1 method(s) without" + + " definition")); + assertEquals(0, baosOut.size()); + } + + @Test + public void testMissingReference_errorToInfoStdOut() throws Throwable { + Path dir = temp.newFolder().toPath(); + Path targetJar = + zipWithTestClasses(dir.resolve("target.jar"), ImmutableList.of(OtherTarget.class)); + Path sourceJar = zipWithTestClasses(dir.resolve("source.jar"), ImmutableList.of(Source.class)); + PrintStream originalErr = System.err; + PrintStream originalOut = System.out; + ByteArrayOutputStream baosErr = new ByteArrayOutputStream(); + ByteArrayOutputStream baosOut = new ByteArrayOutputStream(); + try { + System.setErr(new PrintStream(baosErr)); + System.setOut(new PrintStream(baosOut)); + TraceReferences.run( + TraceReferencesCommand.parse( + new String[] { + "--check", + "--lib", + ToolHelper.getAndroidJar(AndroidApiLevel.P).toString(), + "--target", + targetJar.toString(), + "--source", + sourceJar.toString(), + "--map-diagnostics:MissingDefinitionsDiagnostic", + "error", + "info" + }, + Origin.unknown()) + .build()); + } finally { + System.setErr(originalErr); + System.setOut(originalOut); + } + + assertEquals(0, baosErr.size()); + assertThat( + baosOut.toString(Charsets.UTF_8.name()), + containsString( + "Info: Tracereferences found 1 classe(s), 1 field(s) and 1 method(s) without" + + " definition")); + } + private void checkTargetPartlyMissing(DiagnosticsChecker diagnosticsChecker) { Field field; Method method;
diff --git a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java index cad4538..418a282 100644 --- a/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java +++ b/src/test/java/com/android/tools/r8/tracereferences/TraceReferencesDiagnosticTest.java
@@ -172,6 +172,8 @@ DiagnosticsChecker.checkErrorDiagnostics( checker -> { DiagnosticsChecker.checkContains(snippets, checker.errors); + DiagnosticsChecker.checkNotContains( + ImmutableList.of("Classe(s) without definition:"), checker.errors); try { assertEquals(1, checker.errors.size()); assertTrue(checker.errors.get(0) instanceof MissingDefinitionsDiagnostic);
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java index 28f419f..ffe8151 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -101,6 +101,16 @@ } @Override + public boolean isImplementing(Class<?> clazz) { + throw new Unreachable("Cannot determine if an absent class is implementing a given interface"); + } + + @Override + public boolean isImplementing(String javaTypeName) { + throw new Unreachable("Cannot determine if an absent class is implementing a given interface"); + } + + @Override public DexProgramClass getDexProgramClass() { return null; }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java index ff544ab..a2d32eb 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -169,6 +169,10 @@ public abstract boolean isImplementing(ClassSubject subject); + public abstract boolean isImplementing(Class<?> clazz); + + public abstract boolean isImplementing(String javaTypeName); + public String dumpMethods() { StringBuilder dump = new StringBuilder(); forAllMethods(
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java index ed4d158..654f3a1 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
@@ -44,6 +44,37 @@ }; } + public static Matcher<MethodSubject> instantiatesClass(Class<?> clazz) { + return instantiatesClass(clazz.getTypeName()); + } + + public static Matcher<MethodSubject> instantiatesClass(String clazz) { + return new TypeSafeMatcher<MethodSubject>() { + @Override + protected boolean matchesSafely(MethodSubject subject) { + if (!subject.isPresent()) { + return false; + } + if (!subject.getMethod().hasCode()) { + return false; + } + return subject + .streamInstructions() + .anyMatch(instruction -> instruction.isNewInstance(clazz)); + } + + @Override + public void describeTo(Description description) { + description.appendText("instantiates class `" + clazz + "`"); + } + + @Override + public void describeMismatchSafely(final MethodSubject subject, Description description) { + description.appendText("method did not"); + } + }; + } + public static Matcher<MethodSubject> invokesMethod(MethodSubject targetSubject) { if (!targetSubject.isPresent()) { throw new IllegalArgumentException();
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java index 8e5495e..2d41c1f 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/EnumUnboxingInspector.java
@@ -8,21 +8,20 @@ import static org.junit.Assert.assertTrue; import com.android.tools.r8.graph.DexItemFactory; -import com.android.tools.r8.graph.EnumValueInfoMapCollection; +import com.android.tools.r8.ir.optimize.enums.EnumDataMap; public class EnumUnboxingInspector { private final DexItemFactory dexItemFactory; - private final EnumValueInfoMapCollection unboxedEnums; + private final EnumDataMap unboxedEnums; - public EnumUnboxingInspector( - DexItemFactory dexItemFactory, EnumValueInfoMapCollection unboxedEnums) { + public EnumUnboxingInspector(DexItemFactory dexItemFactory, EnumDataMap unboxedEnums) { this.dexItemFactory = dexItemFactory; this.unboxedEnums = unboxedEnums; } public EnumUnboxingInspector assertUnboxed(Class<? extends Enum<?>> clazz) { - assertTrue(unboxedEnums.containsEnum(toDexType(clazz, dexItemFactory))); + assertTrue(unboxedEnums.isUnboxedEnum(toDexType(clazz, dexItemFactory))); return this; }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java index eafb952..a676666 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -311,6 +311,21 @@ } @Override + public boolean isImplementing(Class<?> clazz) { + return isImplementing(clazz.getTypeName()); + } + + @Override + public boolean isImplementing(String javaTypeName) { + for (DexType itf : getDexProgramClass().interfaces) { + if (itf.toSourceString().equals(javaTypeName)) { + return true; + } + } + return false; + } + + @Override public boolean isAnnotation() { return dexClass.accessFlags.isAnnotation(); }
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java index 37dbe19..7cfc3fe 100644 --- a/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java +++ b/src/test/java/com/android/tools/r8/utils/codeinspector/HorizontallyMergedClassesInspector.java
@@ -12,6 +12,8 @@ import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexType; import com.android.tools.r8.horizontalclassmerging.HorizontallyMergedClasses; +import com.android.tools.r8.utils.SetUtils; +import com.google.common.collect.Sets; import java.util.Set; import java.util.function.BiConsumer; @@ -30,10 +32,29 @@ horizontallyMergedClasses.forEachMergeGroup(consumer); } + public Set<Set<DexType>> getMergeGroups() { + Set<Set<DexType>> mergeGroups = Sets.newLinkedHashSet(); + forEachMergeGroup( + (sources, target) -> { + Set<DexType> mergeGroup = SetUtils.newIdentityHashSet(sources); + mergeGroup.add(target); + mergeGroups.add(mergeGroup); + }); + return mergeGroups; + } + + public Set<DexType> getSources() { + return horizontallyMergedClasses.getSources(); + } + public DexType getTarget(DexType clazz) { return horizontallyMergedClasses.getMergeTargetOrDefault(clazz); } + public Set<DexType> getTargets() { + return horizontallyMergedClasses.getTargets(); + } + public HorizontallyMergedClassesInspector assertMergedInto(Class<?> from, Class<?> target) { assertEquals( horizontallyMergedClasses.getMergeTargetOrDefault(toDexType(from, dexItemFactory)),
diff --git a/third_party/kotlin/kotlin-compiler-1.4.20.tar.gz.sha1 b/third_party/kotlin/kotlin-compiler-1.4.20.tar.gz.sha1 new file mode 100644 index 0000000..a2f45a8 --- /dev/null +++ b/third_party/kotlin/kotlin-compiler-1.4.20.tar.gz.sha1
@@ -0,0 +1 @@ +8f0e34c232dc84ab65a589c514b7e33a19f87cc9 \ No newline at end of file
diff --git a/third_party/openjdk/jdk-11/Linux.tar.gz.sha1 b/third_party/openjdk/jdk-11/Linux.tar.gz.sha1 deleted file mode 100644 index 7b3bb4e..0000000 --- a/third_party/openjdk/jdk-11/Linux.tar.gz.sha1 +++ /dev/null
@@ -1 +0,0 @@ -63d8982af46e1300e617ec3b266d44209ad38ffd \ No newline at end of file
diff --git a/third_party/openjdk/jdk-11/Mac.tar.gz.sha1 b/third_party/openjdk/jdk-11/Mac.tar.gz.sha1 deleted file mode 100644 index f95676e..0000000 --- a/third_party/openjdk/jdk-11/Mac.tar.gz.sha1 +++ /dev/null
@@ -1 +0,0 @@ -0e44bc7f7029397681fb6822729785887d565339 \ No newline at end of file
diff --git a/third_party/openjdk/jdk-11/Windows.tar.gz.sha1 b/third_party/openjdk/jdk-11/Windows.tar.gz.sha1 deleted file mode 100644 index ee3ec56..0000000 --- a/third_party/openjdk/jdk-11/Windows.tar.gz.sha1 +++ /dev/null
@@ -1 +0,0 @@ -f332709d19bd53e8ceee3b5bd82b6355d00c9ae9 \ No newline at end of file
diff --git a/third_party/openjdk/jdk-11/linux.tar.gz.sha1 b/third_party/openjdk/jdk-11/linux.tar.gz.sha1 new file mode 100644 index 0000000..4b310c1 --- /dev/null +++ b/third_party/openjdk/jdk-11/linux.tar.gz.sha1
@@ -0,0 +1 @@ +b6f2e70af4adce81ddd3fd72778dcad442b01864 \ No newline at end of file
diff --git a/third_party/openjdk/jdk-11/osx.tar.gz.sha1 b/third_party/openjdk/jdk-11/osx.tar.gz.sha1 new file mode 100644 index 0000000..13c2e79 --- /dev/null +++ b/third_party/openjdk/jdk-11/osx.tar.gz.sha1
@@ -0,0 +1 @@ +4212f0fcd33202e7ae1ea6a4d10cdedeb3c166b4 \ No newline at end of file
diff --git a/third_party/openjdk/jdk-11/windows.tar.gz.sha1 b/third_party/openjdk/jdk-11/windows.tar.gz.sha1 new file mode 100644 index 0000000..acd4eb7 --- /dev/null +++ b/third_party/openjdk/jdk-11/windows.tar.gz.sha1
@@ -0,0 +1 @@ +c190e8fb2715e30dc5a30c765dd11321c852d56f \ No newline at end of file
diff --git a/third_party/openjdk/jdk-15/linux.tar.gz.sha1 b/third_party/openjdk/jdk-15/linux.tar.gz.sha1 new file mode 100644 index 0000000..2ac250c --- /dev/null +++ b/third_party/openjdk/jdk-15/linux.tar.gz.sha1
@@ -0,0 +1 @@ +116e48c5fcecdeccd6e26c07062298cdc3121ed7 \ No newline at end of file
diff --git a/third_party/openjdk/jdk-15/osx.tar.gz.sha1 b/third_party/openjdk/jdk-15/osx.tar.gz.sha1 new file mode 100644 index 0000000..0393063 --- /dev/null +++ b/third_party/openjdk/jdk-15/osx.tar.gz.sha1
@@ -0,0 +1 @@ +ca8935fb824a4d79a987396b3f268f0ecb1bd51b \ No newline at end of file
diff --git a/third_party/openjdk/jdk-15/windows.tar.gz.sha1 b/third_party/openjdk/jdk-15/windows.tar.gz.sha1 new file mode 100644 index 0000000..6f3c8dc --- /dev/null +++ b/third_party/openjdk/jdk-15/windows.tar.gz.sha1
@@ -0,0 +1 @@ +b0c7e22d73b9c5751ef8b62f8a9b10afe8aafa61 \ No newline at end of file
diff --git a/tools/asmifier.py b/tools/asmifier.py index 5290f0a..8aeb9a7 100755 --- a/tools/asmifier.py +++ b/tools/asmifier.py
@@ -10,7 +10,7 @@ import sys import utils -ASM_VERSION = '8.0' +ASM_VERSION = '9.0' ASM_JAR = 'asm-' + ASM_VERSION + '.jar' ASM_UTIL_JAR = 'asm-util-' + ASM_VERSION + '.jar'
diff --git a/tools/git_sync_cl_chain.py b/tools/git_sync_cl_chain.py index f3a0728..857aab8 100755 --- a/tools/git_sync_cl_chain.py +++ b/tools/git_sync_cl_chain.py
@@ -142,7 +142,9 @@ def delete_branches(branches): assert len(branches) > 0 - utils.RunCmd(['git', 'branch', '-D'].extend(branches), quiet=True) + cmd = ['git', 'branch', '-D'] + cmd.extend(branches) + utils.RunCmd(cmd, quiet=True) def get_branch_with_name(name, branches): for branch in branches:
diff --git a/tools/test.py b/tools/test.py index 0c70124..cf14685 100755 --- a/tools/test.py +++ b/tools/test.py
@@ -156,6 +156,11 @@ '--print-obfuscated-stacktraces', '--print_obfuscated_stacktraces', default=False, action='store_true', help='Print the obfuscated stacktraces') + result.add_option( + '--debug-agent', '--debug_agent', + help='Enable Java debug agent and suspend compilation (default disabled)', + default=False, + action='store_true') return result.parse_args() def archive_failures(): @@ -253,6 +258,8 @@ if options.worktree: gradle_args.append('-g=' + os.path.join(utils.REPO_ROOT, ".gradle_user_home")) gradle_args.append('--no-daemon') + if options.debug_agent: + gradle_args.append('--no-daemon') # Build an R8 with dependencies for bootstrapping tests before adding test sources. gradle_args.append('r8WithDeps') @@ -263,6 +270,8 @@ # Add Gradle tasks gradle_args.append('cleanTest') gradle_args.append('test') + if options.debug_agent: + gradle_args.append('--debug-jvm') if options.fail_fast: gradle_args.append('--fail-fast') if options.failed: