Merge commit '60fa9be75820ce92b13f1ceb511f63c18800f134' into dev-release Change-Id: I4ba237431b3f627e7581909c9f0031d2fbe28950
diff --git a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt index e8f153a..5dfc0a5 100644 --- a/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt +++ b/d8_r8/commonBuildSrc/src/main/kotlin/DependenciesPlugin.kt
@@ -55,7 +55,7 @@ JDK_11("jdk-11", 11), JDK_17("jdk-17", 17), JDK_21("jdk-21", 21), - JDK_24("jdk-24", 24); + JDK_25("jdk-25", 25); fun isJdk8(): Boolean { return this == JDK_8
diff --git a/d8_r8/test/build.gradle.kts b/d8_r8/test/build.gradle.kts index 4bda2f7..729e2d7 100644 --- a/d8_r8/test/build.gradle.kts +++ b/d8_r8/test/build.gradle.kts
@@ -64,7 +64,7 @@ dependsOn(gradle.includedBuild("tests_java_11").task(":clean")) dependsOn(gradle.includedBuild("tests_java_17").task(":clean")) dependsOn(gradle.includedBuild("tests_java_21").task(":clean")) - dependsOn(gradle.includedBuild("tests_java_24").task(":clean")) + dependsOn(gradle.includedBuild("tests_java_25").task(":clean")) } val packageTests by registering(Jar::class) {
diff --git a/d8_r8/test/settings.gradle.kts b/d8_r8/test/settings.gradle.kts index 73ef7f8..71cd9e5 100644 --- a/d8_r8/test/settings.gradle.kts +++ b/d8_r8/test/settings.gradle.kts
@@ -36,4 +36,4 @@ includeBuild(root.resolve("test_modules").resolve("tests_java_11")) includeBuild(root.resolve("test_modules").resolve("tests_java_17")) includeBuild(root.resolve("test_modules").resolve("tests_java_21")) -includeBuild(root.resolve("test_modules").resolve("tests_java_24")) +includeBuild(root.resolve("test_modules").resolve("tests_java_25"))
diff --git a/d8_r8/test_modules/tests_java_24/build.gradle.kts b/d8_r8/test_modules/tests_java_25/build.gradle.kts similarity index 85% rename from d8_r8/test_modules/tests_java_24/build.gradle.kts rename to d8_r8/test_modules/tests_java_25/build.gradle.kts index 2492e3b..c0f8a64 100644 --- a/d8_r8/test_modules/tests_java_24/build.gradle.kts +++ b/d8_r8/test_modules/tests_java_25/build.gradle.kts
@@ -14,10 +14,10 @@ java { sourceSets.test.configure { - java.srcDir(root.resolveAll("src", "test", "java24")) + java.srcDir(root.resolveAll("src", "test", "java25")) } - sourceCompatibility = JavaVersion.VERSION_24 - targetCompatibility = JavaVersion.VERSION_24 + sourceCompatibility = JavaVersion.VERSION_25 + targetCompatibility = JavaVersion.VERSION_25 } val testbaseJavaCompileTask = projectTask("testbase", "compileJava") @@ -36,14 +36,14 @@ dependsOn(gradle.includedBuild("shared").task(":downloadDeps")) options.setFork(true) options.forkOptions.memoryMaximumSize = "3g" - options.forkOptions.executable = getCompilerPath(Jdk.JDK_24) + options.forkOptions.executable = getCompilerPath(Jdk.JDK_25) } withType<Test> { notCompatibleWithConfigurationCache( "Failure storing the configuration cache: cannot serialize object of type 'org.gradle.api.internal.project.DefaultProject', a subtype of 'org.gradle.api.Project', as these are not supported with the configuration cache") TestingState.setUpTestingState(this) - javaLauncher = getJavaLauncher(Jdk.JDK_24) + javaLauncher = getJavaLauncher(Jdk.JDK_25) systemProperty("TEST_DATA_LOCATION", layout.buildDirectory.dir("classes/java/test").get().toString()) systemProperty("TESTBASE_DATA_LOCATION",
diff --git a/d8_r8/test_modules/tests_java_24/settings.gradle.kts b/d8_r8/test_modules/tests_java_25/settings.gradle.kts similarity index 95% rename from d8_r8/test_modules/tests_java_24/settings.gradle.kts rename to d8_r8/test_modules/tests_java_25/settings.gradle.kts index c8111c1..0f47405 100644 --- a/d8_r8/test_modules/tests_java_24/settings.gradle.kts +++ b/d8_r8/test_modules/tests_java_25/settings.gradle.kts
@@ -21,7 +21,7 @@ } } -rootProject.name = "tests_java_24" +rootProject.name = "tests_java_25" val root = rootProject.projectDir.parentFile.parentFile includeBuild(root.resolve("shared"))
diff --git a/infra/config/global/generated/cr-buildbucket.cfg b/infra/config/global/generated/cr-buildbucket.cfg index 3a60dff..76170b1 100644 --- a/infra/config/global/generated/cr-buildbucket.cfg +++ b/infra/config/global/generated/cr-buildbucket.cfg
@@ -1551,7 +1551,7 @@ } } builders { - name: "linux-jdk24" + name: "linux-jdk25" swarming_host: "chrome-swarming.appspot.com" swarming_tags: "vpython:native-python-wrapper" dimensions: "cpu:x86-64" @@ -1568,7 +1568,7 @@ ' "builder_group": "internal.client.r8",' ' "recipe": "rex",' ' "test_options": [' - ' "--runtimes=jdk24",' + ' "--runtimes=jdk25",' ' "--command_cache_dir=/tmp/ccache",' ' "--tool=r8",' ' "--print-times",' @@ -1588,7 +1588,7 @@ } } builders { - name: "linux-jdk24_release" + name: "linux-jdk25_release" swarming_host: "chrome-swarming.appspot.com" swarming_tags: "vpython:native-python-wrapper" dimensions: "cpu:x86-64" @@ -1605,7 +1605,7 @@ ' "builder_group": "internal.client.r8",' ' "recipe": "rex",' ' "test_options": [' - ' "--runtimes=jdk24",' + ' "--runtimes=jdk25",' ' "--command_cache_dir=/tmp/ccache",' ' "--tool=r8",' ' "--print-times",'
diff --git a/infra/config/global/generated/luci-milo.cfg b/infra/config/global/generated/luci-milo.cfg index 7ee27d7..e6c8157 100644 --- a/infra/config/global/generated/luci-milo.cfg +++ b/infra/config/global/generated/luci-milo.cfg
@@ -51,9 +51,9 @@ short_name: "jdk21" } builders { - name: "buildbucket/luci.r8.ci/linux-jdk24" + name: "buildbucket/luci.r8.ci/linux-jdk25" category: "R8" - short_name: "jdk24" + short_name: "jdk25" } builders { name: "buildbucket/luci.r8.ci/linux-android-4.0" @@ -206,9 +206,9 @@ short_name: "jdk21" } builders { - name: "buildbucket/luci.r8.ci/linux-jdk24_release" + name: "buildbucket/luci.r8.ci/linux-jdk25_release" category: "Release|R8" - short_name: "jdk24" + short_name: "jdk25" } builders { name: "buildbucket/luci.r8.ci/linux-android-4.0_release"
diff --git a/infra/config/global/generated/luci-notify.cfg b/infra/config/global/generated/luci-notify.cfg index f072a67..a13297c 100644 --- a/infra/config/global/generated/luci-notify.cfg +++ b/infra/config/global/generated/luci-notify.cfg
@@ -480,7 +480,7 @@ } builders { bucket: "ci" - name: "linux-jdk24" + name: "linux-jdk25" repository: "https://r8.googlesource.com/r8" } } @@ -492,7 +492,7 @@ } builders { bucket: "ci" - name: "linux-jdk24_release" + name: "linux-jdk25_release" repository: "https://r8.googlesource.com/r8" } }
diff --git a/infra/config/global/generated/luci-scheduler.cfg b/infra/config/global/generated/luci-scheduler.cfg index 04e6f0f..ef9103f 100644 --- a/infra/config/global/generated/luci-scheduler.cfg +++ b/infra/config/global/generated/luci-scheduler.cfg
@@ -617,7 +617,7 @@ } } job { - id: "linux-jdk24" + id: "linux-jdk25" realm: "ci" acl_sets: "ci" triggering_policy { @@ -627,11 +627,11 @@ buildbucket { server: "cr-buildbucket.appspot.com" bucket: "ci" - builder: "linux-jdk24" + builder: "linux-jdk25" } } job { - id: "linux-jdk24_release" + id: "linux-jdk25_release" realm: "ci" acl_sets: "ci" triggering_policy { @@ -642,7 +642,7 @@ buildbucket { server: "cr-buildbucket.appspot.com" bucket: "ci" - builder: "linux-jdk24_release" + builder: "linux-jdk25_release" } } job { @@ -821,17 +821,6 @@ } } trigger { - id: "branch-gitiles-8.11-forward" - realm: "ci" - acl_sets: "ci" - triggers: "linux-jdk24_release" - gitiles { - repo: "https://r8.googlesource.com/r8" - refs: "regexp:refs/heads/([8]\\.[1-9][1-9]|[9]\\.[0-9]+)" - path_regexps: "src/main/java/com/android/tools/r8/Version.java" - } -} -trigger { id: "branch-gitiles-8.5-forward" realm: "ci" acl_sets: "ci" @@ -854,6 +843,17 @@ } } trigger { + id: "branch-gitiles-9.0-forward" + realm: "ci" + acl_sets: "ci" + triggers: "linux-jdk25_release" + gitiles { + repo: "https://r8.googlesource.com/r8" + refs: "regexp:refs/heads/([9]\\.[0-9]+)" + path_regexps: "src/main/java/com/android/tools/r8/Version.java" + } +} +trigger { id: "branch-gitiles-trigger" realm: "ci" acl_sets: "ci" @@ -909,7 +909,7 @@ triggers: "linux-jdk11" triggers: "linux-jdk17" triggers: "linux-jdk21" - triggers: "linux-jdk24" + triggers: "linux-jdk25" triggers: "linux-jdk8" triggers: "linux-none" triggers: "linux-old"
diff --git a/infra/config/global/main.star b/infra/config/global/main.star index 49592dc..5760b29 100755 --- a/infra/config/global/main.star +++ b/infra/config/global/main.star
@@ -115,10 +115,10 @@ ) luci.gitiles_poller( - name = "branch-gitiles-8.11-forward", + name = "branch-gitiles-9.0-forward", bucket = "ci", repo = "https://r8.googlesource.com/r8", - refs = ["refs/heads/([8]\\.[1-9][1-9]|[9]\\.[0-9]+)"], + refs = ["refs/heads/([9]\\.[0-9]+)"], path_regexps = ["src/main/java/com/android/tools/r8/Version.java"], ) @@ -386,9 +386,9 @@ ["--runtimes=jdk21", "--command_cache_dir=/tmp/ccache"], ) r8_tester_with_default( - "linux-jdk24", - ["--runtimes=jdk24", "--command_cache_dir=/tmp/ccache"], - release_trigger = ["branch-gitiles-8.11-forward"], + "linux-jdk25", + ["--runtimes=jdk25", "--command_cache_dir=/tmp/ccache"], + release_trigger = ["branch-gitiles-9.0-forward"], ) r8_tester_with_default(
diff --git a/scripts/add-openjdk.sh b/scripts/add-openjdk.sh index 0eecb48..9068086 100755 --- a/scripts/add-openjdk.sh +++ b/scripts/add-openjdk.sh
@@ -18,7 +18,7 @@ # Now run script with fingers crossed! -JDK_VERSION="24" +JDK_VERSION="25" JDK_VERSION_FULL=${JDK_VERSION} # For ea versions the full version name has a postfix. # JDK_VERSION_FULL="${JDK_VERSION}-ea+33" @@ -62,4 +62,4 @@ git add *.sha1 -echo "Update additional files, see https://r8-review.googlesource.com/c/r8/+/61909" +echo "Update additional files, see https://r8-review.googlesource.com/c/r8/+/111040"
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java index 43953a0..91230f3 100644 --- a/src/main/java/com/android/tools/r8/R8Command.java +++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -1082,10 +1082,6 @@ } - void setEnableExperimentalCheckEnumUnboxed() { - parserOptionsBuilder.setEnableExperimentalCheckEnumUnboxed(true); - } - // Internal for-testing method to allow proguard options only available for testing. void setEnableTestProguardOptions() { parserOptionsBuilder.setEnableTestingOptions(true);
diff --git a/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ClassFlagEvent.java b/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ClassFlagEvent.java new file mode 100644 index 0000000..b6bd9cf --- /dev/null +++ b/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ClassFlagEvent.java
@@ -0,0 +1,50 @@ +// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file +// for details. 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.assistant.postprocessing.model; + +import com.android.tools.r8.assistant.runtime.ReflectiveEventType; +import com.android.tools.r8.assistant.runtime.ReflectiveOperationReceiver.ClassFlag; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.shaking.KeepInfoCollectionExported; + +public class ClassFlagEvent extends ReflectiveEvent { + private final DexType holder; + private final ClassFlag classFlag; + + protected ClassFlagEvent( + ReflectiveEventType eventType, String[] stack, String[] args, DexItemFactory factory) { + super(eventType, stack); + assert args.length == 2; + holder = toType(args[0], factory); + classFlag = ClassFlag.valueOf(args[1]); + } + + public ClassFlag getClassFlag() { + return classFlag; + } + + @Override + public boolean isClassFlagEvent() { + return true; + } + + @Override + public ClassFlagEvent asClassFlagEvent() { + return this; + } + + @Override + public String getContentsString() { + return holder.toString() + "#" + classFlag; + } + + @Override + public boolean isKeptBy(KeepInfoCollectionExported keepInfoCollectionExported) { + // TODO(b/428836085): Check inner properties of the keep rules, holder, type and name may have + // to be preserved. + return keepInfoCollectionExported.getKeepClassInfo(holder.asTypeReference()) != null; + } +}
diff --git a/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ClassNewInstance.java b/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ClassNewInstance.java new file mode 100644 index 0000000..dfdab8d --- /dev/null +++ b/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ClassNewInstance.java
@@ -0,0 +1,47 @@ +// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file +// for details. 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.assistant.postprocessing.model; + +import com.android.tools.r8.assistant.runtime.ReflectiveEventType; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.shaking.KeepInfoCollectionExported; + +public class ClassNewInstance extends ReflectiveEvent { + + private final DexType type; + + protected ClassNewInstance( + ReflectiveEventType eventType, String[] stack, String[] args, DexItemFactory factory) { + super(eventType, stack); + type = toType(args[0], factory); + } + + public DexType getType() { + return type; + } + + @Override + public boolean isClassNewInstance() { + return true; + } + + @Override + public ClassNewInstance asClassNewInstance() { + return this; + } + + @Override + public String getContentsString() { + return type.toSourceString(); + } + + @Override + public boolean isKeptBy(KeepInfoCollectionExported keepInfoCollectionExported) { + // TODO(b/428836085): Check inner properties of the keep rules, holder, type and name may have + // to be preserved. + return keepInfoCollectionExported.getKeepClassInfo(type.asTypeReference()) != null; + } +}
diff --git a/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ProxyNewProxyInstance.java b/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ProxyNewProxyInstance.java new file mode 100644 index 0000000..248a0fc --- /dev/null +++ b/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ProxyNewProxyInstance.java
@@ -0,0 +1,57 @@ +// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file +// for details. 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.assistant.postprocessing.model; + +import com.android.tools.r8.assistant.runtime.ReflectiveEventType; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.graph.DexType; +import com.android.tools.r8.shaking.KeepInfoCollectionExported; +import com.android.tools.r8.utils.ArrayUtils; +import java.util.Arrays; + +public class ProxyNewProxyInstance extends ReflectiveEvent { + + private final DexType classLoader; + private final DexType[] interfaces; + private final String invocationHandler; + + public ProxyNewProxyInstance( + ReflectiveEventType eventType, String[] stack, String[] args, DexItemFactory factory) { + super(eventType, stack); + assert args.length > 2; + classLoader = toTypeOrTripleStar(args[0], factory); + invocationHandler = args[1]; + interfaces = new DexType[args.length - 2]; + for (int i = 2; i < args.length; i++) { + interfaces[i - 2] = toType(args[i], factory); + } + } + + public DexType[] getInterfaces() { + return interfaces; + } + + @Override + public boolean isProxyNewProxyInstance() { + return true; + } + + @Override + public ProxyNewProxyInstance asProxyNewProxyInstance() { + return this; + } + + @Override + public String getContentsString() { + return invocationHandler + ", " + classLoader + ", " + Arrays.toString(interfaces); + } + + @Override + public boolean isKeptBy(KeepInfoCollectionExported keepInfoCollectionExported) { + return ArrayUtils.none( + interfaces, + itf -> keepInfoCollectionExported.getKeepClassInfo(itf.asTypeReference()) == null); + } +}
diff --git a/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ReflectiveEvent.java b/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ReflectiveEvent.java index 26090d0..cfa48ac 100644 --- a/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ReflectiveEvent.java +++ b/src/main/java/com/android/tools/r8/assistant/postprocessing/model/ReflectiveEvent.java
@@ -60,6 +60,14 @@ return null; } + public boolean isProxyNewProxyInstance() { + return false; + } + + public ProxyNewProxyInstance asProxyNewProxyInstance() { + return null; + } + public boolean isClassGetMember() { return false; } @@ -68,6 +76,22 @@ return null; } + public boolean isClassNewInstance() { + return false; + } + + public ClassNewInstance asClassNewInstance() { + return null; + } + + public boolean isClassFlagEvent() { + return false; + } + + public ClassFlagEvent asClassFlagEvent() { + return null; + } + public boolean isClassGetMembers() { return false; } @@ -89,7 +113,7 @@ ReflectiveEventType eventType, String[] stack, String[] args, DexItemFactory factory) { switch (eventType) { case CLASS_NEW_INSTANCE: - break; + return new ClassNewInstance(eventType, stack, args, factory); case CLASS_GET_DECLARED_METHOD: case CLASS_GET_DECLARED_FIELD: case CLASS_GET_DECLARED_CONSTRUCTOR: @@ -123,13 +147,13 @@ case CLASS_CAST: break; case CLASS_FLAG: - break; + return new ClassFlagEvent(eventType, stack, args, factory); case ATOMIC_FIELD_UPDATER_NEW_UPDATER: return new AtomicFieldUpdaterNewUpdater(eventType, stack, args, factory); case SERVICE_LOADER_LOAD: return new ServiceLoaderLoad(eventType, stack, args, factory); case PROXY_NEW_PROXY_INSTANCE: - break; + return new ProxyNewProxyInstance(eventType, stack, args, factory); } return new ReflectiveEvent(eventType, stack) { @Override
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 5b17a26..da79f26 100644 --- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java +++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -866,6 +866,8 @@ createStaticallyKnownType("Ldalvik/annotation/NestHost;"); public final DexType annotationNestMembers = createStaticallyKnownType("Ldalvik/annotation/NestMembers;"); + public final DexType annotationNeverCompile = + createStaticallyKnownType("Ldalvik/annotation/optimization/NeverCompile;"); public final DexType annotationPermittedSubclasses = createStaticallyKnownType("Ldalvik/annotation/PermittedSubclasses;"); public final DexType annotationRecord = createStaticallyKnownType("Ldalvik/annotation/Record;");
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java index 1711903..9459e5a 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -755,7 +755,8 @@ && (options.canUseJavaLangVarHandleStoreStoreFence(appView) || inlinerOptions.skipStoreStoreFenceInConstructorInlining) && methodProcessor.hasWaves() - && target.isProgramField()) { + && target.isProgramField() + && appView.getKeepInfo(target.asProgramField()).isOptimizationAllowed(options)) { actionBuilder.setShouldEnsureStoreStoreFence(target.asProgramField()); } else { whyAreYouNotInliningReporter.reportUnsafeConstructorInliningDueToFinalFieldAssignment(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java index 482d5a1..0ae1c43 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/ServiceLoaderRewriter.java
@@ -37,6 +37,8 @@ import com.android.tools.r8.ir.conversion.passes.result.CodeRewriterResult; import com.android.tools.r8.ir.desugar.ServiceLoaderSourceCode; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.shaking.KeepInfo; +import com.android.tools.r8.shaking.MinimumKeepInfoCollection; import com.android.tools.r8.utils.ConsumerUtils; import com.android.tools.r8.utils.DominatorChecker; import com.android.tools.r8.utils.ListUtils; @@ -47,6 +49,7 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import java.util.function.Predicate; /** * ServiceLoaderRewriter will attempt to rewrite calls on the form of: ServiceLoader.load(X.class, @@ -107,9 +110,15 @@ } private boolean shouldReportWhyAreYouNotInliningServiceLoaderLoad() { - AppInfoWithLiveness appInfo = appView().appInfo(); - return appInfo.isWhyAreYouNotInliningMethod(serviceLoaderMethods.load) - || appInfo.isWhyAreYouNotInliningMethod(serviceLoaderMethods.loadWithClassLoader); + MinimumKeepInfoCollection keepInfo = + appView + .rootSet() + .getDependentMinimumKeepInfo() + .getUnconditionalMinimumKeepInfoOrDefault(MinimumKeepInfoCollection.empty()); + Predicate<KeepInfo.Joiner<?, ?, ?>> test = + joiner -> joiner.asMethodJoiner().isWhyAreYouNotInliningEnabled(); + return keepInfo.hasMinimumKeepInfoThatMatches(serviceLoaderMethods.load, test) + || keepInfo.hasMinimumKeepInfoThatMatches(serviceLoaderMethods.loadWithClassLoader, test); } @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java index e70e49e..eeb46c7 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/WhyAreYouNotInliningReporter.java
@@ -14,6 +14,7 @@ import com.android.tools.r8.ir.optimize.Inliner; import com.android.tools.r8.ir.optimize.Inliner.Reason; import com.android.tools.r8.shaking.AppInfoWithLiveness; +import com.android.tools.r8.utils.InternalOptions; import com.android.tools.r8.utils.collections.ProgramMethodSet; import java.util.Set; @@ -21,7 +22,7 @@ public static WhyAreYouNotInliningReporter createFor( ProgramMethod callee, AppView<AppInfoWithLiveness> appView, ProgramMethod context) { - if (appView.appInfo().isWhyAreYouNotInliningMethod(callee.getReference())) { + if (appView.getKeepInfo(callee).isWhyAreYouNotInliningEnabled()) { return new WhyAreYouNotInliningReporterImpl(appView, callee, context); } return NopWhyAreYouNotInliningReporter.getInstance(); @@ -32,7 +33,9 @@ InvokeMethod invoke, AppView<AppInfoWithLiveness> appView, ProgramMethod context) { - if (appView.appInfo().hasNoWhyAreYouNotInliningMethods()) { + InternalOptions options = appView.options(); + if (!options.hasProguardConfiguration() + || !options.getProguardConfiguration().hasWhyAreYouNotInliningRule()) { return; }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutline.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutline.java index 0af7d88..f868bc8 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutline.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutline.java
@@ -3,11 +3,16 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.ir.optimize.outliner.exceptions; +import static com.android.tools.r8.graph.DexAnnotation.VISIBILITY_BUILD; import static com.android.tools.r8.graph.DexClassAndMethod.asProgramMethodOrNull; import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext; import com.android.tools.r8.errors.Unreachable; import com.android.tools.r8.graph.AppView; +import com.android.tools.r8.graph.DexAnnotation; +import com.android.tools.r8.graph.DexAnnotationElement; +import com.android.tools.r8.graph.DexAnnotationSet; +import com.android.tools.r8.graph.DexEncodedAnnotation; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProto; @@ -253,6 +258,7 @@ builder -> builder .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic()) + .setAnnotations(createAnnotations(appView)) // TODO(b/434769547): The API level of the code may be higher than the min-api. // This currently doesn't matter since outlining runs after API outlining. .setApiLevelForCode( @@ -260,4 +266,16 @@ .setCode(methodSig -> lirCode) .setProto(getOptimizedProto(appView.dexItemFactory()))); } + + private DexAnnotationSet createAnnotations(AppView<?> appView) { + if (appView.options().getThrowBlockOutlinerOptions().neverCompile) { + DexItemFactory factory = appView.dexItemFactory(); + DexEncodedAnnotation encodedAnnotation = + new DexEncodedAnnotation( + factory.annotationNeverCompile, DexAnnotationElement.EMPTY_ARRAY); + DexAnnotation annotation = new DexAnnotation(VISIBILITY_BUILD, encodedAnnotation); + return DexAnnotationSet.create(new DexAnnotation[] {annotation}); + } + return DexAnnotationSet.empty(); + } }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerOptions.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerOptions.java index 55036d0..7f6ea7d 100644 --- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerOptions.java +++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerOptions.java
@@ -27,6 +27,10 @@ SystemPropertyUtils.parseSystemPropertyOrDefault( "com.android.tools.r8.throwblockoutliner.users", Integer.MAX_VALUE); + public boolean neverCompile = + SystemPropertyUtils.parseSystemPropertyOrDefault( + "com.android.tools.r8.throwblockoutliner.nevercompile", false); + public Consumer<Collection<ThrowBlockOutline>> outlineConsumerForTesting = null; public Predicate<ThrowBlockOutline> outlineStrategyForTesting = null;
diff --git a/src/main/java/com/android/tools/r8/processkeeprules/FilteredKeepRulesBuilder.java b/src/main/java/com/android/tools/r8/processkeeprules/FilteredKeepRulesBuilder.java index fa4d3ab..9ba68e1 100644 --- a/src/main/java/com/android/tools/r8/processkeeprules/FilteredKeepRulesBuilder.java +++ b/src/main/java/com/android/tools/r8/processkeeprules/FilteredKeepRulesBuilder.java
@@ -4,7 +4,6 @@ package com.android.tools.r8.processkeeprules; import com.android.tools.r8.StringConsumer; -import com.android.tools.r8.origin.Origin; import com.android.tools.r8.position.Position; import com.android.tools.r8.position.TextPosition; import com.android.tools.r8.shaking.FilteredClassPath; @@ -12,7 +11,11 @@ import com.android.tools.r8.shaking.ProguardConfigurationParser.ProguardConfigurationSourceParser; import com.android.tools.r8.shaking.ProguardConfigurationParserConsumer; import com.android.tools.r8.shaking.ProguardConfigurationRule; +import com.android.tools.r8.shaking.ProguardKeepAttributes; import com.android.tools.r8.shaking.ProguardPathList; +import com.android.tools.r8.shaking.WhyAreYouKeepingRule; +import com.android.tools.r8.shaking.WhyAreYouNotInliningRule; +import com.android.tools.r8.shaking.WhyAreYouNotObfuscatingRule; import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode; import com.android.tools.r8.utils.Reporter; import com.android.tools.r8.utils.StringUtils; @@ -64,6 +67,19 @@ } private void write(String string) { + internalWrite(string, false); + } + + private void writeComment(ProguardConfigurationSourceParser parser, TextPosition positionStart) { + writeComment(parser.getContentSince(positionStart)); + } + + private void writeComment(String string) { + ensureComment(); + internalWrite(string, true); + } + + private void internalWrite(String string, boolean beginCommentOnNewline) { int lastNewlineIndex = string.lastIndexOf('\n'); if (lastNewlineIndex < 0) { appendToCurrentLine(string); @@ -73,6 +89,9 @@ consumer.accept(untilNewlineInclusive, reporter); // Due to the newline character we are no longer inside a comment. exitComment(); + if (beginCommentOnNewline) { + ensureComment(); + } // Emit everything after the newline character. String fromNewlineExclusive = string.substring(lastNewlineIndex + 1); appendToCurrentLine(fromNewlineExclusive); @@ -80,20 +99,156 @@ } @Override - public void addKeepAttributePatterns( - List<String> attributesPatterns, - Origin origin, + public void addAdaptClassStringsPattern( + ProguardClassNameList pattern, ProguardConfigurationSourceParser parser, - Position position, TextPosition positionStart) { ensureNewlineAfterComment(); write(parser, positionStart); } @Override - public void addRule(ProguardConfigurationRule rule) { + public void addAdaptResourceFileContents( + ProguardPathList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { ensureNewlineAfterComment(); - write(rule.getSource()); + write(parser, positionStart); + } + + @Override + public void addAdaptResourceFilenames( + ProguardPathList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { + ensureNewlineAfterComment(); + write(parser, positionStart); + } + + @Override + public void addBaseDirectory( + Path baseDirectory, ProguardConfigurationSourceParser parser, TextPosition positionStart) { + ensureNewlineAfterComment(); + write(parser, positionStart); + } + + @Override + public void addDontNotePattern( + ProguardClassNameList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { + ensureNewlineAfterComment(); + write(parser, positionStart); + } + + @Override + public void addDontWarnPattern( + ProguardClassNameList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { + ensureNewlineAfterComment(); + write(parser, positionStart); + } + + @Override + public void addIgnoredOption( + String option, ProguardConfigurationSourceParser parser, TextPosition positionStart) { + ensureNewlineAfterComment(); + write(parser, positionStart); + } + + @Override + public void addInclude( + Path includePath, ProguardConfigurationSourceParser parser, TextPosition positionStart) { + ensureNewlineAfterComment(); + write(parser, positionStart); + } + + @Override + public void addLeadingBOM() { + appendToCurrentLine(Character.toString(StringUtils.BOM)); + } + + @Override + public void addInjars( + List<FilteredClassPath> filteredClassPaths, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + writeComment(parser, positionStart); + } + + @Override + public void addKeepAttributePatterns( + List<String> attributesPatterns, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + ProguardKeepAttributes keepAttributes = ProguardKeepAttributes.fromPatterns(attributesPatterns); + if (keepAttributes.lineNumberTable + || keepAttributes.runtimeInvisibleAnnotations + || keepAttributes.runtimeInvisibleParameterAnnotations + || keepAttributes.runtimeInvisibleTypeAnnotations + || keepAttributes.sourceFile) { + // Comment out the -keepattributes rule. + writeComment(parser, positionStart); + // Unset the undesired attributes and expand the rule. + keepAttributes.lineNumberTable = false; + keepAttributes.runtimeInvisibleAnnotations = false; + keepAttributes.runtimeInvisibleParameterAnnotations = false; + keepAttributes.runtimeInvisibleTypeAnnotations = false; + keepAttributes.sourceFile = false; + ensureNewlineAfterComment(); + write(keepAttributes.toString()); + } else { + ensureNewlineAfterComment(); + write(parser, positionStart); + } + } + + @Override + public void addKeepKotlinMetadata( + ProguardConfigurationSourceParser parser, Position position, TextPosition positionStart) { + ensureNewlineAfterComment(); + write(parser, positionStart); + } + + @Override + public void addKeepPackageNamesPattern( + ProguardClassNameList proguardClassNameList, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { + ensureNewlineAfterComment(); + write(parser, positionStart); + } + + @Override + public void addLibraryJars( + List<FilteredClassPath> filteredClassPaths, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + writeComment(parser, positionStart); + } + + @Override + public void addParsedConfiguration(ProguardConfigurationSourceParser parser) { + assert parser.getPendingIncludes().isEmpty(); + } + + @Override + public void addRule( + ProguardConfigurationRule rule, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { + if (rule instanceof WhyAreYouKeepingRule + || rule instanceof WhyAreYouNotInliningRule + || rule instanceof WhyAreYouNotObfuscatingRule) { + writeComment(parser, positionStart); + } else { + ensureNewlineAfterComment(); + write(parser, positionStart); + } } @Override @@ -102,126 +257,164 @@ } @Override - public void disableObfuscation(Origin origin, Position position) { - ensureComment(); - write("-dontobfuscate"); + public void disableObfuscation(ProguardConfigurationSourceParser parser, Position position) { + writeComment("-dontobfuscate"); } @Override - public void disableOptimization(Origin origin, Position position) { - ensureComment(); - write("-dontoptimize"); + public void disableOptimization(ProguardConfigurationSourceParser parser, Position position) { + writeComment("-dontoptimize"); } @Override - public void disableShrinking(Origin origin, Position position) { - ensureComment(); - write("-dontshrink"); + public void disableShrinking(ProguardConfigurationSourceParser parser, Position position) { + writeComment("-dontshrink"); } @Override - public void setRenameSourceFileAttribute(String s, Origin origin, Position position) {} + public void enableAllowAccessModification( + ProguardConfigurationSourceParser parser, Position position, TextPosition positionStart) { + writeComment(parser, positionStart); + } @Override - public void addKeepPackageNamesPattern(ProguardClassNameList proguardClassNameList) {} + public void enableFlattenPackageHierarchy( + String packagePrefix, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + writeComment(parser, positionStart); + } @Override - public void setKeepParameterNames(boolean b, Origin origin, Position position) {} + public void enableKeepDirectories( + ProguardPathList keepDirectoryPatterns, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { + ensureNewlineAfterComment(); + write(parser, positionStart); + } @Override - public void enableKeepDirectories() {} + public void enablePrintConfiguration( + Path printConfigurationFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + writeComment(parser, positionStart); + } @Override - public void addKeepDirectories(ProguardPathList proguardPathList) {} + public void enablePrintMapping( + Path printMappingFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + writeComment(parser, positionStart); + } @Override - public void addParsedConfiguration(String s) {} + public void enablePrintSeeds( + Path printSeedsFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + writeComment(parser, positionStart); + } @Override - public void setPrintUsage(boolean b) {} + public void enablePrintUsage( + Path printUsageFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + writeComment(parser, positionStart); + } @Override - public void setPrintUsageFile(Path path) {} + public void enableProtoShrinking( + ProguardConfigurationSourceParser parser, TextPosition positionStart) { + writeComment(parser, positionStart); + } @Override - public void enableProtoShrinking() {} + public void enableRepackageClasses( + String packagePrefix, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + writeComment(parser, positionStart); + } @Override - public void setIgnoreWarnings(boolean b) {} + public void joinMaxRemovedAndroidLogLevel( + int maxRemovedAndroidLogLevel, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { + writeComment(parser, positionStart); + } @Override - public void addDontWarnPattern(ProguardClassNameList pattern) {} + public void setApplyMappingFile( + Path applyMappingFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + writeComment(parser, positionStart); + } @Override - public void addDontNotePattern(ProguardClassNameList pattern) {} + public void setIgnoreWarnings( + ProguardConfigurationSourceParser parser, TextPosition positionStart) { + ensureNewlineAfterComment(); + write(parser, positionStart); + } @Override - public void enableAllowAccessModification(Origin origin, Position position) {} + public void setClassObfuscationDictionary( + Path path, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + writeComment(parser, positionStart); + } @Override - public void enablePrintConfiguration(Origin origin, Position position) {} + public void setKeepParameterNames( + ProguardConfigurationSourceParser parser, Position position, TextPosition positionStart) { + ensureNewlineAfterComment(); + write(parser, positionStart); + } @Override - public void setPrintConfigurationFile(Path path) {} + public void setObfuscationDictionary( + Path path, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + writeComment(parser, positionStart); + } @Override - public void enablePrintMapping(Origin origin, Position position) {} + public void setPackageObfuscationDictionary( + Path path, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + writeComment(parser, positionStart); + } @Override - public void setPrintMappingFile(Path path) {} - - @Override - public void setApplyMappingFile(Path path, Origin origin, Position position) {} - - @Override - public void addInjars( - List<FilteredClassPath> filteredClassPaths, Origin origin, Position position) {} - - @Override - public void addLibraryJars( - List<FilteredClassPath> filteredClassPaths, Origin origin, Position position) {} - - @Override - public void setPrintSeeds(boolean b, Origin origin, Position position) {} - - @Override - public void setSeedFile(Path path) {} - - @Override - public void setObfuscationDictionary(Path path, Origin origin, Position position) {} - - @Override - public void setClassObfuscationDictionary(Path path, Origin origin, Position position) {} - - @Override - public void setPackageObfuscationDictionary(Path path, Origin origin, Position position) {} - - @Override - public void addAdaptClassStringsPattern(ProguardClassNameList pattern) {} - - @Override - public void addAdaptResourceFileContents(ProguardPathList pattern) {} - - @Override - public void addAdaptResourceFilenames(ProguardPathList pattern) {} - - @Override - public void joinMaxRemovedAndroidLogLevel(int maxRemovedAndroidLogLevel) {} + public void setRenameSourceFileAttribute( + String s, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + writeComment(parser, positionStart); + } @Override public PackageObfuscationMode getPackageObfuscationMode() { return null; } - - @Override - public void setPackagePrefix(String s) {} - - @Override - public void setFlattenPackagePrefix(String s) {} - - @Override - public void enableRepackageClasses(Origin origin, Position position) {} - - @Override - public void enableFlattenPackageHierarchy(Origin origin, Position position) {} }
diff --git a/src/main/java/com/android/tools/r8/processkeeprules/KeepAttributeLibraryConsumerRuleDiagnostic.java b/src/main/java/com/android/tools/r8/processkeeprules/KeepAttributeLibraryConsumerRuleDiagnostic.java index c1f3d5a..3886317 100644 --- a/src/main/java/com/android/tools/r8/processkeeprules/KeepAttributeLibraryConsumerRuleDiagnostic.java +++ b/src/main/java/com/android/tools/r8/processkeeprules/KeepAttributeLibraryConsumerRuleDiagnostic.java
@@ -12,8 +12,7 @@ private final String attribute; - public KeepAttributeLibraryConsumerRuleDiagnostic( - Origin origin, Position position, String attribute) { + KeepAttributeLibraryConsumerRuleDiagnostic(Origin origin, Position position, String attribute) { super(origin, position); this.attribute = attribute; }
diff --git a/src/main/java/com/android/tools/r8/processkeeprules/GlobalLibraryConsumerRuleDiagnostic.java b/src/main/java/com/android/tools/r8/processkeeprules/LibraryConsumerRuleDiagnostic.java similarity index 78% rename from src/main/java/com/android/tools/r8/processkeeprules/GlobalLibraryConsumerRuleDiagnostic.java rename to src/main/java/com/android/tools/r8/processkeeprules/LibraryConsumerRuleDiagnostic.java index bc1a21d..a3bf0c9 100644 --- a/src/main/java/com/android/tools/r8/processkeeprules/GlobalLibraryConsumerRuleDiagnostic.java +++ b/src/main/java/com/android/tools/r8/processkeeprules/LibraryConsumerRuleDiagnostic.java
@@ -8,10 +8,11 @@ import com.android.tools.r8.position.Position; @KeepForApi -public class GlobalLibraryConsumerRuleDiagnostic extends ConsumerRuleDiagnostic { +public class LibraryConsumerRuleDiagnostic extends ConsumerRuleDiagnostic { + private final String rule; - public GlobalLibraryConsumerRuleDiagnostic(Origin origin, Position position, String rule) { + LibraryConsumerRuleDiagnostic(Origin origin, Position position, String rule) { super(origin, position); this.rule = rule; }
diff --git a/src/main/java/com/android/tools/r8/processkeeprules/ValidateLibraryConsumerRulesKeepRuleProcessor.java b/src/main/java/com/android/tools/r8/processkeeprules/ValidateLibraryConsumerRulesKeepRuleProcessor.java index c2ced3e..e0bbeab 100644 --- a/src/main/java/com/android/tools/r8/processkeeprules/ValidateLibraryConsumerRulesKeepRuleProcessor.java +++ b/src/main/java/com/android/tools/r8/processkeeprules/ValidateLibraryConsumerRulesKeepRuleProcessor.java
@@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.processkeeprules; -import com.android.tools.r8.origin.Origin; import com.android.tools.r8.position.Position; import com.android.tools.r8.position.TextPosition; import com.android.tools.r8.shaking.FilteredClassPath; @@ -13,12 +12,16 @@ import com.android.tools.r8.shaking.ProguardConfigurationRule; import com.android.tools.r8.shaking.ProguardKeepAttributes; import com.android.tools.r8.shaking.ProguardPathList; +import com.android.tools.r8.shaking.WhyAreYouKeepingRule; +import com.android.tools.r8.shaking.WhyAreYouNotInliningRule; +import com.android.tools.r8.shaking.WhyAreYouNotObfuscatingRule; import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode; import com.android.tools.r8.utils.Reporter; import java.nio.file.Path; import java.util.List; class ValidateLibraryConsumerRulesKeepRuleProcessor implements ProguardConfigurationParserConsumer { + private final Reporter reporter; public ValidateLibraryConsumerRulesKeepRuleProcessor(Reporter reporter) { @@ -26,189 +29,286 @@ this.reporter = reporter; } - private void handleGlobalRule(Origin origin, Position position, String rule) { - reporter.error(new GlobalLibraryConsumerRuleDiagnostic(origin, position, rule)); + private void handleRule( + ProguardConfigurationSourceParser parser, Position position, String rule) { + reporter.error(new LibraryConsumerRuleDiagnostic(parser.getOrigin(), position, rule)); } - private void handleKeepAttribute(Origin origin, Position position, String attribute) { - reporter.error(new KeepAttributeLibraryConsumerRuleDiagnostic(origin, position, attribute)); + private void handleKeepAttribute( + ProguardConfigurationSourceParser parser, Position position, String attribute) { + reporter.error( + new KeepAttributeLibraryConsumerRuleDiagnostic(parser.getOrigin(), position, attribute)); } @Override - public void disableOptimization(Origin origin, Position position) { - handleGlobalRule(origin, position, "-dontoptimize"); + public void disableOptimization(ProguardConfigurationSourceParser parser, Position position) { + handleRule(parser, position, "-dontoptimize"); } @Override - public void disableObfuscation(Origin origin, Position position) { - handleGlobalRule(origin, position, "-dontobfuscate"); + public void disableObfuscation(ProguardConfigurationSourceParser parser, Position position) { + handleRule(parser, position, "-dontobfuscate"); } @Override - public void disableShrinking(Origin origin, Position position) { - handleGlobalRule(origin, position, "-dontshrink"); + public void disableShrinking(ProguardConfigurationSourceParser parser, Position position) { + handleRule(parser, position, "-dontshrink"); } @Override - public void enableRepackageClasses(Origin origin, Position position) { - handleGlobalRule(origin, position, "-repackageclasses"); + public void enableRepackageClasses( + String packagePrefix, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + handleRule(parser, position, "-repackageclasses"); } @Override - public void enableFlattenPackageHierarchy(Origin origin, Position position) { - handleGlobalRule(origin, position, "-flattenpackagehierarchy"); + public void enableFlattenPackageHierarchy( + String packagePrefix, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + handleRule(parser, position, "-flattenpackagehierarchy"); } @Override - public void enableAllowAccessModification(Origin origin, Position position) { - handleGlobalRule(origin, position, "-allowaccessmodification"); + public void enableAllowAccessModification( + ProguardConfigurationSourceParser parser, Position position, TextPosition positionStart) { + handleRule(parser, position, "-allowaccessmodification"); } @Override - public void setRenameSourceFileAttribute(String s, Origin origin, Position position) { - handleGlobalRule(origin, position, "-renamesourcefileattribute"); + public void setRenameSourceFileAttribute( + String s, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + handleRule(parser, position, "-renamesourcefileattribute"); } @Override - public void addParsedConfiguration(String s) {} + public void addBaseDirectory( + Path baseDirectory, ProguardConfigurationSourceParser parser, TextPosition positionStart) {} @Override - public void addRule(ProguardConfigurationRule rule) {} + public void addIgnoredOption( + String option, ProguardConfigurationSourceParser parser, TextPosition positionStart) {} + + @Override + public void addInclude( + Path includePath, ProguardConfigurationSourceParser parser, TextPosition positionStart) { + // TODO(b/270289387): Report error. + } + + @Override + public void addLeadingBOM() {} + + @Override + public void addParsedConfiguration(ProguardConfigurationSourceParser parser) {} + + @Override + public void addRule( + ProguardConfigurationRule rule, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { + if (rule instanceof WhyAreYouKeepingRule) { + handleRule(parser, positionStart, "-whyareyoukeeping"); + } else if (rule instanceof WhyAreYouNotInliningRule) { + handleRule(parser, positionStart, "-whyareyounotinlining"); + } else if (rule instanceof WhyAreYouNotObfuscatingRule) { + handleRule(parser, positionStart, "-whyareyounotobfuscating"); + } + } @Override public void addKeepAttributePatterns( List<String> attributesPatterns, - Origin origin, ProguardConfigurationSourceParser parser, Position position, TextPosition positionStart) { // TODO(b/270289387): Add support for more attributes. ProguardKeepAttributes keepAttributes = ProguardKeepAttributes.fromPatterns(attributesPatterns); if (keepAttributes.lineNumberTable) { - handleKeepAttribute(origin, position, ProguardKeepAttributes.LINE_NUMBER_TABLE); + handleKeepAttribute(parser, position, ProguardKeepAttributes.LINE_NUMBER_TABLE); } if (keepAttributes.runtimeInvisibleAnnotations) { - handleKeepAttribute(origin, position, ProguardKeepAttributes.RUNTIME_INVISIBLE_ANNOTATIONS); + handleKeepAttribute(parser, position, ProguardKeepAttributes.RUNTIME_INVISIBLE_ANNOTATIONS); } if (keepAttributes.runtimeInvisibleTypeAnnotations) { handleKeepAttribute( - origin, position, ProguardKeepAttributes.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); + parser, position, ProguardKeepAttributes.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); } if (keepAttributes.runtimeInvisibleParameterAnnotations) { handleKeepAttribute( - origin, position, ProguardKeepAttributes.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS); + parser, position, ProguardKeepAttributes.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS); } if (keepAttributes.sourceFile) { - handleKeepAttribute(origin, position, ProguardKeepAttributes.SOURCE_FILE); + handleKeepAttribute(parser, position, ProguardKeepAttributes.SOURCE_FILE); } } @Override - public void addKeepPackageNamesPattern(ProguardClassNameList proguardClassNameList) {} + public void addKeepKotlinMetadata( + ProguardConfigurationSourceParser parser, Position position, TextPosition positionStart) {} @Override - public void setKeepParameterNames(boolean b, Origin origin, Position position) {} + public void addKeepPackageNamesPattern( + ProguardClassNameList proguardClassNameList, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) {} @Override - public void enableKeepDirectories() {} + public void setKeepParameterNames( + ProguardConfigurationSourceParser parser, Position position, TextPosition positionStart) {} @Override - public void addKeepDirectories(ProguardPathList proguardPathList) {} + public void enableKeepDirectories( + ProguardPathList keepDirectoryPatterns, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) {} @Override - public void setPrintUsage(boolean b) {} - - @Override - public void setPrintUsageFile(Path path) {} - - @Override - public void enableProtoShrinking() {} - - @Override - public void setIgnoreWarnings(boolean b) {} - - @Override - public void addDontWarnPattern(ProguardClassNameList pattern) {} - - @Override - public void addDontNotePattern(ProguardClassNameList pattern) {} - - @Override - public void enablePrintConfiguration(Origin origin, Position position) { - handleGlobalRule(origin, position, "-printconfiguration"); + public void enableProtoShrinking( + ProguardConfigurationSourceParser parser, TextPosition positionStart) { + handleRule(parser, positionStart, "-shrinkunusedprotofields"); } @Override - public void setPrintConfigurationFile(Path path) {} + public void setIgnoreWarnings( + ProguardConfigurationSourceParser parser, TextPosition positionStart) {} @Override - public void enablePrintMapping(Origin origin, Position position) { - handleGlobalRule(origin, position, "-printmapping"); + public void addDontWarnPattern( + ProguardClassNameList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) {} + + @Override + public void addDontNotePattern( + ProguardClassNameList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) {} + + @Override + public void enablePrintConfiguration( + Path printConfigurationFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + handleRule(parser, position, "-printconfiguration"); } @Override - public void setPrintMappingFile(Path path) {} + public void enablePrintMapping( + Path printMappingFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + handleRule(parser, position, "-printmapping"); + } @Override - public void setApplyMappingFile(Path path, Origin origin, Position position) { - handleGlobalRule(origin, position, "-applymapping"); + public void enablePrintSeeds( + Path printSeedsFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + handleRule(parser, position, "-printseeds"); + } + + @Override + public void enablePrintUsage( + Path printUsageFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + handleRule(parser, position, "-printusage"); + } + + @Override + public void setApplyMappingFile( + Path applyMappingFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + handleRule(parser, position, "-applymapping"); } @Override public void addInjars( - List<FilteredClassPath> filteredClassPaths, Origin origin, Position position) { - handleGlobalRule(origin, position, "-injars"); + List<FilteredClassPath> filteredClassPaths, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + handleRule(parser, position, "-injars"); } @Override public void addLibraryJars( - List<FilteredClassPath> filteredClassPaths, Origin origin, Position position) { - handleGlobalRule(origin, position, "-libraryjars"); + List<FilteredClassPath> filteredClassPaths, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + handleRule(parser, position, "-libraryjars"); } @Override - public void setPrintSeeds(boolean b, Origin origin, Position position) { - handleGlobalRule(origin, position, "-printseeds"); + public void setObfuscationDictionary( + Path path, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + handleRule(parser, position, "-obfuscationdictionary"); } @Override - public void setSeedFile(Path path) {} - - @Override - public void setObfuscationDictionary(Path path, Origin origin, Position position) { - handleGlobalRule(origin, position, "-obfuscationdictionary"); + public void setClassObfuscationDictionary( + Path path, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + handleRule(parser, position, "-classobfuscationdictionary"); } @Override - public void setClassObfuscationDictionary(Path path, Origin origin, Position position) { - handleGlobalRule(origin, position, "-classobfuscationdictionary"); + public void setPackageObfuscationDictionary( + Path path, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + handleRule(parser, position, "-packageobfuscationdictionary"); } @Override - public void setPackageObfuscationDictionary(Path path, Origin origin, Position position) { - handleGlobalRule(origin, position, "-packageobfuscationdictionary"); + public void addAdaptClassStringsPattern( + ProguardClassNameList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) {} + + @Override + public void addAdaptResourceFileContents( + ProguardPathList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) {} + + @Override + public void addAdaptResourceFilenames( + ProguardPathList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) {} + + @Override + public void joinMaxRemovedAndroidLogLevel( + int maxRemovedAndroidLogLevel, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { + handleRule(parser, positionStart, "-maximumremovedandroidloglevel <int>"); } @Override - public void addAdaptClassStringsPattern(ProguardClassNameList pattern) {} - - @Override - public void addAdaptResourceFileContents(ProguardPathList pattern) {} - - @Override - public void addAdaptResourceFilenames(ProguardPathList pattern) {} - - @Override - public void joinMaxRemovedAndroidLogLevel(int maxRemovedAndroidLogLevel) {} - - @Override public PackageObfuscationMode getPackageObfuscationMode() { return null; } - - @Override - public void setPackagePrefix(String s) {} - - @Override - public void setFlattenPackagePrefix(String s) {} }
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 25f2a95..cb5e4b8 100644 --- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java +++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -147,8 +147,6 @@ public final Map<DexReference, ProguardMemberRule> mayHaveSideEffects; /** All methods that should be inlined if possible due to a configuration directive. */ private final Set<DexMethod> alwaysInline; - /** Items for which to print inlining decisions for (testing only). */ - private final Set<DexMethod> whyAreYouNotInlining; /** All methods that must be reprocessed (testing only). */ private final Set<DexMethod> reprocess; /** All types that should be inlined if possible due to a configuration directive. */ @@ -210,7 +208,6 @@ KeepInfoCollection keepInfo, Map<DexReference, ProguardMemberRule> mayHaveSideEffects, Set<DexMethod> alwaysInline, - Set<DexMethod> whyAreYouNotInlining, Set<DexMethod> reprocess, PredicateSet<DexType> alwaysClassInline, IdentifierNameStringCollection identifierNameStrings, @@ -236,7 +233,6 @@ this.mayHaveSideEffects = mayHaveSideEffects; this.callSites = callSites; this.alwaysInline = alwaysInline; - this.whyAreYouNotInlining = whyAreYouNotInlining; this.reprocess = reprocess; this.alwaysClassInline = alwaysClassInline; this.identifierNameStrings = identifierNameStrings; @@ -270,7 +266,6 @@ previous.keepInfo, previous.mayHaveSideEffects, previous.alwaysInline, - previous.whyAreYouNotInlining, previous.reprocess, previous.alwaysClassInline, previous.identifierNameStrings, @@ -305,7 +300,6 @@ extendPinnedItems(previous, prunedItems.getAdditionalPinnedItems()), previous.mayHaveSideEffects, pruneMethods(previous.alwaysInline, prunedItems, tasks), - pruneMethods(previous.whyAreYouNotInlining, prunedItems, tasks), pruneMethods(previous.reprocess, prunedItems, tasks), previous.alwaysClassInline, previous.identifierNameStrings.prune(prunedItems, tasks), @@ -431,7 +425,6 @@ keepInfo, mayHaveSideEffects, alwaysInline, - whyAreYouNotInlining, reprocess, alwaysClassInline, identifierNameStrings, @@ -502,7 +495,6 @@ this.mayHaveSideEffects = previous.mayHaveSideEffects; this.callSites = previous.callSites; this.alwaysInline = previous.alwaysInline; - this.whyAreYouNotInlining = previous.whyAreYouNotInlining; this.reprocess = previous.reprocess; this.alwaysClassInline = previous.alwaysClassInline; this.identifierNameStrings = previous.identifierNameStrings; @@ -638,14 +630,6 @@ return alwaysInline.contains(method); } - public boolean isWhyAreYouNotInliningMethod(DexMethod method) { - return whyAreYouNotInlining.contains(method); - } - - public boolean hasNoWhyAreYouNotInliningMethods() { - return whyAreYouNotInlining.isEmpty(); - } - public Set<DexMethod> getReprocessMethods() { return reprocess; } @@ -999,7 +983,6 @@ // Take any rule in case of collisions. lens.rewriteReferenceKeys(mayHaveSideEffects, (reference, rules) -> ListUtils.first(rules)), lens.rewriteReferences(alwaysInline), - lens.rewriteReferences(whyAreYouNotInlining), lens.rewriteReferences(reprocess), alwaysClassInline.rewriteItems(lens::lookupType), identifierNameStrings.rewrittenWithLens(lens),
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 e5cb200..4482352 100644 --- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java +++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -506,7 +506,7 @@ this.options = options; this.taskCollection = new EnqueuerTaskCollection(this, options.getThreadingModule(), executorService); - this.keepInfo = new MutableKeepInfoCollection(options); + this.keepInfo = new MutableKeepInfoCollection(appView, this); this.reflectiveIdentification = new EnqueuerReflectiveIdentification(appView, this); this.useRegistryFactory = createUseRegistryFactory(); this.worklist = EnqueuerWorklist.createWorklist(this, options.getThreadingModule()); @@ -4746,7 +4746,6 @@ getKeepInfo(), rootSet.mayHaveSideEffects, amendWithCompanionMethods(rootSet.alwaysInline), - amendWithCompanionMethods(rootSet.whyAreYouNotInlining), amendWithCompanionMethods(rootSet.reprocess), rootSet.alwaysClassInline, new IdentifierNameStringCollection(
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java index 3eca5de..4c4e47b 100644 --- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java +++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -31,6 +31,7 @@ private final boolean allowShrinking; private final boolean allowSignatureRemoval; private final boolean checkDiscarded; + private final boolean whyAreYouNotObfuscating; private final KeepAnnotationCollectionInfo annotationsInfo; private final KeepAnnotationCollectionInfo typeAnnotationsInfo; @@ -42,6 +43,7 @@ boolean allowShrinking, boolean allowSignatureRemoval, boolean checkDiscarded, + boolean whyAreYouNotObfuscating, KeepAnnotationCollectionInfo annotationsInfo, KeepAnnotationCollectionInfo typeAnnotationsInfo) { this.allowAccessModification = allowAccessModification; @@ -51,6 +53,7 @@ this.allowShrinking = allowShrinking; this.allowSignatureRemoval = allowSignatureRemoval; this.checkDiscarded = checkDiscarded; + this.whyAreYouNotObfuscating = whyAreYouNotObfuscating; this.annotationsInfo = annotationsInfo; this.typeAnnotationsInfo = typeAnnotationsInfo; } @@ -64,6 +67,7 @@ builder.isShrinkingAllowed(), builder.isSignatureRemovalAllowed(), builder.isCheckDiscardedEnabled(), + builder.isWhyAreYouNotObfuscatingEnabled(), builder.getAnnotationsInfo().build(), builder.getTypeAnnotationsInfo().build()); } @@ -150,6 +154,14 @@ return checkDiscarded; } + public boolean isWhyAreYouNotObfuscatingEnabled() { + return internalIsWhyAreYouNotObfuscatingEnabled(); + } + + boolean internalIsWhyAreYouNotObfuscatingEnabled() { + return whyAreYouNotObfuscating; + } + /** * True if an item must be present in the output. * @@ -299,6 +311,7 @@ && (allowShrinking || !other.internalIsShrinkingAllowed()) && (allowSignatureRemoval || !other.internalIsSignatureRemovalAllowed()) && (!checkDiscarded || other.internalIsCheckDiscardedEnabled()) + && (!whyAreYouNotObfuscating || other.internalIsWhyAreYouNotObfuscatingEnabled()) && annotationsInfo.isLessThanOrEqualTo(other.internalAnnotationsInfo()) && typeAnnotationsInfo.isLessThanOrEqualTo(other.internalTypeAnnotationsInfo()); } @@ -312,7 +325,8 @@ && allowOptimization == other.internalIsOptimizationAllowed() && allowShrinking == other.internalIsShrinkingAllowed() && allowSignatureRemoval == other.internalIsSignatureRemovalAllowed() - && checkDiscarded == other.internalIsCheckDiscardedEnabled(); + && checkDiscarded == other.internalIsCheckDiscardedEnabled() + && whyAreYouNotObfuscating == other.internalIsWhyAreYouNotObfuscatingEnabled(); } public boolean equalsWithAnnotations(K other) { @@ -341,6 +355,7 @@ hash += bit(allowShrinking, index++); hash += bit(allowSignatureRemoval, index++); hash += bit(checkDiscarded, index++); + hash += bit(whyAreYouNotObfuscating, index++); hash += bit(annotationsInfo.isTop(), index++); hash += bit(typeAnnotationsInfo.isTop(), index); return hash; @@ -369,6 +384,9 @@ case "checkDiscarded": builder.setCheckDiscarded(Boolean.parseBoolean(value)); return true; + case "whyAreYouNotObfuscating": + builder.setWhyAreYouNotObfuscating(Boolean.parseBoolean(value)); + return true; case "annotationsInfo": builder.setAnnotationInfo(KeepAnnotationCollectionInfoExported.parse(value)); return true; @@ -389,6 +407,7 @@ lines.add("allowShrinking: " + allowShrinking); lines.add("allowSignatureRemoval: " + allowSignatureRemoval); lines.add("checkDiscarded: " + checkDiscarded); + lines.add("whyAreYouNotObfuscating: " + whyAreYouNotObfuscating); lines.add("annotationsInfo: " + annotationsInfo); lines.add("typeAnnotationsInfo: " + typeAnnotationsInfo); return lines; @@ -423,6 +442,7 @@ private boolean allowShrinking; private boolean allowSignatureRemoval; private boolean checkDiscarded; + private boolean whyAreYouNotObfuscating; private KeepAnnotationCollectionInfo.Builder annotationsInfo; private KeepAnnotationCollectionInfo.Builder typeAnnotationsInfo; @@ -439,6 +459,7 @@ allowShrinking = original.internalIsShrinkingAllowed(); allowSignatureRemoval = original.internalIsSignatureRemovalAllowed(); checkDiscarded = original.internalIsCheckDiscardedEnabled(); + whyAreYouNotObfuscating = original.internalIsWhyAreYouNotObfuscatingEnabled(); annotationsInfo = original.internalAnnotationsInfo().toBuilder(); typeAnnotationsInfo = original.internalTypeAnnotationsInfo().toBuilder(); } @@ -453,6 +474,7 @@ setAllowShrinking(false); setAllowSignatureRemoval(false); setCheckDiscarded(false); + setWhyAreYouNotObfuscating(false); return self(); } @@ -466,6 +488,7 @@ setAllowShrinking(true); setAllowSignatureRemoval(true); setCheckDiscarded(false); + setWhyAreYouNotObfuscating(false); return self(); } @@ -493,6 +516,7 @@ && isShrinkingAllowed() == other.internalIsShrinkingAllowed() && isSignatureRemovalAllowed() == other.internalIsSignatureRemovalAllowed() && isCheckDiscardedEnabled() == other.internalIsCheckDiscardedEnabled() + && isWhyAreYouNotObfuscatingEnabled() == other.internalIsWhyAreYouNotObfuscatingEnabled() && annotationsInfo.isEqualTo(other.internalAnnotationsInfo()) && typeAnnotationsInfo.isEqualTo(other.internalTypeAnnotationsInfo()); } @@ -506,6 +530,15 @@ return self(); } + public boolean isWhyAreYouNotObfuscatingEnabled() { + return whyAreYouNotObfuscating; + } + + public B setWhyAreYouNotObfuscating(boolean whyAreYouNotObfuscating) { + this.whyAreYouNotObfuscating = whyAreYouNotObfuscating; + return self(); + } + public boolean isMinificationAllowed() { return allowMinification; } @@ -654,6 +687,10 @@ return builder.isCheckDiscardedEnabled(); } + public boolean isWhyAreYouNotObfuscatingEnabled() { + return builder.isWhyAreYouNotObfuscatingEnabled(); + } + public boolean isMinificationAllowed() { return builder.isMinificationAllowed(); } @@ -735,6 +772,11 @@ return self(); } + public J setWhyAreYouNotObfuscating() { + builder.setWhyAreYouNotObfuscating(true); + return self(); + } + public J merge(J joiner) { Builder<B, K> otherBuilder = joiner.builder; applyIf(!otherBuilder.isAccessModificationAllowed(), Joiner::disallowAccessModification); @@ -746,6 +788,7 @@ applyIf(!otherBuilder.isShrinkingAllowed(), Joiner::disallowShrinking); applyIf(!otherBuilder.isSignatureRemovalAllowed(), Joiner::disallowSignatureRemoval); applyIf(otherBuilder.isCheckDiscardedEnabled(), Joiner::setCheckDiscarded); + applyIf(otherBuilder.isWhyAreYouNotObfuscatingEnabled(), Joiner::setWhyAreYouNotObfuscating); builder.getAnnotationsInfo().destructiveJoin(otherBuilder.getAnnotationsInfo()); builder.getTypeAnnotationsInfo().destructiveJoin(otherBuilder.getTypeAnnotationsInfo()); reasons.addAll(joiner.reasons);
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 11ebdfb..574dc74 100644 --- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java +++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -284,7 +284,9 @@ // Mutation interface for building up the keep info. public static class MutableKeepInfoCollection extends KeepInfoCollection { + private final AppView<?> appView; private final DexItemFactory factory; + private final Enqueuer.Mode mode; // These are typed at signatures but the interface should make sure never to allow access // directly with a signature. See the comment in KeepInfoCollection. @@ -304,9 +306,10 @@ private final KeepInfoCanonicalizer canonicalizer; - MutableKeepInfoCollection(InternalOptions options) { + MutableKeepInfoCollection(AppView<?> appView, Enqueuer enqueuer) { this( - options, + appView, + enqueuer.getMode(), new IdentityHashMap<>(), new IdentityHashMap<>(), new IdentityHashMap<>(), @@ -314,13 +317,14 @@ new IdentityHashMap<>(), new IdentityHashMap<>(), MaterializedRules.empty(), - options.testing.enableKeepInfoCanonicalizer + appView.testing().enableKeepInfoCanonicalizer ? KeepInfoCanonicalizer.newCanonicalizer() : KeepInfoCanonicalizer.newNopCanonicalizer()); } private MutableKeepInfoCollection( - InternalOptions options, + AppView<?> appView, + Enqueuer.Mode mode, Map<DexType, KeepClassInfo> keepClassInfo, Map<DexMethod, KeepMethodInfo> keepMethodInfo, Map<DexField, KeepFieldInfo> keepFieldInfo, @@ -329,7 +333,9 @@ Map<DexMethod, KeepMethodInfo.Joiner> methodRuleInstances, MaterializedRules materializedRules, KeepInfoCanonicalizer keepInfoCanonicalizer) { - this.factory = options.dexItemFactory(); + this.appView = appView; + this.factory = appView.dexItemFactory(); + this.mode = mode; this.keepClassInfo = keepClassInfo; this.keepMethodInfo = keepMethodInfo; this.keepFieldInfo = keepFieldInfo; @@ -388,7 +394,8 @@ Map<DexField, KeepFieldInfo> newFieldInfo = rewriteFieldInfo(lens, options, timing); MutableKeepInfoCollection result = new MutableKeepInfoCollection( - options, + appView, + mode, newClassInfo, newMethodInfo, newFieldInfo, @@ -610,10 +617,12 @@ KeepClassInfo.Joiner joiner = info.joiner(); fn.accept(joiner); KeepClassInfo joined = joiner.join(); - if (!info.equals(joined)) { - keepClassInfo.put(clazz.type, canonicalizer.canonicalizeKeepClassInfo(joined)); - maybeDisallowKotlinMetadataRemoval(clazz, info, joined, joiner); + if (info.equals(joined)) { + return; } + keepClassInfo.put(clazz.type, canonicalizer.canonicalizeKeepClassInfo(joined)); + maybeDisallowKotlinMetadataRemoval(clazz, info, joined, joiner); + reportWhyAreYouNotObfuscating(clazz, info, joined, joiner); } private void maybeDisallowKotlinMetadataRemoval( @@ -639,6 +648,54 @@ allowKotlinMetadataRemoval = false; } + private void reportWhyAreYouNotObfuscating( + ProgramDefinition definition, + KeepInfo<?, ?> previousKeepInfo, + KeepInfo<?, ?> joinedKeepInfo, + KeepInfo.Joiner<?, ?, ?> joiner) { + InternalOptions options = appView.options(); + if (!mode.isFinalTreeShaking() + || options.getProguardConfiguration() == null + || !options.getProguardConfiguration().hasWhyAreYouNotObfuscatingRule() + || !previousKeepInfo.internalIsMinificationAllowed() + || joiner.isMinificationAllowed()) { + return; + } + MinimumKeepInfoCollection minimumKeepInfoCollection = + appView + .rootSet() + .getDependentMinimumKeepInfo() + .getUnconditionalMinimumKeepInfoOrDefault(MinimumKeepInfoCollection.empty()); + if (!minimumKeepInfoCollection.hasMinimumKeepInfoThatMatches( + definition.getReference(), KeepInfo.Joiner::isWhyAreYouNotObfuscatingEnabled)) { + return; + } + assert !joinedKeepInfo.internalIsMinificationAllowed(); + boolean foundRule = false; + for (ProguardKeepRuleBase rule : joiner.getRules()) { + if (!rule.getModifiers().allowsObfuscation) { + appView + .reporter() + .warning( + definition.getReference().toSourceString() + " is not obfuscated due to " + rule); + foundRule = true; + } + } + if (!foundRule) { + boolean foundReason = false; + for (KeepReason reason : joiner.getReasons()) { + appView + .reporter() + .warning( + definition.getReference().toSourceString() + + " is not obfuscated due to " + + reason); + foundReason = true; + } + assert foundReason; + } + } + public void keepClass(DexProgramClass clazz) { joinClass(clazz, KeepInfo.Joiner::top); } @@ -652,9 +709,11 @@ KeepMethodInfo.Joiner joiner = info.joiner(); fn.accept(joiner); KeepMethodInfo joined = joiner.join(); - if (!info.equals(joined)) { - keepMethodInfo.put(method.getReference(), canonicalizer.canonicalizeKeepMethodInfo(joined)); + if (info.equals(joined)) { + return; } + keepMethodInfo.put(method.getReference(), canonicalizer.canonicalizeKeepMethodInfo(joined)); + reportWhyAreYouNotObfuscating(method, info, joined, joiner); } public void keepMethod(ProgramMethod method) { @@ -670,9 +729,11 @@ Joiner joiner = info.joiner(); fn.accept(joiner); KeepFieldInfo joined = joiner.join(); - if (!info.equals(joined)) { - keepFieldInfo.put(field.getReference(), canonicalizer.canonicalizeKeepFieldInfo(joined)); + if (info.equals(joined)) { + return; } + keepFieldInfo.put(field.getReference(), canonicalizer.canonicalizeKeepFieldInfo(joined)); + reportWhyAreYouNotObfuscating(field, info, joined, joiner); } public void keepField(ProgramField field) {
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java index 597b016..a9879ad 100644 --- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java +++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -48,6 +48,7 @@ private final boolean allowUnusedArgumentOptimization; private final boolean allowUnusedReturnValueOptimization; private final boolean allowParameterNamesRemoval; + private final boolean whyAreYouNotInlining; private final KeepAnnotationCollectionInfo parameterAnnotationsInfo; protected KeepMethodInfo(Builder builder) { @@ -68,6 +69,7 @@ this.allowUnusedArgumentOptimization = builder.isUnusedArgumentOptimizationAllowed(); this.allowUnusedReturnValueOptimization = builder.isUnusedReturnValueOptimizationAllowed(); this.allowParameterNamesRemoval = builder.isParameterNamesRemovalAllowed(); + this.whyAreYouNotInlining = builder.isWhyAreYouNotInliningEnabled(); this.parameterAnnotationsInfo = builder.getParameterAnnotationsInfo().build(); } @@ -293,6 +295,14 @@ return allowParameterNamesRemoval; } + public boolean isWhyAreYouNotInliningEnabled() { + return internalIsWhyAreYouNotInliningEnabled(); + } + + boolean internalIsWhyAreYouNotInliningEnabled() { + return whyAreYouNotInlining; + } + public Joiner joiner() { assert !isTop(); return new Joiner(this); @@ -326,7 +336,8 @@ && allowUnusedArgumentOptimization == other.internalIsUnusedArgumentOptimizationAllowed() && allowUnusedReturnValueOptimization == other.internalIsUnusedReturnValueOptimizationAllowed() - && allowParameterNamesRemoval == other.internalIsParameterNamesRemovalAllowed(); + && allowParameterNamesRemoval == other.internalIsParameterNamesRemovalAllowed() + && whyAreYouNotInlining == other.internalIsWhyAreYouNotInliningEnabled(); } @Override @@ -365,6 +376,7 @@ hash += bit(allowUnusedArgumentOptimization, index++); hash += bit(allowUnusedReturnValueOptimization, index++); hash += bit(allowParameterNamesRemoval, index++); + hash += bit(whyAreYouNotInlining, index++); hash += bit(parameterAnnotationsInfo.isTop(), index); return hash; } @@ -432,6 +444,9 @@ case "allowParameterNamesRemoval": builder.setAllowParameterNamesRemoval(Boolean.parseBoolean(value)); break; + case "whyAreYouNotInlining": + builder.setWhyAreYouNotInlining(Boolean.parseBoolean(value)); + break; case "parameterAnnotationsInfo": builder.setParameterAnnotationInfo(KeepAnnotationCollectionInfoExported.parse(value)); break; @@ -462,6 +477,7 @@ lines.add("allowUnusedArgumentOptimization: " + allowUnusedArgumentOptimization); lines.add("allowUnusedReturnValueOptimization: " + allowUnusedReturnValueOptimization); lines.add("allowParameterNamesRemoval: " + allowParameterNamesRemoval); + lines.add("whyAreYouNotInlining: " + whyAreYouNotInlining); lines.add("parameterAnnotationsInfo: " + parameterAnnotationsInfo); return lines; } @@ -484,6 +500,7 @@ private boolean allowUnusedArgumentOptimization; private boolean allowUnusedReturnValueOptimization; private boolean allowParameterNamesRemoval; + private boolean whyAreYouNotInlining; private KeepAnnotationCollectionInfo.Builder parameterAnnotationsInfo; public Builder() { @@ -509,6 +526,7 @@ allowUnusedReturnValueOptimization = original.internalIsUnusedReturnValueOptimizationAllowed(); allowParameterNamesRemoval = original.internalIsParameterNamesRemovalAllowed(); + whyAreYouNotInlining = original.internalIsWhyAreYouNotInliningEnabled(); parameterAnnotationsInfo = original.internalParameterAnnotationsInfo().toBuilder(); } @@ -657,6 +675,15 @@ return self(); } + public boolean isWhyAreYouNotInliningEnabled() { + return whyAreYouNotInlining; + } + + public Builder setWhyAreYouNotInlining(boolean whyAreYouNotInlining) { + this.whyAreYouNotInlining = whyAreYouNotInlining; + return self(); + } + public KeepAnnotationCollectionInfo.Builder getParameterAnnotationsInfo() { return parameterAnnotationsInfo; } @@ -709,6 +736,7 @@ && isUnusedReturnValueOptimizationAllowed() == other.internalIsUnusedReturnValueOptimizationAllowed() && isParameterNamesRemovalAllowed() == other.internalIsParameterNamesRemovalAllowed() + && isWhyAreYouNotInliningEnabled() == other.internalIsWhyAreYouNotInliningEnabled() && parameterAnnotationsInfo.isEqualTo(other.parameterAnnotationsInfo); } @@ -736,6 +764,7 @@ .setAllowUnusedArgumentOptimization(false) .setAllowUnusedReturnValueOptimization(false) .setAllowParameterNamesRemoval(false) + .setWhyAreYouNotInlining(false) .setParameterAnnotationInfo(KeepAnnotationCollectionInfo.Builder.createTop()); } @@ -758,6 +787,7 @@ .setAllowUnusedArgumentOptimization(true) .setAllowUnusedReturnValueOptimization(true) .setAllowParameterNamesRemoval(true) + .setWhyAreYouNotInlining(false) .setParameterAnnotationInfo(KeepAnnotationCollectionInfo.Builder.createBottom()); } } @@ -862,6 +892,15 @@ return self(); } + public boolean isWhyAreYouNotInliningEnabled() { + return builder.isWhyAreYouNotInliningEnabled(); + } + + public Joiner setWhyAreYouNotInlining() { + builder.setWhyAreYouNotInlining(true); + return self(); + } + @Override public Joiner asMethodJoiner() { return this; @@ -904,7 +943,8 @@ Joiner::disallowUnusedReturnValueOptimization) .applyIf( !joiner.builder.isParameterNamesRemovalAllowed(), - Joiner::disallowParameterNamesRemoval); + Joiner::disallowParameterNamesRemoval) + .applyIf(joiner.builder.isWhyAreYouNotInliningEnabled(), Joiner::setWhyAreYouNotInlining); } @Override
diff --git a/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java index a2d6e00..755a476 100644 --- a/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java +++ b/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java
@@ -7,13 +7,13 @@ import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull; import static com.android.tools.r8.utils.MapUtils.ignoreKey; +import com.android.tools.r8.graph.Definition; import com.android.tools.r8.graph.DexDefinitionSupplier; import com.android.tools.r8.graph.DexField; import com.android.tools.r8.graph.DexMethod; import com.android.tools.r8.graph.DexProgramClass; import com.android.tools.r8.graph.DexReference; import com.android.tools.r8.graph.DexType; -import com.android.tools.r8.graph.ProgramDefinition; import com.android.tools.r8.graph.ProgramField; import com.android.tools.r8.graph.ProgramMethod; import com.android.tools.r8.graph.PrunedItems; @@ -133,15 +133,11 @@ minimumKeepInfo, (reference, minimumKeepInfoForReference) -> { assert !minimumKeepInfoForReference.isBottom(); - ProgramDefinition definition = + Definition definition = reference.apply( - clazz -> asProgramClassOrNull(definitions.definitionFor(clazz)), - field -> - field.lookupOnProgramClass( - asProgramClassOrNull(definitions.definitionFor(field.getHolderType()))), - method -> - method.lookupOnProgramClass( - asProgramClassOrNull(definitions.definitionFor(method.getHolderType())))); + definitions::definitionFor, + definitions::definitionFor, + definitions::definitionFor); return definition == null || !enqueuer.isReachable(definition); }); }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java index 5b0c3f1..4e47c83 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -3,12 +3,16 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.shaking; +import static com.android.tools.r8.shaking.ProguardKeepAttributes.RUNTIME_INVISIBLE_ANNOTATIONS; +import static com.android.tools.r8.shaking.ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS; + import com.android.tools.r8.errors.dontwarn.DontWarnConfiguration; import com.android.tools.r8.graph.DexItemFactory; import com.android.tools.r8.naming.DictionaryReader; import com.android.tools.r8.origin.Origin; import com.android.tools.r8.position.Position; import com.android.tools.r8.position.TextPosition; +import com.android.tools.r8.shaking.ProguardConfigurationParser.IncludeWorkItem; import com.android.tools.r8.shaking.ProguardConfigurationParser.ProguardConfigurationSourceParser; import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode; import com.android.tools.r8.utils.Reporter; @@ -18,6 +22,7 @@ import com.google.common.collect.Sets; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.function.Consumer; @@ -26,7 +31,7 @@ public static class Builder implements ProguardConfigurationParserConsumer { - private final List<String> parsedConfiguration = new ArrayList<>(); + private final StringBuilder parsedConfiguration = new StringBuilder(); private final List<FilteredClassPath> injars = new ArrayList<>(); private final List<FilteredClassPath> libraryJars = new ArrayList<>(); @@ -53,13 +58,11 @@ protected final Set<ProguardConfigurationRule> rules = Sets.newLinkedHashSet(); private final DexItemFactory dexItemFactory; private boolean printSeeds; - private Path seedFile; + private Path printSeedsFile; private Path obfuscationDictionary; private Path classObfuscationDictionary; private Path packageObfuscationDictionary; private boolean keepParameterNames; - private Origin keepParameterNamesOptionOrigin; - private Position keepParameterNamesOptionPosition; private final ProguardClassFilter.Builder adaptClassStrings = ProguardClassFilter.builder(); private final ProguardPathFilter.Builder adaptResourceFilenames = ProguardPathFilter.builder() @@ -85,43 +88,90 @@ } @Override - public void addParsedConfiguration(String source) { - parsedConfiguration.add(source); + public void addBaseDirectory( + Path baseDirectory, ProguardConfigurationSourceParser parser, TextPosition positionStart) { + // Intentionally empty. } @Override - public void addInjars(List<FilteredClassPath> injars, Origin origin, Position position) { + public void addIgnoredOption( + String option, ProguardConfigurationSourceParser parser, TextPosition positionStart) { + // Intentionally empty. + } + + @Override + public void addInclude( + Path includePath, ProguardConfigurationSourceParser parser, TextPosition positionStart) { + IncludeWorkItem include = new IncludeWorkItem(includePath, positionStart, parser.getOffset()); + parser.getPendingIncludes().add(include); + } + + @Override + public void addLeadingBOM() { + // Intentionally empty. + } + + @Override + public void addParsedConfiguration(ProguardConfigurationSourceParser parser) { + parsedConfiguration.append( + "# The proguard configuration file for the following section is " + parser.getOrigin()); + parsedConfiguration.append(System.lineSeparator()); + int lastIncludePositionEnd = 0; + for (IncludeWorkItem pendingInclude : parser.getPendingIncludes()) { + int includePositionStart = pendingInclude.includePositionStart.getOffsetAsInt(); + parsedConfiguration.append( + parser.getContentInRange(lastIncludePositionEnd, includePositionStart)); + lastIncludePositionEnd = pendingInclude.includePositionEnd; + } + parsedConfiguration.append(parser.getContentAfter(lastIncludePositionEnd)); + parsedConfiguration.append(System.lineSeparator()); + parsedConfiguration.append("# End of content from "); + parsedConfiguration.append(parser.getOrigin()); + parsedConfiguration.append(System.lineSeparator()); + } + + @Override + public void addInjars( + List<FilteredClassPath> injars, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { this.injars.addAll(injars); } @Override public void addLibraryJars( - List<FilteredClassPath> libraryJars, Origin origin, Position position) { + List<FilteredClassPath> libraryJars, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { this.libraryJars.addAll(libraryJars); } @Override - public void enableAllowAccessModification(Origin origin, Position position) { + public void enableAllowAccessModification( + ProguardConfigurationSourceParser parser, Position position, TextPosition positionStart) { this.allowAccessModification = true; } @Override - public void setIgnoreWarnings(boolean ignoreWarnings) { - this.ignoreWarnings = ignoreWarnings; + public void setIgnoreWarnings( + ProguardConfigurationSourceParser parser, TextPosition positionStart) { + this.ignoreWarnings = true; } @Override - public void disableOptimization(Origin origin, Position position) { + public void disableOptimization(ProguardConfigurationSourceParser parser, Position position) { this.optimizing = false; } @Override - public void disableObfuscation(Origin origin, Position position) { + public void disableObfuscation(ProguardConfigurationSourceParser parser, Position position) { this.obfuscating = false; } @Override - public void disableShrinking(Origin origin, Position position) { + public void disableShrinking(ProguardConfigurationSourceParser parser, Position position) { this.shrinking = false; } @@ -135,10 +185,6 @@ return this; } - boolean isAccessModificationEnabled() { - return allowAccessModification; - } - boolean isObfuscating() { return obfuscating; } @@ -157,79 +203,114 @@ } @Override - public void enablePrintConfiguration(Origin origin, Position position) { + public void enablePrintConfiguration( + Path printConfigurationFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { this.printConfiguration = true; + this.printConfigurationFile = printConfigurationFile; } @Override - public void setPrintConfigurationFile(Path file) { - assert printConfiguration; - this.printConfigurationFile = file; - } - - @Override - public void setPrintUsage(boolean printUsage) { - this.printUsage = printUsage; - } - - @Override - public void setPrintUsageFile(Path printUsageFile) { + public void enablePrintUsage( + Path printUsageFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + this.printUsage = true; this.printUsageFile = printUsageFile; } @Override - public void enablePrintMapping(Origin origin, Position position) { + public void enablePrintMapping( + Path printMappingFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { this.printMapping = true; + this.printMappingFile = printMappingFile; } @Override - public void setPrintMappingFile(Path file) { - assert printMapping; - this.printMappingFile = file; - } - - @Override - public void setApplyMappingFile(Path file, Origin origin, Position position) { - this.applyMappingFile = file; - } - - public boolean hasApplyMappingFile() { - return applyMappingFile != null; + public void setApplyMappingFile( + Path applyMappingFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + this.applyMappingFile = applyMappingFile; } @Override public void setRenameSourceFileAttribute( - String renameSourceFileAttribute, Origin origin, Position position) { + String renameSourceFileAttribute, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { this.renameSourceFileAttribute = renameSourceFileAttribute; } @Override public void addKeepAttributePatterns( List<String> keepAttributePatterns, - Origin origin, ProguardConfigurationSourceParser parser, Position position, TextPosition positionStart) { this.keepAttributePatterns.addAll(keepAttributePatterns); } + @Override + public void addKeepKotlinMetadata( + ProguardConfigurationSourceParser parser, Position position, TextPosition positionStart) { + Origin origin = parser.getOrigin(); + String source = "-keepkotlinmetadata"; + ProguardKeepRule keepKotlinMetadata = + ProguardKeepRuleUtils.keepClassAndMembersRule( + origin, positionStart, dexItemFactory.kotlinMetadataType, source); + ProguardKeepRule keepKotlinJvmNameAnnotation = + ProguardKeepRuleUtils.keepClassAndMembersRule( + origin, positionStart, dexItemFactory.kotlinJvmNameType, source); + // Mark the rules as used to ensure we do not report any information messages if the class + // is not present. + keepKotlinMetadata.markAsUsed(); + keepKotlinJvmNameAnnotation.markAsUsed(); + addRule(keepKotlinMetadata, parser, positionStart); + addRule(keepKotlinJvmNameAnnotation, parser, positionStart); + addKeepAttributePatterns( + Collections.singletonList(RUNTIME_VISIBLE_ANNOTATIONS), parser, position, positionStart); + addKeepAttributePatterns( + Collections.singletonList(RUNTIME_INVISIBLE_ANNOTATIONS), + parser, + position, + positionStart); + } + public Builder addKeepAttributePatterns(List<String> keepAttributePatterns) { this.keepAttributePatterns.addAll(keepAttributePatterns); return this; } @Override - public void addRule(ProguardConfigurationRule rule) { + public void addRule( + ProguardConfigurationRule rule, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { this.rules.add(rule); } @Override - public void addKeepPackageNamesPattern(ProguardClassNameList pattern) { + public void addKeepPackageNamesPattern( + ProguardClassNameList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { keepPackageNamesPatterns.addPattern(pattern); } @Override - public void joinMaxRemovedAndroidLogLevel(int maxRemovedAndroidLogLevel) { + public void joinMaxRemovedAndroidLogLevel( + int maxRemovedAndroidLogLevel, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { assert maxRemovedAndroidLogLevel >= MaximumRemovedAndroidLogLevelRule.NONE; if (this.maxRemovedAndroidLogLevel == MaximumRemovedAndroidLogLevelRule.NOT_SET) { this.maxRemovedAndroidLogLevel = maxRemovedAndroidLogLevel; @@ -251,91 +332,100 @@ } @Override - public void setPackagePrefix(String packagePrefix) { - this.packagePrefix = packagePrefix; - } - - @Override - public void setFlattenPackagePrefix(String packagePrefix) { - this.packagePrefix = packagePrefix; - } - - @Override - public void enableFlattenPackageHierarchy(Origin origin, Position position) { + public void enableFlattenPackageHierarchy( + String packagePrefix, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { packageObfuscationMode = PackageObfuscationMode.FLATTEN; + this.packagePrefix = packagePrefix; } @Override - public void enableRepackageClasses(Origin origin, Position position) { + public void enableRepackageClasses( + String packagePrefix, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { packageObfuscationMode = PackageObfuscationMode.REPACKAGE; + this.packagePrefix = packagePrefix; } @Override - public void addDontWarnPattern(ProguardClassNameList pattern) { + public void addDontWarnPattern( + ProguardClassNameList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { dontWarnPatterns.addPattern(pattern); } @Override - public void addDontNotePattern(ProguardClassNameList pattern) { + public void addDontNotePattern( + ProguardClassNameList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { dontNotePatterns.addPattern(pattern); } @Override - public void setSeedFile(Path seedFile) { - this.seedFile = seedFile; - } - - @Override - public void setPrintSeeds(boolean printSeeds, Origin origin, Position position) { - this.printSeeds = printSeeds; + public void enablePrintSeeds( + Path printSeedsFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { + this.printSeeds = true; + this.printSeedsFile = printSeedsFile; } @Override public void setObfuscationDictionary( - Path obfuscationDictionary, Origin origin, Position position) { + Path obfuscationDictionary, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { this.obfuscationDictionary = obfuscationDictionary; } @Override public void setClassObfuscationDictionary( - Path classObfuscationDictionary, Origin origin, Position position) { + Path classObfuscationDictionary, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { this.classObfuscationDictionary = classObfuscationDictionary; } @Override public void setPackageObfuscationDictionary( - Path packageObfuscationDictionary, Origin origin, Position position) { + Path packageObfuscationDictionary, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart) { this.packageObfuscationDictionary = packageObfuscationDictionary; } @Override public void setKeepParameterNames( - boolean keepParameterNames, Origin optionOrigin, Position optionPosition) { + ProguardConfigurationSourceParser optionOrigin, + Position optionPosition, + TextPosition positionStart) { assert optionOrigin != null || !keepParameterNames; - this.keepParameterNames = keepParameterNames; - this.keepParameterNamesOptionOrigin = optionOrigin; - this.keepParameterNamesOptionPosition = optionPosition; - } - - boolean isKeepParameterNames() { - return keepParameterNames; - } - - Origin getKeepParameterNamesOptionOrigin() { - return keepParameterNamesOptionOrigin; - } - - Position getKeepParameterNamesOptionPosition() { - return keepParameterNamesOptionPosition; + this.keepParameterNames = true; } @Override - public void addAdaptClassStringsPattern(ProguardClassNameList pattern) { + public void addAdaptClassStringsPattern( + ProguardClassNameList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { adaptClassStrings.addPattern(pattern); } @Override - public void addAdaptResourceFilenames(ProguardPathList pattern) { + public void addAdaptResourceFilenames( + ProguardPathList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { adaptResourceFilenames.addPattern(pattern); } @@ -346,18 +436,19 @@ } @Override - public void addAdaptResourceFileContents(ProguardPathList pattern) { + public void addAdaptResourceFileContents( + ProguardPathList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { adaptResourceFileContents.addPattern(pattern); } @Override - public void enableKeepDirectories() { - keepDirectories.enable(); - } - - @Override - public void addKeepDirectories(ProguardPathList pattern) { - keepDirectories.addPattern(pattern); + public void enableKeepDirectories( + ProguardPathList keepDirectoryPatterns, + ProguardConfigurationSourceParser parser, + TextPosition positionStart) { + keepDirectories.enable().addPattern(keepDirectoryPatterns); } public boolean isForceProguardCompatibility() { @@ -370,7 +461,8 @@ } @Override - public void enableProtoShrinking() { + public void enableProtoShrinking( + ProguardConfigurationSourceParser parser, TextPosition positionStart) { protoShrinking = true; } @@ -383,7 +475,7 @@ } ProguardConfiguration configuration = new ProguardConfiguration( - String.join(System.lineSeparator(), parsedConfiguration), + parsedConfiguration.toString(), dexItemFactory, injars, libraryJars, @@ -408,7 +500,7 @@ dontNotePatterns.build(), rules, printSeeds, - seedFile, + printSeedsFile, DictionaryReader.readAllNames(obfuscationDictionary, reporter), DictionaryReader.readAllNames(classObfuscationDictionary, reporter), DictionaryReader.readAllNames(packageObfuscationDictionary, reporter), @@ -471,6 +563,8 @@ private final ProguardPathFilter keepDirectories; private final boolean protoShrinking; private final int maxRemovedAndroidLogLevel; + private final boolean hasWhyAreYouNotInliningRule; + private final boolean hasWhyAreYouNotObfuscatingRule; private ProguardConfiguration( String parsedConfiguration, @@ -545,6 +639,10 @@ this.keepDirectories = keepDirectories; this.protoShrinking = protoShrinking; this.maxRemovedAndroidLogLevel = maxRemovedAndroidLogLevel; + this.hasWhyAreYouNotInliningRule = + Iterables.any(rules, rule -> rule instanceof WhyAreYouNotInliningRule); + this.hasWhyAreYouNotObfuscatingRule = + Iterables.any(rules, rule -> rule instanceof WhyAreYouNotObfuscatingRule); } /** @@ -716,6 +814,14 @@ return Iterables.any(rules, ProguardConfigurationRule::isMaximumRemovedAndroidLogLevelRule); } + public boolean hasWhyAreYouNotInliningRule() { + return hasWhyAreYouNotInliningRule; + } + + public boolean hasWhyAreYouNotObfuscatingRule() { + return hasWhyAreYouNotObfuscatingRule; + } + @Override public String toString() { StringBuilder builder = new StringBuilder();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java index dddd397..61355d7 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -3,8 +3,6 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.shaking; -import static com.android.tools.r8.shaking.ProguardKeepAttributes.RUNTIME_INVISIBLE_ANNOTATIONS; -import static com.android.tools.r8.shaking.ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS; import static com.android.tools.r8.utils.DescriptorUtils.javaTypeToDescriptor; import com.android.tools.r8.InputDependencyGraphConsumer; @@ -39,8 +37,11 @@ import java.nio.CharBuffer; import java.nio.file.NoSuchFileException; import java.nio.file.Path; +import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.Deque; import java.util.List; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -87,7 +88,7 @@ "dontusemixedcaseclassnames"); private static final List<String> IGNORED_CLASS_DESCRIPTOR_OPTIONS = - ImmutableList.of("isclassnamestring", "whyarenotsimple"); + ImmutableList.of("checkenumstringsdiscarded", "isclassnamestring", "whyarenotsimple"); private static final List<String> WARNED_SINGLE_ARG_OPTIONS = ImmutableList.of( // TODO(b/37137994): -outjars should be reported as errors, not just as warnings! @@ -135,7 +136,6 @@ reporter, ProguardConfigurationParserOptions.builder() .setEnableLegacyFullModeForKeepRules(false) - .setEnableExperimentalCheckEnumUnboxed(false) .setEnableTestingOptions(false) .build(), null, @@ -224,22 +224,45 @@ private final String name; private final String contents; private int position = 0; - private int positionAfterInclude = 0; private int line = 1; private int lineStartPosition = 0; private Path baseDirectory; private final Origin origin; + private final Deque<IncludeWorkItem> pendingIncludes = new ArrayDeque<>(); + ProguardConfigurationSourceParser(ProguardConfigurationSource source) throws IOException { // Strip any leading BOM here so it is not included in the text position. - contents = StringUtils.stripLeadingBOM(source.get()); baseDirectory = source.getBaseDirectory(); name = source.getName(); this.origin = source.getOrigin(); + String sourceWithPossibleLeadingBOM = source.get(); + if (StringUtils.hasLeadingBOM(sourceWithPossibleLeadingBOM)) { + contents = StringUtils.stripLeadingBOM(sourceWithPossibleLeadingBOM); + configurationConsumer.addLeadingBOM(); + } else { + contents = sourceWithPossibleLeadingBOM; + } + } + + public String getContentAfter(int start) { + return getContentInRange(start, contents.length()); + } + + public String getContentInRange(int start, int end) { + return contents.substring(start, end); } public String getContentSince(TextPosition start) { - return contents.substring(start.getOffsetAsInt(), position); + return getContentInRange(start.getOffsetAsInt(), position); + } + + public Origin getOrigin() { + return origin; + } + + public Collection<IncludeWorkItem> getPendingIncludes() { + return pendingIncludes; } public void parse() throws ProguardRuleParserException { @@ -249,14 +272,11 @@ configurationConsumer.addWhitespace(this, whitespaceStart); } } while (parseOption()); - // This may be unknown, but we want to always ensure that we don't attribute lines to the - // wrong configuration. - configurationConsumer.addParsedConfiguration( - "# The proguard configuration file for the following section is " + origin.toString()); - - // Collect the parsed configuration. - configurationConsumer.addParsedConfiguration(contents.substring(positionAfterInclude)); - configurationConsumer.addParsedConfiguration("# End of content from " + origin); + configurationConsumer.addParsedConfiguration(this); + while (!pendingIncludes.isEmpty()) { + IncludeWorkItem includeWorkItem = pendingIncludes.removeFirst(); + parseInclude(includeWorkItem.includePath, includeWorkItem.includePositionStart); + } reporter.failIfPendingErrors(); } @@ -264,74 +284,48 @@ if (eof()) { return false; } - if (acceptArobaseInclude()) { + TextPosition optionStart = getPosition(); + if (acceptArobaseInclude(optionStart)) { return true; } - TextPosition optionStart = getPosition(); expectChar('-'); if (parseIgnoredOption(optionStart) || parseIgnoredOptionAndWarn(optionStart) - || parseExperimentalOption(optionStart) || parseTestingOption(optionStart) || parseUnsupportedOptionAndErr(optionStart)) { // Intentionally left empty. } else if (acceptString("keepkotlinmetadata")) { - String source = "-keepkotlinmetadata"; - ProguardKeepRule keepKotlinMetadata = - ProguardKeepRuleUtils.keepClassAndMembersRule( - origin, optionStart, dexItemFactory.kotlinMetadataType, source); - ProguardKeepRule keepKotlinJvmNameAnnotation = - ProguardKeepRuleUtils.keepClassAndMembersRule( - origin, optionStart, dexItemFactory.kotlinJvmNameType, source); - // Mark the rules as used to ensure we do not report any information messages if the class - // is not present. - keepKotlinMetadata.markAsUsed(); - keepKotlinJvmNameAnnotation.markAsUsed(); - configurationConsumer.addRule(keepKotlinMetadata); - configurationConsumer.addRule(keepKotlinJvmNameAnnotation); - configurationConsumer.addKeepAttributePatterns( - Collections.singletonList(RUNTIME_VISIBLE_ANNOTATIONS), - origin, - this, - getPosition(optionStart), - optionStart); - configurationConsumer.addKeepAttributePatterns( - Collections.singletonList(RUNTIME_INVISIBLE_ANNOTATIONS), - origin, - this, - getPosition(optionStart), - optionStart); + configurationConsumer.addKeepKotlinMetadata(this, getPosition(optionStart), optionStart); } else if (acceptString("renamesourcefileattribute")) { skipWhitespace(); String renameSourceFileAttribute = isOptionalArgumentGiven() ? acceptQuotedOrUnquotedString() : ""; configurationConsumer.setRenameSourceFileAttribute( - renameSourceFileAttribute, origin, getPosition(optionStart)); + renameSourceFileAttribute, this, getPosition(optionStart), optionStart); } else if (acceptString("keepattributes")) { parseKeepAttributes(optionStart); } else if (acceptString("keeppackagenames")) { - parseClassFilter(configurationConsumer::addKeepPackageNamesPattern); + ProguardClassNameList keepPackageNamePatterns = parseOptionalClassFilter(); + configurationConsumer.addKeepPackageNamesPattern( + keepPackageNamePatterns, this, optionStart); } else if (acceptString("keepparameternames")) { - configurationConsumer.setKeepParameterNames(true, origin, getPosition(optionStart)); + configurationConsumer.setKeepParameterNames(this, getPosition(optionStart), optionStart); } else if (acceptString("checkdiscard")) { ProguardCheckDiscardRule rule = parseRuleWithClassSpec(optionStart, ProguardCheckDiscardRule.builder()); - configurationConsumer.addRule(rule); - } else if (acceptString("checkenumstringsdiscarded")) { - // Not supported, ignore. - parseRuleWithClassSpec(optionStart, ProguardCheckDiscardRule.builder()); + configurationConsumer.addRule(rule, this, optionStart); } else if (acceptString("keepdirectories")) { - configurationConsumer.enableKeepDirectories(); - parsePathFilter(configurationConsumer::addKeepDirectories); + ProguardPathList keepDirectoryPatterns = parseOptionalPathFilter(); + configurationConsumer.enableKeepDirectories(keepDirectoryPatterns, this, optionStart); } else if (acceptString("keep")) { ProguardKeepRule rule = parseKeepRule(optionStart); - configurationConsumer.addRule(rule); + configurationConsumer.addRule(rule, this, optionStart); } else if (acceptString("whyareyoukeeping")) { - ProguardWhyAreYouKeepingRule rule = - parseRuleWithClassSpec(optionStart, ProguardWhyAreYouKeepingRule.builder()); - configurationConsumer.addRule(rule); + WhyAreYouKeepingRule rule = + parseRuleWithClassSpec(optionStart, WhyAreYouKeepingRule.builder()); + configurationConsumer.addRule(rule, this, optionStart); } else if (acceptString("dontoptimize")) { - configurationConsumer.disableOptimization(origin, getPosition(optionStart)); + configurationConsumer.disableOptimization(this, getPosition(optionStart)); } else if (acceptString("optimizationpasses")) { skipWhitespace(); Integer expectedOptimizationPasses = acceptInteger(); @@ -339,42 +333,43 @@ throw reporter.fatalError(new StringDiagnostic( "Missing n of \"-optimizationpasses n\"", origin, getPosition(optionStart))); } + configurationConsumer.addIgnoredOption("optimizationpasses", this, optionStart); infoIgnoringOptions("optimizationpasses", optionStart); } else if (acceptString("dontobfuscate")) { - configurationConsumer.disableObfuscation(origin, getPosition(optionStart)); + configurationConsumer.disableObfuscation(this, getPosition(optionStart)); } else if (acceptString("dontshrink")) { - configurationConsumer.disableShrinking(origin, getPosition(optionStart)); + configurationConsumer.disableShrinking(this, getPosition(optionStart)); } else if (acceptString("printusage")) { - configurationConsumer.setPrintUsage(true); skipWhitespace(); - if (isOptionalArgumentGiven()) { - configurationConsumer.setPrintUsageFile(parseFileName(false)); - } + configurationConsumer.enablePrintUsage( + parseOptionalFileName(), this, getPosition(optionStart), optionStart); } else if (acceptString("shrinkunusedprotofields")) { - configurationConsumer.enableProtoShrinking(); + configurationConsumer.enableProtoShrinking(this, optionStart); } else if (acceptString("ignorewarnings")) { - configurationConsumer.setIgnoreWarnings(true); + configurationConsumer.setIgnoreWarnings(this, optionStart); } else if (acceptString("dontwarn")) { - parseClassFilter(configurationConsumer::addDontWarnPattern); + ProguardClassNameList dontWarnPattern = parseOptionalClassFilter(); + configurationConsumer.addDontWarnPattern(dontWarnPattern, this, optionStart); } else if (acceptString("dontnote")) { - parseClassFilter(configurationConsumer::addDontNotePattern); + ProguardClassNameList dontNotePattern = parseOptionalClassFilter(); + configurationConsumer.addDontNotePattern(dontNotePattern, this, optionStart); } else if (acceptString(REPACKAGE_CLASSES)) { if (configurationConsumer.getPackageObfuscationMode() == PackageObfuscationMode.FLATTEN) { warnOverridingOptions(REPACKAGE_CLASSES, FLATTEN_PACKAGE_HIERARCHY, optionStart); } skipWhitespace(); char quote = acceptQuoteIfPresent(); + String packagePrefix; if (isQuote(quote)) { - configurationConsumer.setPackagePrefix(parsePackageNameOrEmptyString()); + packagePrefix = parsePackageNameOrEmptyString(); expectClosingQuote(quote); + } else if (hasNextChar('-')) { + packagePrefix = ""; } else { - if (hasNextChar('-')) { - configurationConsumer.setPackagePrefix(""); - } else { - configurationConsumer.setPackagePrefix(parsePackageNameOrEmptyString()); - } + packagePrefix = parsePackageNameOrEmptyString(); } - configurationConsumer.enableRepackageClasses(origin, getPosition(optionStart)); + configurationConsumer.enableRepackageClasses( + packagePrefix, this, getPosition(optionStart), optionStart); } else if (acceptString(FLATTEN_PACKAGE_HIERARCHY)) { if (configurationConsumer.getPackageObfuscationMode() == PackageObfuscationMode.REPACKAGE) { warnOverridingOptions(REPACKAGE_CLASSES, FLATTEN_PACKAGE_HIERARCHY, optionStart); @@ -385,108 +380,119 @@ } else { skipWhitespace(); char quote = acceptQuoteIfPresent(); + String packagePrefix; if (isQuote(quote)) { - configurationConsumer.setFlattenPackagePrefix(parsePackageNameOrEmptyString()); + packagePrefix = parsePackageNameOrEmptyString(); expectClosingQuote(quote); + } else if (hasNextChar('-')) { + packagePrefix = ""; } else { - if (hasNextChar('-')) { - configurationConsumer.setFlattenPackagePrefix(""); - } else { - configurationConsumer.setFlattenPackagePrefix(parsePackageNameOrEmptyString()); - } + packagePrefix = parsePackageNameOrEmptyString(); } - configurationConsumer.enableFlattenPackageHierarchy(origin, getPosition(optionStart)); + configurationConsumer.enableFlattenPackageHierarchy( + packagePrefix, this, getPosition(optionStart), optionStart); } } else if (acceptString("allowaccessmodification")) { - configurationConsumer.enableAllowAccessModification(origin, getPosition(optionStart)); + configurationConsumer.enableAllowAccessModification( + this, getPosition(optionStart), optionStart); } else if (acceptString("printconfiguration")) { - configurationConsumer.enablePrintConfiguration(origin, getPosition(optionStart)); skipWhitespace(); - if (isOptionalArgumentGiven()) { - configurationConsumer.setPrintConfigurationFile(parseFileName(false)); - } + configurationConsumer.enablePrintConfiguration( + parseOptionalFileName(), this, getPosition(optionStart), optionStart); } else if (acceptString("printmapping")) { - configurationConsumer.enablePrintMapping(origin, getPosition(optionStart)); skipWhitespace(); - if (isOptionalArgumentGiven()) { - configurationConsumer.setPrintMappingFile(parseFileName(false)); - } + configurationConsumer.enablePrintMapping( + parseOptionalFileName(), this, getPosition(optionStart), optionStart); } else if (acceptString("applymapping")) { + Path applyMappingFile = + parseFileInputDependency(inputDependencyConsumer::acceptProguardApplyMapping); configurationConsumer.setApplyMappingFile( - parseFileInputDependency(inputDependencyConsumer::acceptProguardApplyMapping), - origin, - getPosition(optionStart)); + applyMappingFile, this, getPosition(optionStart), optionStart); } else if (acceptString("assumenosideeffects")) { ProguardAssumeNoSideEffectRule rule = parseAssumeNoSideEffectsRule(optionStart); - configurationConsumer.addRule(rule); + configurationConsumer.addRule(rule, this, optionStart); } else if (acceptString("assumevalues")) { ProguardAssumeValuesRule rule = parseAssumeValuesRule(optionStart); - configurationConsumer.addRule(rule); + configurationConsumer.addRule(rule, this, optionStart); } else if (acceptString("include")) { - // Collect the parsed configuration until the include. - configurationConsumer.addParsedConfiguration( - contents.substring(positionAfterInclude, position - ("include".length() + 1))); skipWhitespace(); - parseInclude(); - positionAfterInclude = position; + enqueueInclude(optionStart); } else if (acceptString("basedirectory")) { skipWhitespace(); - baseDirectory = parseFileName(false); + baseDirectory = parseFileName(); + configurationConsumer.addBaseDirectory(baseDirectory, this, optionStart); } else if (acceptString("injars")) { configurationConsumer.addInjars( parseClassPath(inputDependencyConsumer::acceptProguardInJars), - origin, - getPosition(optionStart)); + this, + getPosition(optionStart), + optionStart); } else if (acceptString("libraryjars")) { configurationConsumer.addLibraryJars( parseClassPath(inputDependencyConsumer::acceptProguardLibraryJars), - origin, - getPosition(optionStart)); + this, + getPosition(optionStart), + optionStart); } else if (acceptString("printseeds")) { - configurationConsumer.setPrintSeeds(true, origin, getPosition(optionStart)); skipWhitespace(); - if (isOptionalArgumentGiven()) { - configurationConsumer.setSeedFile(parseFileName(false)); - } + configurationConsumer.enablePrintSeeds( + parseOptionalFileName(), this, getPosition(optionStart), optionStart); } else if (acceptString("obfuscationdictionary")) { + Path obfuscationDictionary = + parseFileInputDependency(inputDependencyConsumer::acceptProguardObfuscationDictionary); configurationConsumer.setObfuscationDictionary( - parseFileInputDependency(inputDependencyConsumer::acceptProguardObfuscationDictionary), - origin, - getPosition(optionStart)); + obfuscationDictionary, this, getPosition(optionStart), optionStart); } else if (acceptString("classobfuscationdictionary")) { + Path classObfuscationDictionary = + parseFileInputDependency( + inputDependencyConsumer::acceptProguardClassObfuscationDictionary); configurationConsumer.setClassObfuscationDictionary( - parseFileInputDependency( - inputDependencyConsumer::acceptProguardClassObfuscationDictionary), - origin, - getPosition(optionStart)); + classObfuscationDictionary, this, getPosition(optionStart), optionStart); } else if (acceptString("packageobfuscationdictionary")) { - configurationConsumer.setPackageObfuscationDictionary( + Path packageObfuscationDictionary = parseFileInputDependency( - inputDependencyConsumer::acceptProguardPackageObfuscationDictionary), - origin, - getPosition(optionStart)); + inputDependencyConsumer::acceptProguardPackageObfuscationDictionary); + configurationConsumer.setPackageObfuscationDictionary( + packageObfuscationDictionary, this, getPosition(optionStart), optionStart); } else if (acceptString("alwaysinline")) { InlineRule rule = parseRuleWithClassSpec( optionStart, InlineRule.builder().setType(InlineRuleType.ALWAYS)); - configurationConsumer.addRule(rule); + configurationConsumer.addRule(rule, this, optionStart); } else if (acceptString("adaptclassstrings")) { - parseClassFilter(configurationConsumer::addAdaptClassStringsPattern); + ProguardClassNameList adaptClassStringsPattern = parseOptionalClassFilter(); + configurationConsumer.addAdaptClassStringsPattern( + adaptClassStringsPattern, this, optionStart); } else if (acceptString("adaptresourcefilenames")) { - parsePathFilter(configurationConsumer::addAdaptResourceFilenames); + ProguardPathList pattern = parseOptionalPathFilter(); + configurationConsumer.addAdaptResourceFilenames(pattern, this, optionStart); } else if (acceptString("adaptresourcefilecontents")) { - parsePathFilter(configurationConsumer::addAdaptResourceFileContents); + ProguardPathList pattern = parseOptionalPathFilter(); + configurationConsumer.addAdaptResourceFileContents(pattern, this, optionStart); } else if (acceptString("identifiernamestring")) { configurationConsumer.addRule( - parseRuleWithClassSpec(optionStart, ProguardIdentifierNameStringRule.builder())); + parseRuleWithClassSpec(optionStart, ProguardIdentifierNameStringRule.builder()), + this, + optionStart); } else if (acceptString("if")) { - configurationConsumer.addRule(parseIfRule(optionStart)); + configurationConsumer.addRule(parseIfRule(optionStart), this, optionStart); + } else if (acceptString(CheckEnumUnboxedRule.RULE_NAME)) { + configurationConsumer.addRule(parseCheckEnumUnboxedRule(optionStart), this, optionStart); + return true; } else if (acceptString(ConvertCheckNotNullRule.RULE_NAME)) { - configurationConsumer.addRule(parseConvertCheckNotNullRule(optionStart)); + configurationConsumer.addRule(parseConvertCheckNotNullRule(optionStart), this, optionStart); + return true; + } else if (acceptString(WhyAreYouNotObfuscatingRule.RULE_NAME)) { + configurationConsumer.addRule( + parseRuleWithClassSpec(optionStart, WhyAreYouNotObfuscatingRule.builder()), + this, + optionStart); return true; } else if (acceptString(WhyAreYouNotInliningRule.RULE_NAME)) { configurationConsumer.addRule( - parseRuleWithClassSpec(optionStart, WhyAreYouNotInliningRule.builder())); + parseRuleWithClassSpec(optionStart, WhyAreYouNotInliningRule.builder()), + this, + optionStart); return true; } else if (parseMaximumRemovedAndroidLogLevelRule(optionStart)) { return true; @@ -503,171 +509,83 @@ return true; } - private boolean parseExperimentalOption(TextPosition optionStart) - throws ProguardRuleParserException { - if (acceptString(CheckEnumUnboxedRule.RULE_NAME)) { - CheckEnumUnboxedRule checkEnumUnboxedRule = parseCheckEnumUnboxedRule(optionStart); - if (options.isExperimentalCheckEnumUnboxedEnabled()) { - configurationConsumer.addRule(checkEnumUnboxedRule); - } - return true; - } - return false; - } - private boolean parseTestingOption(TextPosition optionStart) throws ProguardRuleParserException { - if (options.isTestingOptionsEnabled()) { - if (acceptString("assumemayhavesideeffects")) { - ProguardAssumeMayHaveSideEffectsRule rule = - parseAssumeMayHaveSideEffectsRule(optionStart); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString(KeepConstantArgumentRule.RULE_NAME)) { - KeepConstantArgumentRule rule = - parseRuleWithClassSpec(optionStart, KeepConstantArgumentRule.builder()); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString(KeepUnusedArgumentRule.RULE_NAME)) { - KeepUnusedArgumentRule rule = - parseRuleWithClassSpec(optionStart, KeepUnusedArgumentRule.builder()); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString(KeepUnusedReturnValueRule.RULE_NAME)) { - KeepUnusedReturnValueRule rule = - parseRuleWithClassSpec(optionStart, KeepUnusedReturnValueRule.builder()); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString("alwaysclassinline")) { - ClassInlineRule rule = - parseRuleWithClassSpec( - optionStart, ClassInlineRule.builder().setType(ClassInlineRule.Type.ALWAYS)); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString("neverclassinline")) { - ClassInlineRule rule = - parseRuleWithClassSpec( - optionStart, ClassInlineRule.builder().setType(ClassInlineRule.Type.NEVER)); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString("neverinline")) { - InlineRule rule = - parseRuleWithClassSpec( - optionStart, InlineRule.builder().setType(InlineRuleType.NEVER)); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString("neversinglecallerinline")) { - InlineRule rule = - parseRuleWithClassSpec( - optionStart, InlineRule.builder().setType(InlineRuleType.NEVER_SINGLE_CALLER)); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString(NoAccessModificationRule.RULE_NAME)) { - NoAccessModificationRule rule = - parseRuleWithClassSpec(optionStart, NoAccessModificationRule.builder()); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString(NoFieldTypeStrengtheningRule.RULE_NAME)) { - NoFieldTypeStrengtheningRule rule = - parseRuleWithClassSpec(optionStart, NoFieldTypeStrengtheningRule.builder()); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString(NoUnusedInterfaceRemovalRule.RULE_NAME)) { - NoUnusedInterfaceRemovalRule rule = - parseRuleWithClassSpec(optionStart, NoUnusedInterfaceRemovalRule.builder()); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString(NoVerticalClassMergingRule.RULE_NAME)) { - NoVerticalClassMergingRule rule = - parseRuleWithClassSpec(optionStart, NoVerticalClassMergingRule.builder()); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString(NoHorizontalClassMergingRule.RULE_NAME)) { - NoHorizontalClassMergingRule rule = - parseRuleWithClassSpec(optionStart, NoHorizontalClassMergingRule.builder()); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString(NoMethodStaticizingRule.RULE_NAME)) { - NoMethodStaticizingRule rule = - parseRuleWithClassSpec(optionStart, NoMethodStaticizingRule.builder()); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString(NoParameterReorderingRule.RULE_NAME)) { - NoParameterReorderingRule rule = - parseRuleWithClassSpec(optionStart, NoParameterReorderingRule.builder()); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString(NoParameterTypeStrengtheningRule.RULE_NAME)) { - NoParameterTypeStrengtheningRule rule = - parseRuleWithClassSpec(optionStart, NoParameterTypeStrengtheningRule.builder()); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString(NoRedundantFieldLoadEliminationRule.RULE_NAME)) { - NoRedundantFieldLoadEliminationRule rule = - parseRuleWithClassSpec(optionStart, NoRedundantFieldLoadEliminationRule.builder()); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString(NoReturnTypeStrengtheningRule.RULE_NAME)) { - NoReturnTypeStrengtheningRule rule = - parseRuleWithClassSpec(optionStart, NoReturnTypeStrengtheningRule.builder()); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString("neverpropagatevalue")) { - NoValuePropagationRule rule = - parseRuleWithClassSpec(optionStart, NoValuePropagationRule.builder()); - configurationConsumer.addRule(rule); - return true; - } - if (acceptString("neverreprocessclassinitializer")) { - configurationConsumer.addRule( - parseRuleWithClassSpec( - optionStart, - ReprocessClassInitializerRule.builder() - .setType(ReprocessClassInitializerRule.Type.NEVER))); - return true; - } - if (acceptString("neverreprocessmethod")) { - configurationConsumer.addRule( - parseRuleWithClassSpec( - optionStart, - ReprocessMethodRule.builder().setType(ReprocessMethodRule.Type.NEVER))); - return true; - } - if (acceptString("reprocessclassinitializer")) { - configurationConsumer.addRule( - parseRuleWithClassSpec( - optionStart, - ReprocessClassInitializerRule.builder() - .setType(ReprocessClassInitializerRule.Type.ALWAYS))); - return true; - } - if (acceptString("reprocessmethod")) { - configurationConsumer.addRule( - parseRuleWithClassSpec( - optionStart, - ReprocessMethodRule.builder().setType(ReprocessMethodRule.Type.ALWAYS))); - return true; - } + if (!options.isTestingOptionsEnabled()) { + return false; } - return false; + ProguardConfigurationRule rule; + if (acceptString("assumemayhavesideeffects")) { + rule = parseAssumeMayHaveSideEffectsRule(optionStart); + } else if (acceptString(KeepConstantArgumentRule.RULE_NAME)) { + rule = parseRuleWithClassSpec(optionStart, KeepConstantArgumentRule.builder()); + } else if (acceptString(KeepUnusedArgumentRule.RULE_NAME)) { + rule = parseRuleWithClassSpec(optionStart, KeepUnusedArgumentRule.builder()); + } else if (acceptString(KeepUnusedReturnValueRule.RULE_NAME)) { + rule = parseRuleWithClassSpec(optionStart, KeepUnusedReturnValueRule.builder()); + } else if (acceptString("alwaysclassinline")) { + rule = + parseRuleWithClassSpec( + optionStart, ClassInlineRule.builder().setType(ClassInlineRule.Type.ALWAYS)); + } else if (acceptString("neverclassinline")) { + rule = + parseRuleWithClassSpec( + optionStart, ClassInlineRule.builder().setType(ClassInlineRule.Type.NEVER)); + } else if (acceptString("neverinline")) { + rule = + parseRuleWithClassSpec(optionStart, InlineRule.builder().setType(InlineRuleType.NEVER)); + } else if (acceptString("neversinglecallerinline")) { + rule = + parseRuleWithClassSpec( + optionStart, InlineRule.builder().setType(InlineRuleType.NEVER_SINGLE_CALLER)); + } else if (acceptString(NoAccessModificationRule.RULE_NAME)) { + rule = parseRuleWithClassSpec(optionStart, NoAccessModificationRule.builder()); + } else if (acceptString(NoFieldTypeStrengtheningRule.RULE_NAME)) { + rule = parseRuleWithClassSpec(optionStart, NoFieldTypeStrengtheningRule.builder()); + } else if (acceptString(NoUnusedInterfaceRemovalRule.RULE_NAME)) { + rule = parseRuleWithClassSpec(optionStart, NoUnusedInterfaceRemovalRule.builder()); + } else if (acceptString(NoVerticalClassMergingRule.RULE_NAME)) { + rule = parseRuleWithClassSpec(optionStart, NoVerticalClassMergingRule.builder()); + } else if (acceptString(NoHorizontalClassMergingRule.RULE_NAME)) { + rule = parseRuleWithClassSpec(optionStart, NoHorizontalClassMergingRule.builder()); + } else if (acceptString(NoMethodStaticizingRule.RULE_NAME)) { + rule = parseRuleWithClassSpec(optionStart, NoMethodStaticizingRule.builder()); + } else if (acceptString(NoParameterReorderingRule.RULE_NAME)) { + rule = parseRuleWithClassSpec(optionStart, NoParameterReorderingRule.builder()); + } else if (acceptString(NoParameterTypeStrengtheningRule.RULE_NAME)) { + rule = parseRuleWithClassSpec(optionStart, NoParameterTypeStrengtheningRule.builder()); + } else if (acceptString(NoRedundantFieldLoadEliminationRule.RULE_NAME)) { + rule = parseRuleWithClassSpec(optionStart, NoRedundantFieldLoadEliminationRule.builder()); + } else if (acceptString(NoReturnTypeStrengtheningRule.RULE_NAME)) { + rule = parseRuleWithClassSpec(optionStart, NoReturnTypeStrengtheningRule.builder()); + } else if (acceptString("neverpropagatevalue")) { + rule = parseRuleWithClassSpec(optionStart, NoValuePropagationRule.builder()); + } else if (acceptString("neverreprocessclassinitializer")) { + rule = + parseRuleWithClassSpec( + optionStart, + ReprocessClassInitializerRule.builder() + .setType(ReprocessClassInitializerRule.Type.NEVER)); + } else if (acceptString("neverreprocessmethod")) { + rule = + parseRuleWithClassSpec( + optionStart, ReprocessMethodRule.builder().setType(ReprocessMethodRule.Type.NEVER)); + } else if (acceptString("reprocessclassinitializer")) { + rule = + parseRuleWithClassSpec( + optionStart, + ReprocessClassInitializerRule.builder() + .setType(ReprocessClassInitializerRule.Type.ALWAYS)); + } else if (acceptString("reprocessmethod")) { + rule = + parseRuleWithClassSpec( + optionStart, + ReprocessMethodRule.builder().setType(ReprocessMethodRule.Type.ALWAYS)); + } else { + return false; + } + configurationConsumer.addRule(rule, this, optionStart); + return true; } private RuntimeException unknownOption(String unknownOption, TextPosition optionStart) { @@ -709,43 +627,66 @@ } } } + configurationConsumer.addIgnoredOption(option, this, optionStart); warnIgnoringOptions(option, optionStart); return true; } private boolean parseIgnoredOption(TextPosition optionStart) throws ProguardRuleParserException { - return Iterables.any(IGNORED_SINGLE_ARG_OPTIONS, this::skipOptionWithSingleArg) - || Iterables.any( - IGNORED_OPTIONAL_SINGLE_ARG_OPTIONS, this::skipOptionWithOptionalSingleArg) - || Iterables.any(IGNORED_FLAG_OPTIONS, this::skipFlag) - || Iterables.any(IGNORED_CLASS_DESCRIPTOR_OPTIONS, this::skipOptionWithClassSpec) - || parseOptimizationOption(optionStart); + String option = + Iterables.find(IGNORED_SINGLE_ARG_OPTIONS, this::skipOptionWithSingleArg, null); + if (option == null) { + option = + Iterables.find( + IGNORED_OPTIONAL_SINGLE_ARG_OPTIONS, this::skipOptionWithOptionalSingleArg, null); + if (option == null) { + option = Iterables.find(IGNORED_FLAG_OPTIONS, this::skipFlag, null); + if (option == null) { + option = + Iterables.find( + IGNORED_CLASS_DESCRIPTOR_OPTIONS, this::skipOptionWithClassSpec, null); + if (option == null) { + if (parseOptimizationOption(optionStart)) { + option = "optimizations"; + } else { + return false; + } + } + } + } + } + configurationConsumer.addIgnoredOption(option, this, optionStart); + return true; } - private void parseInclude() throws ProguardRuleParserException { - TextPosition start = getPosition(); - Path included = parseFileInputDependency(inputDependencyConsumer::acceptProguardInclude); + private void enqueueInclude(TextPosition optionStart) throws ProguardRuleParserException { + Path includePath = parseFileInputDependency(inputDependencyConsumer::acceptProguardInclude); + configurationConsumer.addInclude(includePath, this, optionStart); + } + + private void parseInclude(Path includePath, TextPosition includePositionStart) + throws ProguardRuleParserException { try { - new ProguardConfigurationSourceParser(new ProguardConfigurationSourceFile(included)) + new ProguardConfigurationSourceParser(new ProguardConfigurationSourceFile(includePath)) .parse(); } catch (FileNotFoundException | NoSuchFileException e) { - throw parseError("Included file '" + included.toString() + "' not found", - start, e); + throw parseError("Included file '" + includePath + "' not found", includePositionStart, e); } catch (IOException e) { - throw parseError("Failed to read included file '" + included.toString() + "'", - start, e); + throw parseError( + "Failed to read included file '" + includePath + "'", includePositionStart, e); } } - private boolean acceptArobaseInclude() throws ProguardRuleParserException { + private boolean acceptArobaseInclude(TextPosition optionStart) + throws ProguardRuleParserException { if (remainingChars() < 2) { return false; } if (!acceptChar('@')) { return false; } - parseInclude(); + enqueueInclude(optionStart); return true; } @@ -768,7 +709,7 @@ } } configurationConsumer.addKeepAttributePatterns( - attributesPatterns, origin, this, getPosition(start), start); + attributesPatterns, this, getPosition(start), start); } private boolean skipFlag(String name) { @@ -914,45 +855,48 @@ "Expecting '-keep' option after '-if' option.", origin, getPosition(optionStart))); } - private boolean parseMaximumRemovedAndroidLogLevelRule(Position start) + private boolean parseMaximumRemovedAndroidLogLevelRule(TextPosition optionStart) throws ProguardRuleParserException { - if (acceptString("maximumremovedandroidloglevel")) { - skipWhitespace(); - // First parse the mandatory log level int. - Integer maxRemovedAndroidLogLevel = acceptInteger(); - if (maxRemovedAndroidLogLevel == null - || maxRemovedAndroidLogLevel < MaximumRemovedAndroidLogLevelRule.NONE) { - throw parseError("Expected integer greater than or equal to 1", getPosition()); - } - MaximumRemovedAndroidLogLevelRule.Builder builder = - MaximumRemovedAndroidLogLevelRule.builder() - .setMaxRemovedAndroidLogLevel(maxRemovedAndroidLogLevel) - .setOrigin(origin) - .setStart(start); - // Check if we can parse any class annotations or flag. - if (parseClassAnnotationsAndFlags(builder)) { - // Parse the remainder of the class specification. - parseClassSpecFromClassTypeInclusive(builder, false); - } else { - // Otherwise check if we can parse a class name. - parseClassType( - builder, - // Parse the remainder of the class specification. - () -> parseClassSpecFromClassNameInclusive(builder, false), - // In case of an error, move position back to the place we expected an (optional) - // class type. - expectedClassTypeStart -> position = expectedClassTypeStart.getOffsetAsInt()); - } - if (builder.hasClassType()) { - Position end = getPosition(); - configurationConsumer.addRule( - builder.setEnd(end).setSource(getSourceSnippet(contents, start, end)).build()); - } else { - configurationConsumer.joinMaxRemovedAndroidLogLevel(maxRemovedAndroidLogLevel); - } - return true; + if (!acceptString("maximumremovedandroidloglevel")) { + return false; } - return false; + skipWhitespace(); + // First parse the mandatory log level int. + Integer maxRemovedAndroidLogLevel = acceptInteger(); + if (maxRemovedAndroidLogLevel == null + || maxRemovedAndroidLogLevel < MaximumRemovedAndroidLogLevelRule.NONE) { + throw parseError("Expected integer greater than or equal to 1", getPosition()); + } + MaximumRemovedAndroidLogLevelRule.Builder builder = + MaximumRemovedAndroidLogLevelRule.builder() + .setMaxRemovedAndroidLogLevel(maxRemovedAndroidLogLevel) + .setOrigin(origin) + .setStart(optionStart); + // Check if we can parse any class annotations or flag. + if (parseClassAnnotationsAndFlags(builder)) { + // Parse the remainder of the class specification. + parseClassSpecFromClassTypeInclusive(builder, false); + } else { + // Otherwise check if we can parse a class name. + parseClassType( + builder, + // Parse the remainder of the class specification. + () -> parseClassSpecFromClassNameInclusive(builder, false), + // In case of an error, move position back to the place we expected an (optional) + // class type. + expectedClassTypeStart -> position = expectedClassTypeStart.getOffsetAsInt()); + } + if (builder.hasClassType()) { + Position end = getPosition(); + configurationConsumer.addRule( + builder.setEnd(end).setSource(getSourceSnippet(contents, optionStart, end)).build(), + this, + optionStart); + } else { + configurationConsumer.joinMaxRemovedAndroidLogLevel( + maxRemovedAndroidLogLevel, this, optionStart); + } + return true; } void verifyAndLinkBackReferences(Iterable<ProguardWildcard> wildcards) { @@ -1550,20 +1494,28 @@ } } } - - if (copied == 0) return fileName; - - result.append(fileName.substring(copied, fileName.length())); + if (copied == 0) { + return fileName; + } + result.append(fileName.substring(copied)); return result.toString(); } private Path parseFileInputDependency(BiConsumer<Origin, Path> dependencyConsumer) throws ProguardRuleParserException { - Path file = parseFileName(false); + Path file = parseFileName(); dependencyConsumer.accept(origin, file); return file; } + private Path parseOptionalFileName() throws ProguardRuleParserException { + return isOptionalArgumentGiven() ? parseFileName() : null; + } + + private Path parseFileName() throws ProguardRuleParserException { + return parseFileName(false); + } + private Path parseFileName(boolean stopAfterPathSeparator) throws ProguardRuleParserException { TextPosition start = getPosition(); skipWhitespace(); @@ -2026,6 +1978,9 @@ if (isQuote(quote)) { expectClosingQuote(quote); } + int lastPatternEndLine = line; + int lastPatternEndLineStartPosition = lineStartPosition; + int lastPatternEndPosition = position; while (pattern != null) { patterns.add(pattern); skipWhitespace(); @@ -2037,17 +1992,24 @@ if (isQuote(quote)) { expectClosingQuote(quote); } + lastPatternEndLine = line; + lastPatternEndLineStartPosition = lineStartPosition; + lastPatternEndPosition = position; if (pattern == null) { throw parseError("Expected list element", start); } } else { - pattern = null; + break; } } skipWhitespace(); if (!eof() && !hasNextChar('-') && !hasNextChar('@')) { throw parseError("Unexpected attribute"); } + // Position the parser at the end of the rule before notifying the configuration consumer. + line = lastPatternEndLine; + lineStartPosition = lastPatternEndLineStartPosition; + position = lastPatternEndPosition; return patterns; } @@ -2086,15 +2048,11 @@ } } - private void parseClassFilter(Consumer<ProguardClassNameList> consumer) - throws ProguardRuleParserException { + private ProguardClassNameList parseOptionalClassFilter() throws ProguardRuleParserException { skipWhitespace(); - if (isOptionalArgumentGiven()) { - consumer.accept(parseClassNames()); - } else { - consumer.accept( - ProguardClassNameList.singletonList(ProguardTypeMatcher.defaultAllMatcher())); - } + return isOptionalArgumentGiven() + ? parseClassNames() + : ProguardClassNameList.singletonList(ProguardTypeMatcher.defaultAllMatcher()); } private void parseClassNameAddToBuilder(ProguardClassNameList.Builder builder) @@ -2139,14 +2097,9 @@ return character != ',' && !Character.isWhitespace(character); } - private void parsePathFilter(Consumer<ProguardPathList> consumer) - throws ProguardRuleParserException { + private ProguardPathList parseOptionalPathFilter() throws ProguardRuleParserException { skipWhitespace(); - if (isOptionalArgumentGiven()) { - consumer.accept(parsePathFilter()); - } else { - consumer.accept(ProguardPathList.emptyList()); - } + return isOptionalArgumentGiven() ? parsePathFilter() : ProguardPathList.emptyList(); } private ProguardPathList parsePathFilter() throws ProguardRuleParserException { @@ -2234,6 +2187,10 @@ "Ignoring modifier: " + modifier, origin, getPosition(start))); } + int getOffset() { + return position; + } + private Position getPosition(TextPosition start) { if (start.getOffset() == position) { return start; @@ -2327,4 +2284,17 @@ this.negated = negated; } } + + static class IncludeWorkItem { + + final Path includePath; + final TextPosition includePositionStart; + final int includePositionEnd; + + IncludeWorkItem(Path includePath, TextPosition includePositionStart, int includePositionEnd) { + this.includePath = includePath; + this.includePositionStart = includePositionStart; + this.includePositionEnd = includePositionEnd; + } + } }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserConsumer.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserConsumer.java index ea658c5..b3067da 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserConsumer.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserConsumer.java
@@ -3,7 +3,6 @@ // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.shaking; -import com.android.tools.r8.origin.Origin; import com.android.tools.r8.position.Position; import com.android.tools.r8.position.TextPosition; import com.android.tools.r8.shaking.ProguardConfigurationParser.ProguardConfigurationSourceParser; @@ -13,88 +12,168 @@ public interface ProguardConfigurationParserConsumer { - void addParsedConfiguration(String s); + void addBaseDirectory( + Path baseDirectory, ProguardConfigurationSourceParser parser, TextPosition positionStart); - void addRule(ProguardConfigurationRule rule); + void addIgnoredOption( + String option, ProguardConfigurationSourceParser parser, TextPosition positionStart); + + void addInclude( + Path includePath, ProguardConfigurationSourceParser parser, TextPosition positionStart); + + void addLeadingBOM(); + + void addParsedConfiguration(ProguardConfigurationSourceParser parser); + + void addRule( + ProguardConfigurationRule rule, + ProguardConfigurationSourceParser parser, + TextPosition positionStart); void addKeepAttributePatterns( List<String> attributesPatterns, - Origin origin, ProguardConfigurationSourceParser parser, Position position, TextPosition positionStart); - void setRenameSourceFileAttribute(String s, Origin origin, Position position); + void addKeepKotlinMetadata( + ProguardConfigurationSourceParser parser, Position position, TextPosition positionStart); - void addKeepPackageNamesPattern(ProguardClassNameList proguardClassNameList); + void addKeepPackageNamesPattern( + ProguardClassNameList proguardClassNameList, + ProguardConfigurationSourceParser parser, + TextPosition positionStart); - void setKeepParameterNames(boolean b, Origin origin, Position position); + void setKeepParameterNames( + ProguardConfigurationSourceParser parser, Position position, TextPosition positionStart); - void enableKeepDirectories(); + void setRenameSourceFileAttribute( + String s, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart); - void addKeepDirectories(ProguardPathList proguardPathList); + void enableKeepDirectories( + ProguardPathList keepDirectoryPatterns, + ProguardConfigurationSourceParser parser, + TextPosition positionStart); - void disableOptimization(Origin origin, Position position); + void enablePrintConfiguration( + Path printConfigurationFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart); - void disableObfuscation(Origin origin, Position position); + void enablePrintMapping( + Path printMappingFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart); - void disableShrinking(Origin origin, Position position); + void enablePrintSeeds( + Path printSeedsFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart); - void setPrintUsage(boolean b); + void enablePrintUsage( + Path printUsageFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart); - void setPrintUsageFile(Path path); + void disableOptimization(ProguardConfigurationSourceParser parser, Position position); - void enableProtoShrinking(); + void disableObfuscation(ProguardConfigurationSourceParser parser, Position position); - void setIgnoreWarnings(boolean b); + void disableShrinking(ProguardConfigurationSourceParser parser, Position position); - void addDontWarnPattern(ProguardClassNameList pattern); + void enableProtoShrinking(ProguardConfigurationSourceParser parser, TextPosition positionStart); - void addDontNotePattern(ProguardClassNameList pattern); + void setIgnoreWarnings(ProguardConfigurationSourceParser parser, TextPosition positionStart); - void enableAllowAccessModification(Origin origin, Position position); + void addDontWarnPattern( + ProguardClassNameList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart); - void enablePrintConfiguration(Origin origin, Position position); + void addDontNotePattern( + ProguardClassNameList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart); - void setPrintConfigurationFile(Path path); + void enableAllowAccessModification( + ProguardConfigurationSourceParser parser, Position position, TextPosition positionStart); - void enablePrintMapping(Origin origin, Position position); + void setApplyMappingFile( + Path applyMappingFile, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart); - void setPrintMappingFile(Path path); + void addInjars( + List<FilteredClassPath> filteredClassPaths, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart); - void setApplyMappingFile(Path path, Origin origin, Position position); + void addLibraryJars( + List<FilteredClassPath> filteredClassPaths, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart); - void addInjars(List<FilteredClassPath> filteredClassPaths, Origin origin, Position position); + void setObfuscationDictionary( + Path path, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart); - void addLibraryJars(List<FilteredClassPath> filteredClassPaths, Origin origin, Position position); + void setClassObfuscationDictionary( + Path path, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart); - void setPrintSeeds(boolean b, Origin origin, Position position); + void setPackageObfuscationDictionary( + Path path, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart); - void setSeedFile(Path path); + void addAdaptClassStringsPattern( + ProguardClassNameList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart); - void setObfuscationDictionary(Path path, Origin origin, Position position); + void addAdaptResourceFileContents( + ProguardPathList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart); - void setClassObfuscationDictionary(Path path, Origin origin, Position position); + void addAdaptResourceFilenames( + ProguardPathList pattern, + ProguardConfigurationSourceParser parser, + TextPosition positionStart); - void setPackageObfuscationDictionary(Path path, Origin origin, Position position); - - void addAdaptClassStringsPattern(ProguardClassNameList pattern); - - void addAdaptResourceFileContents(ProguardPathList pattern); - - void addAdaptResourceFilenames(ProguardPathList pattern); - - void joinMaxRemovedAndroidLogLevel(int maxRemovedAndroidLogLevel); + void joinMaxRemovedAndroidLogLevel( + int maxRemovedAndroidLogLevel, + ProguardConfigurationSourceParser parser, + TextPosition positionStart); PackageObfuscationMode getPackageObfuscationMode(); - void setPackagePrefix(String s); + void enableRepackageClasses( + String packagePrefix, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart); - void setFlattenPackagePrefix(String s); - - void enableRepackageClasses(Origin origin, Position position); - - void enableFlattenPackageHierarchy(Origin origin, Position position); + void enableFlattenPackageHierarchy( + String packagePrefix, + ProguardConfigurationSourceParser parser, + Position position, + TextPosition positionStart); default void addWhitespace(ProguardConfigurationSourceParser parser, TextPosition position) {} }
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserOptions.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserOptions.java index b7392e7..6ebba5a 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserOptions.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParserOptions.java
@@ -10,7 +10,6 @@ private final boolean enableLegacyFullModeForKeepRules; private final boolean enableLegacyFullModeForKeepRulesWarnings; - private final boolean enableExperimentalCheckEnumUnboxed; private final boolean enableKeepRuntimeInvisibleAnnotations; private final boolean enableTestingOptions; private final boolean forceProguardCompatibility; @@ -18,11 +17,9 @@ ProguardConfigurationParserOptions( boolean enableLegacyFullModeForKeepRules, boolean enableLegacyFullModeForKeepRulesWarnings, - boolean enableExperimentalCheckEnumUnboxed, boolean enableKeepRuntimeInvisibleAnnotations, boolean enableTestingOptions, boolean forceProguardCompatibility) { - this.enableExperimentalCheckEnumUnboxed = enableExperimentalCheckEnumUnboxed; this.enableKeepRuntimeInvisibleAnnotations = enableKeepRuntimeInvisibleAnnotations; this.enableTestingOptions = enableTestingOptions; this.enableLegacyFullModeForKeepRules = enableLegacyFullModeForKeepRules; @@ -44,10 +41,6 @@ return !forceProguardCompatibility && enableLegacyFullModeForKeepRulesWarnings; } - public boolean isExperimentalCheckEnumUnboxedEnabled() { - return enableExperimentalCheckEnumUnboxed; - } - public boolean isKeepRuntimeInvisibleAnnotationsEnabled() { return enableKeepRuntimeInvisibleAnnotations; } @@ -60,7 +53,6 @@ private boolean enableLegacyFullModeForKeepRules = true; private boolean enableLegacyFullModeForKeepRulesWarnings = false; - private boolean enableExperimentalCheckEnumUnboxed; private boolean enableKeepRuntimeInvisibleAnnotations = true; private boolean enableTestingOptions; private boolean forceProguardCompatibility = false; @@ -72,9 +64,6 @@ enableLegacyFullModeForKeepRulesWarnings = parseSystemPropertyOrDefault( "com.android.tools.r8.enableLegacyFullModeForKeepRulesWarnings", false); - enableExperimentalCheckEnumUnboxed = - parseSystemPropertyOrDefault( - "com.android.tools.r8.experimental.enablecheckenumunboxed", false); enableKeepRuntimeInvisibleAnnotations = parseSystemPropertyOrDefault( "com.android.tools.r8.enableKeepRuntimeInvisibleAnnotations", true); @@ -94,12 +83,6 @@ return this; } - public Builder setEnableExperimentalCheckEnumUnboxed( - boolean enableExperimentalCheckEnumUnboxed) { - this.enableExperimentalCheckEnumUnboxed = enableExperimentalCheckEnumUnboxed; - return this; - } - public Builder setEnableTestingOptions(boolean enableTestingOptions) { this.enableTestingOptions = enableTestingOptions; return this; @@ -114,7 +97,6 @@ return new ProguardConfigurationParserOptions( enableLegacyFullModeForKeepRules, enableLegacyFullModeForKeepRulesWarnings, - enableExperimentalCheckEnumUnboxed, enableKeepRuntimeInvisibleAnnotations, enableTestingOptions, forceProguardCompatibility);
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java index cf407e6..e0848b0 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java +++ b/src/main/java/com/android/tools/r8/shaking/ProguardKeepAttributes.java
@@ -224,60 +224,58 @@ public StringBuilder append(StringBuilder builder) { List<String> attributes = new ArrayList<>(); - if (sourceFile) { - attributes.add(SOURCE_FILE); - } - if (innerClasses) { - attributes.add(INNER_CLASSES); + if (annotationDefault) { + attributes.add(ANNOTATION_DEFAULT); } if (enclosingMethod) { attributes.add(ENCLOSING_METHOD); } - if (signature) { - attributes.add(SIGNATURE); - } if (exceptions) { attributes.add(EXCEPTIONS); } + if (innerClasses) { + attributes.add(INNER_CLASSES); + } if (methodParameters) { attributes.add(METHOD_PARAMETERS); } - if (sourceDebugExtension) { - attributes.add(SOURCE_DEBUG_EXTENSION); - } - if (runtimeVisibleAnnotations) { - attributes.add(RUNTIME_VISIBLE_ANNOTATIONS); + if (permittedSubclasses) { + attributes.add(PERMITTED_SUBCLASSES); } if (runtimeInvisibleAnnotations) { attributes.add(RUNTIME_INVISIBLE_ANNOTATIONS); } - if (runtimeVisibleParameterAnnotations) { - attributes.add(RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS); - } if (runtimeInvisibleParameterAnnotations) { attributes.add(RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS); } - if (runtimeVisibleTypeAnnotations) { - attributes.add(RUNTIME_VISIBLE_TYPE_ANNOTATIONS); - } if (runtimeInvisibleTypeAnnotations) { attributes.add(RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); } - if (annotationDefault) { - attributes.add(ANNOTATION_DEFAULT); + if (runtimeVisibleAnnotations) { + attributes.add(RUNTIME_VISIBLE_ANNOTATIONS); + } + if (runtimeVisibleParameterAnnotations) { + attributes.add(RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS); + } + if (runtimeVisibleTypeAnnotations) { + attributes.add(RUNTIME_VISIBLE_TYPE_ANNOTATIONS); + } + if (signature) { + attributes.add(SIGNATURE); + } + if (sourceDebugExtension) { + attributes.add(SOURCE_DEBUG_EXTENSION); + } + if (sourceFile) { + attributes.add(SOURCE_FILE); } if (stackMapTable) { attributes.add(STACK_MAP_TABLE); } - if (permittedSubclasses) { - attributes.add(PERMITTED_SUBCLASSES); - } - - if (attributes.size() > 0) { + if (!attributes.isEmpty()) { builder.append("-keepattributes "); builder.append(String.join(",", attributes)); } - return builder; }
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java index 2541fcb..3906ea2 100644 --- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java +++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -138,7 +138,6 @@ DependentMinimumKeepInfoCollection.createConcurrent(); private final LinkedHashMap<DexReference, DexReference> reasonAsked = new LinkedHashMap<>(); private final Set<DexMethod> alwaysInline = Sets.newIdentityHashSet(); - private final Set<DexMethod> whyAreYouNotInlining = Sets.newIdentityHashSet(); private final Set<DexMethod> reprocess = Sets.newIdentityHashSet(); private final PredicateSet<DexType> alwaysClassInline = new PredicateSet<>(); private final Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule = @@ -444,8 +443,7 @@ evaluateCheckDiscardRule(clazz, rule.asProguardCheckDiscardRule()); } else if (rule instanceof CheckEnumUnboxedRule) { evaluateCheckEnumUnboxedRule(clazz, (CheckEnumUnboxedRule) rule); - } else if (rule instanceof NoAccessModificationRule - || rule instanceof ProguardWhyAreYouKeepingRule) { + } else if (rule instanceof NoAccessModificationRule || rule instanceof WhyAreYouKeepingRule) { markClass(clazz, rule, ifRulePreconditionMatch); markMatchingVisibleMethods( clazz, methodKeepRules, rule, null, true, true, ifRulePreconditionMatch); @@ -499,9 +497,14 @@ } else if (rule instanceof ProguardIdentifierNameStringRule) { markMatchingFields(clazz, fieldKeepRules, rule, ifRulePreconditionMatch); markMatchingMethods(clazz, methodKeepRules, rule, ifRulePreconditionMatch); - } else { - assert rule instanceof ConvertCheckNotNullRule; + } else if (rule instanceof ConvertCheckNotNullRule) { markMatchingMethods(clazz, methodKeepRules, rule, ifRulePreconditionMatch); + } else if (rule instanceof WhyAreYouNotObfuscatingRule) { + markClass(clazz, rule, ifRulePreconditionMatch); + markMatchingFields(clazz, fieldKeepRules, rule, ifRulePreconditionMatch); + markMatchingMethods(clazz, methodKeepRules, rule, ifRulePreconditionMatch); + } else { + assert false : rule.getClass().getName(); } } @@ -658,7 +661,6 @@ dependentMinimumKeepInfo, ImmutableList.copyOf(reasonAsked.values()), alwaysInline, - whyAreYouNotInlining, reprocess, alwaysClassInline, mayHaveSideEffects, @@ -1441,7 +1443,7 @@ evaluateAssumeNoSideEffectsRule(item, (ProguardAssumeNoSideEffectRule) context, rule); } else if (context instanceof ProguardAssumeValuesRule) { evaluateAssumeValuesRule(item, (ProguardAssumeValuesRule) context, rule); - } else if (context instanceof ProguardWhyAreYouKeepingRule) { + } else if (context instanceof WhyAreYouKeepingRule) { reasonAsked.computeIfAbsent(item.getReference(), i -> i); context.markAsUsed(); } else if (context.isProguardCheckDiscardRule()) { @@ -1482,7 +1484,15 @@ if (!item.isMethod()) { throw new Unreachable(); } - whyAreYouNotInlining.add(item.asMethod().getReference()); + dependentMinimumKeepInfo + .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference()) + .asMethodJoiner() + .setWhyAreYouNotInlining(); + context.markAsUsed(); + } else if (context instanceof WhyAreYouNotObfuscatingRule) { + dependentMinimumKeepInfo + .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference()) + .setWhyAreYouNotObfuscating(); context.markAsUsed(); } else if (context.isClassInlineRule()) { ClassInlineRule classInlineRule = context.asClassInlineRule(); @@ -2202,7 +2212,6 @@ public final ImmutableList<DexReference> reasonAsked; public final Set<DexMethod> alwaysInline; - public final Set<DexMethod> whyAreYouNotInlining; public final Set<DexMethod> reprocess; public final PredicateSet<DexType> alwaysClassInline; public final Map<DexReference, ProguardMemberRule> mayHaveSideEffects; @@ -2215,7 +2224,6 @@ DependentMinimumKeepInfoCollection dependentMinimumKeepInfo, ImmutableList<DexReference> reasonAsked, Set<DexMethod> alwaysInline, - Set<DexMethod> whyAreYouNotInlining, Set<DexMethod> reprocess, PredicateSet<DexType> alwaysClassInline, Map<DexReference, ProguardMemberRule> mayHaveSideEffects, @@ -2233,7 +2241,6 @@ pendingMethodMoveInverse); this.reasonAsked = reasonAsked; this.alwaysInline = alwaysInline; - this.whyAreYouNotInlining = whyAreYouNotInlining; this.reprocess = reprocess; this.alwaysClassInline = alwaysClassInline; this.mayHaveSideEffects = mayHaveSideEffects; @@ -2347,7 +2354,6 @@ getDependentMinimumKeepInfo().rewrittenWithLens(graphLens, timing), reasonAsked, alwaysInline, - whyAreYouNotInlining, reprocess, alwaysClassInline, mayHaveSideEffects, @@ -2648,7 +2654,6 @@ reasonAsked, Collections.emptySet(), Collections.emptySet(), - Collections.emptySet(), PredicateSet.empty(), emptyMap(), emptyMap(),
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java b/src/main/java/com/android/tools/r8/shaking/WhyAreYouKeepingRule.java similarity index 86% rename from src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java rename to src/main/java/com/android/tools/r8/shaking/WhyAreYouKeepingRule.java index cffaab1..e04c6e6 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java +++ b/src/main/java/com/android/tools/r8/shaking/WhyAreYouKeepingRule.java
@@ -7,11 +7,11 @@ import com.android.tools.r8.position.Position; import java.util.List; -public class ProguardWhyAreYouKeepingRule extends ProguardConfigurationRule { +public class WhyAreYouKeepingRule extends ProguardConfigurationRule { @SuppressWarnings("NonCanonicalType") public static class Builder - extends ProguardConfigurationRule.Builder<ProguardWhyAreYouKeepingRule, Builder> { + extends ProguardConfigurationRule.Builder<WhyAreYouKeepingRule, Builder> { private Builder() { super(); @@ -23,8 +23,8 @@ } @Override - public ProguardWhyAreYouKeepingRule build() { - return new ProguardWhyAreYouKeepingRule( + public WhyAreYouKeepingRule build() { + return new WhyAreYouKeepingRule( origin, getPosition(), source, @@ -41,7 +41,7 @@ } } - private ProguardWhyAreYouKeepingRule( + private WhyAreYouKeepingRule( Origin origin, Position position, String source,
diff --git a/src/main/java/com/android/tools/r8/shaking/WhyAreYouNotInliningRule.java b/src/main/java/com/android/tools/r8/shaking/WhyAreYouNotInliningRule.java index 4801930..f114450 100644 --- a/src/main/java/com/android/tools/r8/shaking/WhyAreYouNotInliningRule.java +++ b/src/main/java/com/android/tools/r8/shaking/WhyAreYouNotInliningRule.java
@@ -77,6 +77,13 @@ return new Builder(); } + // The ServiceLoader rewriter emits debug information if -whyareyounotinlining matches + // ServiceLoader.load. + @Override + public boolean isApplicableToLibraryClasses() { + return true; + } + @Override String typeString() { return RULE_NAME;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java b/src/main/java/com/android/tools/r8/shaking/WhyAreYouNotObfuscatingRule.java similarity index 79% copy from src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java copy to src/main/java/com/android/tools/r8/shaking/WhyAreYouNotObfuscatingRule.java index cffaab1..b6b291b 100644 --- a/src/main/java/com/android/tools/r8/shaking/ProguardWhyAreYouKeepingRule.java +++ b/src/main/java/com/android/tools/r8/shaking/WhyAreYouNotObfuscatingRule.java
@@ -1,4 +1,4 @@ -// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file +// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. package com.android.tools.r8.shaking; @@ -7,11 +7,13 @@ import com.android.tools.r8.position.Position; import java.util.List; -public class ProguardWhyAreYouKeepingRule extends ProguardConfigurationRule { +public class WhyAreYouNotObfuscatingRule extends ProguardConfigurationRule { + + public static final String RULE_NAME = "whyareyounotobfuscating"; @SuppressWarnings("NonCanonicalType") public static class Builder - extends ProguardConfigurationRule.Builder<ProguardWhyAreYouKeepingRule, Builder> { + extends ProguardConfigurationRule.Builder<WhyAreYouNotObfuscatingRule, Builder> { private Builder() { super(); @@ -23,8 +25,8 @@ } @Override - public ProguardWhyAreYouKeepingRule build() { - return new ProguardWhyAreYouKeepingRule( + public WhyAreYouNotObfuscatingRule build() { + return new WhyAreYouNotObfuscatingRule( origin, getPosition(), source, @@ -41,7 +43,7 @@ } } - private ProguardWhyAreYouKeepingRule( + private WhyAreYouNotObfuscatingRule( Origin origin, Position position, String source, @@ -77,6 +79,6 @@ @Override String typeString() { - return "whyareyoukeeping"; + return RULE_NAME; } }
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java index 27ae067..89992ae 100644 --- a/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java +++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticMethodBuilder.java
@@ -186,7 +186,6 @@ return method.isStatic() && method.isNonAbstractNonNativeMethod() && method.isPublic() - && method.annotations().isEmpty() && method.getParameterAnnotations().isEmpty(); }
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 c0f2cbc..8ab29e7 100644 --- a/src/main/java/com/android/tools/r8/utils/StringUtils.java +++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -407,12 +407,13 @@ return Character.isWhitespace(codePoint) || isBOM(codePoint); } + public static boolean hasLeadingBOM(String s) { + return !s.isEmpty() && s.charAt(0) == StringUtils.BOM; + } + public static String stripLeadingBOM(String s) { - if (s.length() > 0 && s.charAt(0) == StringUtils.BOM) { - return s.substring(1); - } else { - return s; - } + assert hasLeadingBOM(s); + return s.substring(1); } public static String trim(String s) {
diff --git a/src/test/java/com/android/tools/r8/assistant/JavaLangClassJsonTest.java b/src/test/java/com/android/tools/r8/assistant/JavaLangClassJsonTest.java index 5e1fbf8..ff05d32 100644 --- a/src/test/java/com/android/tools/r8/assistant/JavaLangClassJsonTest.java +++ b/src/test/java/com/android/tools/r8/assistant/JavaLangClassJsonTest.java
@@ -15,6 +15,7 @@ import com.android.tools.r8.assistant.postprocessing.model.ClassGetMember; import com.android.tools.r8.assistant.postprocessing.model.ClassGetMembers; import com.android.tools.r8.assistant.postprocessing.model.ClassGetName; +import com.android.tools.r8.assistant.postprocessing.model.ClassNewInstance; import com.android.tools.r8.assistant.postprocessing.model.ReflectiveEvent; import com.android.tools.r8.assistant.runtime.ReflectiveEventType; import com.android.tools.r8.assistant.runtime.ReflectiveOperationJsonLogger; @@ -69,7 +70,7 @@ .assertSuccess(); List<ReflectiveEvent> reflectiveEvents = new ReflectiveOperationJsonParser(factoryBox.get()).parse(path); - Assert.assertEquals(29, reflectiveEvents.size()); + Assert.assertEquals(30, reflectiveEvents.size()); assertTrue(reflectiveEvents.get(4).isClassGetMember()); ClassGetMember updater00 = reflectiveEvents.get(4).asClassGetMember(); @@ -163,6 +164,11 @@ Reference.methodFromMethod(Bar.class.getConstructor()), updater24.getMember().asDexMethod().asMethodReference()); + assertTrue(reflectiveEvents.get(29).isClassNewInstance()); + ClassNewInstance updater29 = reflectiveEvents.get(29).asClassNewInstance(); + assertEquals(ReflectiveEventType.CLASS_NEW_INSTANCE, updater29.getEventType()); + assertEquals(Bar.class.getName(), updater29.getType().toSourceString()); + Box<KeepInfoCollectionExported> keepInfoBox = new Box<>(); testForR8(parameters) .addProgramClasses(JavaLangClassTestClass.class, Foo.class, Bar.class) @@ -188,6 +194,7 @@ "public com.android.tools.r8.assistant.JavaLangClassTestClass$Bar()", "true", "class com.android.tools.r8.assistant.JavaLangClassTestClass$Bar", + "11", "END"); KeepInfoCollectionExported keepInfoCollectionExported = keepInfoBox.get();
diff --git a/src/test/java/com/android/tools/r8/assistant/JavaLangClassTest.java b/src/test/java/com/android/tools/r8/assistant/JavaLangClassTest.java index 374014b..ce78e14 100644 --- a/src/test/java/com/android/tools/r8/assistant/JavaLangClassTest.java +++ b/src/test/java/com/android/tools/r8/assistant/JavaLangClassTest.java
@@ -85,6 +85,8 @@ "true", "40", "class com.android.tools.r8.assistant.JavaLangClassTestClass$Bar", + "50", + "11", "END"); } @@ -201,7 +203,7 @@ @Override public void onClassNewInstance(Stack stack, Class<?> clazz) { - super.onClassNewInstance(stack, clazz); + printNumIfTrue(clazz.getName().endsWith("Bar"), 50); } @Override
diff --git a/src/test/java/com/android/tools/r8/assistant/JavaLangClassTestClass.java b/src/test/java/com/android/tools/r8/assistant/JavaLangClassTestClass.java index a0308d6..feb74d1 100644 --- a/src/test/java/com/android/tools/r8/assistant/JavaLangClassTestClass.java +++ b/src/test/java/com/android/tools/r8/assistant/JavaLangClassTestClass.java
@@ -52,6 +52,13 @@ Bar cast = Bar.class.cast(o); System.out.println(Bar.class.isInstance(o)); System.out.println(Bar.class.asSubclass(Foo.class)); + Bar newBar = null; + try { + newBar = Bar.class.newInstance(); + System.out.println(newBar.bar()); + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } System.out.println("END"); } catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException e) { System.out.println("EXCEPTION " + e.getMessage());
diff --git a/src/test/java/com/android/tools/r8/assistant/ProxyJsonTest.java b/src/test/java/com/android/tools/r8/assistant/ProxyJsonTest.java new file mode 100644 index 0000000..0f75c00 --- /dev/null +++ b/src/test/java/com/android/tools/r8/assistant/ProxyJsonTest.java
@@ -0,0 +1,84 @@ +// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file +// for details. 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.assistant; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.assistant.postprocessing.ReflectiveOperationJsonParser; +import com.android.tools.r8.assistant.postprocessing.model.ProxyNewProxyInstance; +import com.android.tools.r8.assistant.postprocessing.model.ReflectiveEvent; +import com.android.tools.r8.assistant.runtime.ReflectiveEventType; +import com.android.tools.r8.assistant.runtime.ReflectiveOperationJsonLogger; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.shaking.KeepInfoCollectionExported; +import com.android.tools.r8.utils.Box; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class ProxyJsonTest extends TestBase { + + @Parameter(0) + public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNativeMultidexDexRuntimes().withMaximumApiLevel().build(); + } + + @Test + public void testInstrumentationWithCustomOracle() throws Exception { + Path path = Paths.get(temp.newFile().getAbsolutePath()); + Box<DexItemFactory> factoryBox = new Box<>(); + testForAssistant() + .addProgramClassesAndInnerClasses(ProxyTestClass.class) + .addInstrumentationClasses(Instrumentation.class) + .setCustomReflectiveOperationReceiver(Instrumentation.class) + .setMinApi(parameters) + .addOptionsModification(opt -> factoryBox.set(opt.itemFactory)) + .compile() + .addVmArguments("-Dcom.android.tools.r8.reflectiveJsonLogger=" + path) + .run(parameters.getRuntime(), ProxyTestClass.class) + .assertSuccess(); + List<ReflectiveEvent> reflectiveEvents = + new ReflectiveOperationJsonParser(factoryBox.get()).parse(path); + assertEquals(1, reflectiveEvents.size()); + + assertTrue(reflectiveEvents.get(0).isProxyNewProxyInstance()); + ProxyNewProxyInstance updater0 = reflectiveEvents.get(0).asProxyNewProxyInstance(); + assertEquals(ReflectiveEventType.PROXY_NEW_PROXY_INSTANCE, updater0.getEventType()); + assertEquals(ProxyTestClass.F.class.getName(), updater0.getInterfaces()[0].toSourceString()); + assertEquals(1, updater0.getInterfaces().length); + + Box<KeepInfoCollectionExported> keepInfoBox = new Box<>(); + testForR8(parameters) + .addProgramClassesAndInnerClasses(ProxyTestClass.class) + .addOptionsModification( + opt -> opt.testing.finalKeepInfoCollectionConsumer = keepInfoBox::set) + .setMinApi(parameters) + .addKeepMainRule(ProxyTestClass.class) + .run(parameters.getRuntime(), ProxyTestClass.class) + .assertSuccess(); + + KeepInfoCollectionExported keepInfoCollectionExported = keepInfoBox.get(); + + assertTrue(updater0.isKeptBy(keepInfoCollectionExported)); + } + + public static class Instrumentation extends ReflectiveOperationJsonLogger { + public Instrumentation() throws IOException {} + } +}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/DefaultInterfaceMethodCollisionInSubclassAfterClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/DefaultInterfaceMethodCollisionInSubclassAfterClassMergingTest.java index a492bae..996c3b6 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/DefaultInterfaceMethodCollisionInSubclassAfterClassMergingTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/DefaultInterfaceMethodCollisionInSubclassAfterClassMergingTest.java
@@ -53,6 +53,7 @@ runResult.assertFailureWithErrorThatThrows( parameters.isCfRuntime() && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11) + && parameters.getRuntime().asCf().isOlderThan(CfVm.JDK25) ? AbstractMethodError.class : IncompatibleClassChangeError.class), runResult -> runResult.assertSuccessWithOutputLines("A", "I", "J"));
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java index c0fa6fe..6814cf9 100644 --- a/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java +++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/interfaces/CollisionWithDefaultMethodOutsideMergeGroupLambdaTest.java
@@ -83,6 +83,7 @@ builder.assertFailureWithErrorThatThrows( parameters.isCfRuntime() && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11) + && parameters.getRuntime().asCf().isOlderThan(CfVm.JDK25) ? AbstractMethodError.class : IncompatibleClassChangeError.class), builder -> builder.assertSuccessWithOutputLines("K", "J"));
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideConflictWithLibrary2Test.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideConflictWithLibrary2Test.java index be7017e..69417de 100644 --- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideConflictWithLibrary2Test.java +++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/DefaultMethodOverrideConflictWithLibrary2Test.java
@@ -98,8 +98,10 @@ } private void checkResult(TestRunResult<?> result) { - if (parameters.isCfRuntime() && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) { - // TODO(b/145566657): For some reason JDK11+ throws AbstractMethodError. + if (parameters.isCfRuntime() + && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11) + && parameters.getRuntime().asCf().isOlderThan(CfVm.JDK25)) { + // For some reason [JDK11,JDK25[ throws AbstractMethodError (see b/145566657). result.assertFailureWithErrorThatThrows(AbstractMethodError.class); } else { result.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesIllegalSubclassMergedTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesIllegalSubclassMergedTest.java index 78dcfeb..3d7dd8f 100644 --- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesIllegalSubclassMergedTest.java +++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesIllegalSubclassMergedTest.java
@@ -32,7 +32,10 @@ @Parameter(0) public TestParameters parameters; - static final Matcher<String> EXPECTED = containsString("cannot inherit from sealed class"); + static final Matcher<String> EXPECTED_BEFORE_JDK25 = + containsString("cannot inherit from sealed class"); + static final Matcher<String> EXPECTED_FROM_JDK25 = + containsString("Failed listed permitted subclass check"); static final String EXPECTED_WITHOUT_PERMITTED_SUBCLASSES_ATTRIBUTE_OR_FIXED_ATTRIBUTE = StringUtils.lines("Sub1", "Sub2"); @@ -59,7 +62,10 @@ testForJvm(parameters) .apply(this::addTestClasses) .run(parameters.getRuntime(), TestClass.class) - .assertFailureWithErrorThatMatches(EXPECTED); + .assertFailureWithErrorThatMatches( + parameters.isCfRuntime() && parameters.asCfRuntime().isOlderThan(CfVm.JDK25) + ? EXPECTED_BEFORE_JDK25 + : EXPECTED_FROM_JDK25); } private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesIllegalSubclassTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesIllegalSubclassTest.java index 77d9de2..6343728 100644 --- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesIllegalSubclassTest.java +++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedClassesIllegalSubclassTest.java
@@ -40,7 +40,10 @@ @Parameter(1) public boolean keepPermittedSubclassesAttribute; - static final Matcher<String> EXPECTED = containsString("cannot inherit from sealed class"); + static final Matcher<String> EXPECTED_BEFORE_JDK25 = + containsString("cannot inherit from sealed class"); + static final Matcher<String> EXPECTED_FROM_JDK25 = + containsString("Failed listed permitted subclass check"); static final String EXPECTED_WITHOUT_PERMITTED_SUBCLASSES_ATTRIBUTE = StringUtils.lines("Sub1", "Sub2", "Sub3"); @@ -69,7 +72,10 @@ testForJvm(parameters) .apply(this::addTestClasses) .run(parameters.getRuntime(), TestClass.class) - .assertFailureWithErrorThatMatches(EXPECTED); + .assertFailureWithErrorThatMatches( + parameters.isCfRuntime() && parameters.asCfRuntime().isOlderThan(CfVm.JDK25) + ? EXPECTED_BEFORE_JDK25 + : EXPECTED_FROM_JDK25); } @Test @@ -81,8 +87,10 @@ .applyIf( DesugarTestConfiguration::isNotJavac, r -> r.assertSuccessWithOutput(EXPECTED_WITHOUT_PERMITTED_SUBCLASSES_ATTRIBUTE), + c -> parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK25), + r -> r.assertFailureWithErrorThatMatches(EXPECTED_FROM_JDK25), c -> parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK17), - r -> r.assertFailureWithErrorThatMatches(EXPECTED), + r -> r.assertFailureWithErrorThatMatches(EXPECTED_BEFORE_JDK25), r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class)); } @@ -125,8 +133,13 @@ && parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK17)), r -> r.assertSuccessWithOutput(EXPECTED_WITHOUT_PERMITTED_SUBCLASSES_ATTRIBUTE), parameters.isCfRuntime() + && parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK25) && keepPermittedSubclassesAttribute, - r -> r.assertFailureWithErrorThatMatches(EXPECTED), + r -> r.assertFailureWithErrorThatMatches(EXPECTED_FROM_JDK25), + parameters.isCfRuntime() + && parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK17) + && keepPermittedSubclassesAttribute, + r -> r.assertFailureWithErrorThatMatches(EXPECTED_BEFORE_JDK25), r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class)); }
diff --git a/src/test/java/com/android/tools/r8/graph/genericsignature/UnboundedFormalTypeGenericSignatureTest.java b/src/test/java/com/android/tools/r8/graph/genericsignature/UnboundedFormalTypeGenericSignatureTest.java index 23dc374..9bade28 100644 --- a/src/test/java/com/android/tools/r8/graph/genericsignature/UnboundedFormalTypeGenericSignatureTest.java +++ b/src/test/java/com/android/tools/r8/graph/genericsignature/UnboundedFormalTypeGenericSignatureTest.java
@@ -56,7 +56,7 @@ transformer(Super.class).removeInnerClasses().transform()) .run(parameters.getRuntime(), Main.class); if (parameters.isCfRuntime()) { - if (parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK24)) { + if (parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK25)) { runResult.assertSuccessWithOutputLines( "class java.lang.TypeNotPresentException::Type R not present", "R", @@ -91,7 +91,7 @@ transformer(Super.class).removeInnerClasses().transform()) .run(parameters.getRuntime(), Main.class); if (parameters.isCfRuntime()) { - if (parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK24)) { + if (parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK25)) { runResult.assertSuccessWithOutputLines( Super.class.getTypeName() + "<T>", "class java.lang.TypeNotPresentException::Type S not present",
diff --git a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge3Test.java b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge3Test.java index 9331e2c..f3cc39a 100644 --- a/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge3Test.java +++ b/src/test/java/com/android/tools/r8/graph/invokespecial/InvokeSpecialInterfaceWithBridge3Test.java
@@ -13,6 +13,7 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestParameters; import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.TestRuntime.CfVm; import com.android.tools.r8.ToolHelper.DexVm.Version; import com.android.tools.r8.utils.AndroidApiLevel; import com.android.tools.r8.utils.StringUtils; @@ -60,12 +61,18 @@ Version version = parameters.getRuntime().asDex().getVm().getVersion(); if (version.isOlderThanOrEqual(Version.V4_4_4)) { return VerifyError.class; - } - if (version.isNewerThanOrEqual(Version.V7_0_0)) { + } else if (version.isNewerThanOrEqual(Version.V7_0_0)) { return AbstractMethodError.class; + } else { + return IncompatibleClassChangeError.class; + } + } else { + if (parameters.getRuntime().asCf().getVm().isGreaterThanOrEqualTo(CfVm.JDK25)) { + return VerifyError.class; + } else { + return IncompatibleClassChangeError.class; } } - return IncompatibleClassChangeError.class; } @Test
diff --git a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java index e290f65..66362ce 100644 --- a/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java +++ b/src/test/java/com/android/tools/r8/internal/CompilationTestBase.java
@@ -120,8 +120,8 @@ ToolHelper.addProguardConfigurationConsumer( builder, pgConfig -> { - pgConfig.setPrintSeeds(false, null, null); - pgConfig.setIgnoreWarnings(true); + pgConfig.enablePrintSeeds(null, null, null, null); + pgConfig.setIgnoreWarnings(null, null); }); outputApp = new AndroidAppConsumers(builder); ToolHelper.runR8(builder.build(), optionsConsumer);
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/DoNotInlineConstructorWithKeptFinalFieldTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/DoNotInlineConstructorWithKeptFinalFieldTest.java new file mode 100644 index 0000000..99e7bb6 --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/DoNotInlineConstructorWithKeptFinalFieldTest.java
@@ -0,0 +1,83 @@ +// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file +// for details. 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.utils.codeinspector.Matchers.isFinal; +import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; +import static com.android.tools.r8.utils.codeinspector.Matchers.onlyIf; +import static junit.framework.TestCase.assertTrue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assume.assumeTrue; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.ToolHelper; +import com.android.tools.r8.utils.BooleanUtils; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.FieldSubject; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class DoNotInlineConstructorWithKeptFinalFieldTest extends TestBase { + + @Parameter(0) + public boolean keep; + + @Parameter(1) + public TestParameters parameters; + + @Parameters(name = "{1}, keep: {0}") + public static List<Object[]> data() { + return buildParameters( + BooleanUtils.values(), getTestParameters().withDexRuntimesAndAllApiLevels().build()); + } + + @Test + public void test() throws Exception { + assumeTrue(parameters.canUseJavaLangInvokeVarHandleStoreStoreFence()); + assertTrue(parameters.canInitNewInstanceUsingSuperclassConstructor()); + testForR8(parameters) + .addInnerClasses(getClass()) + // Use most recent android.jar so that VarHandle is present. + .applyIf( + parameters.isDexRuntime(), + testBuilder -> testBuilder.addLibraryFiles(ToolHelper.getMostRecentAndroidJar())) + .addKeepMainRule(Main.class) + .applyIf(keep, b -> b.addKeepRules("-keepclassmembers class * { final int f; }")) + .compile() + .inspect(this::inspect); + } + + private void inspect(CodeInspector inspector) { + // When the field is kept we should not change its modifiers, since the app may, for example, + // reflect on whether the final flag is set. + FieldSubject fieldSubject = inspector.clazz(Main.class).uniqueFieldWithOriginalName("f"); + assertThat(fieldSubject, isPresent()); + assertThat(fieldSubject, onlyIf(keep, isFinal())); + } + + static class Main { + + public static void main(String[] args) { + Main main = new Main(args.length); + System.out.println(main); + } + + final int f; + + Main(int f) { + this.f = f; + } + + @Override + public String toString() { + return Integer.toString(f); + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerNeverCompileTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerNeverCompileTest.java new file mode 100644 index 0000000..1d6a31d --- /dev/null +++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/exceptions/ThrowBlockOutlinerNeverCompileTest.java
@@ -0,0 +1,104 @@ +// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file +// for details. 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.outliner.exceptions; + +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.assertFalse; +import static org.junit.Assert.assertTrue; + +import com.android.tools.r8.SingleTestRunResult; +import com.android.tools.r8.TestCompilerBuilder; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.synthesis.SyntheticItemsTestUtils; +import com.android.tools.r8.utils.codeinspector.ClassSubject; +import com.android.tools.r8.utils.codeinspector.CodeInspector; +import com.android.tools.r8.utils.codeinspector.MethodSubject; +import java.util.Collection; +import org.junit.Test; + +public class ThrowBlockOutlinerNeverCompileTest extends ThrowBlockOutlinerTestBase { + + @Test + public void testD8() throws Exception { + runTest(testForD8(parameters), testForD8(parameters)); + } + + @Test + public void testR8() throws Exception { + assumeRelease(); + runTest( + testForR8(parameters).addKeepMainRule(Main.class).noInliningOfSynthetics(), + testForR8(parameters).addKeepMainRule(Main.class).noInliningOfSynthetics()); + } + + private void runTest( + TestCompilerBuilder<?, ?, ?, ? extends SingleTestRunResult<?>, ?> testBuilder, + TestCompilerBuilder<?, ?, ?, ? extends SingleTestRunResult<?>, ?> otherTestBuilder) + throws Exception { + long oatSize = + testBuilder + .addInnerClasses(getClass()) + .addOptionsModification( + options -> { + assertFalse(options.getThrowBlockOutlinerOptions().neverCompile); + }) + .apply(this::configure) + .compile() + .inspect(inspector -> inspectOutput(inspector, false)) + .runDex2Oat(parameters.getRuntime()) + .getOatSizeOrDefault(-1); + assertTrue(0 < oatSize); + + long oatSizeNeverCompile = + otherTestBuilder + .addInnerClasses(getClass()) + .addOptionsModification( + options -> { + assertFalse(options.getThrowBlockOutlinerOptions().neverCompile); + options.getThrowBlockOutlinerOptions().neverCompile = true; + }) + .apply(this::configure) + .compile() + .inspect(inspector -> inspectOutput(inspector, true)) + .runDex2Oat(parameters.getRuntime()) + .getOatSizeOrDefault(-1); + assertTrue(0 < oatSizeNeverCompile); + // TODO(b/434769547): Why is the @NeverCompile version not smaller? + assertEquals(oatSize, oatSizeNeverCompile); + } + + @Override + public void inspectOutlines(Collection<ThrowBlockOutline> outlines, DexItemFactory factory) { + // Intentionally empty. + } + + private void inspectOutput(CodeInspector inspector, boolean neverCompile) { + assertEquals(2, inspector.allClasses().size()); + + ClassSubject outlineClassSubject = + inspector.clazz(SyntheticItemsTestUtils.syntheticThrowBlockOutlineClass(Main.class, 0)); + assertThat(outlineClassSubject, isPresent()); + assertEquals(1, outlineClassSubject.allMethods().size()); + + MethodSubject outlineMethodSubject = outlineClassSubject.uniqueMethod(); + assertEquals(neverCompile ? 1 : 0, outlineMethodSubject.annotations().size()); + } + + @Override + public boolean shouldOutline(ThrowBlockOutline outline) { + return true; + } + + static class Main { + + public static void main(String[] args) { + int i = Integer.parseInt(args[0]); + if (i == 0) { + throw new IllegalArgumentException(); + } + } + } +}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsWithMissingAnnotationsTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsWithMissingAnnotationsTest.java index abe8855..a982c2d 100644 --- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsWithMissingAnnotationsTest.java +++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedAnnotatedArgumentsWithMissingAnnotationsTest.java
@@ -4,7 +4,6 @@ package com.android.tools.r8.ir.optimize.unusedarguments; -import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent; import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; @@ -45,10 +44,6 @@ } private void checkClass(ClassSubject clazz, String expectedAnnotationClass) { - if (parameters.canUseJavaLangInvokeVarHandleStoreStoreFence()) { - assertThat(clazz, isAbsent()); - return; - } assertThat(clazz, isPresent()); MethodSubject init = clazz.init("Test", "java.lang.String"); assertThat(init, isPresent());
diff --git a/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java b/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java index 42243c0..7fbff7b 100644 --- a/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java +++ b/src/test/java/com/android/tools/r8/naming/InterfaceRenamingTestRunner.java
@@ -78,7 +78,7 @@ ToolHelper.addProguardConfigurationConsumer( R8Command.builder(), pgConfig -> { - pgConfig.enablePrintMapping(null, null); + pgConfig.enablePrintMapping(null, null, null, null); if (!minify.isMinify()) { pgConfig.disableObfuscation(); }
diff --git a/src/test/java/com/android/tools/r8/naming/ProgramTypeRenamedAsClasspathTypeTest.java b/src/test/java/com/android/tools/r8/naming/ProgramTypeRenamedAsClasspathTypeTest.java index 1ba9022..4be52fb 100644 --- a/src/test/java/com/android/tools/r8/naming/ProgramTypeRenamedAsClasspathTypeTest.java +++ b/src/test/java/com/android/tools/r8/naming/ProgramTypeRenamedAsClasspathTypeTest.java
@@ -31,7 +31,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK24) + .withCfRuntimesStartingFromIncluding(CfVm.JDK25) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .withPartialCompilation()
diff --git a/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java b/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java index 1067960..8f6fb1e 100644 --- a/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java +++ b/src/test/java/com/android/tools/r8/naming/RenameSourceFileDebugTest.java
@@ -48,8 +48,8 @@ ToolHelper.addProguardConfigurationConsumer( R8Command.builder(), pgConfig -> { - pgConfig.addRule(ProguardKeepRule.defaultKeepAllRule(unused -> {})); - pgConfig.setRenameSourceFileAttribute(TEST_FILE, null, null); + pgConfig.addRule(ProguardKeepRule.defaultKeepAllRule(unused -> {}), null, null); + pgConfig.setRenameSourceFileAttribute(TEST_FILE, null, null, null); pgConfig.addKeepAttributePatterns( ImmutableList.of("SourceFile", "LineNumberTable")); })
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java index 2f684c0..6ccaba2 100644 --- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java +++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
@@ -48,8 +48,8 @@ ToolHelper.addProguardConfigurationConsumer( ToolHelper.prepareR8CommandBuilder(app), pgConfig -> { - pgConfig.enablePrintMapping(null, null); - pgConfig.setPrintMappingFile(out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE)); + Path printMappingFile = out.resolve(ToolHelper.DEFAULT_PROGUARD_MAP_FILE); + pgConfig.enablePrintMapping(printMappingFile, null, null, null); }) .addProguardConfiguration( ImmutableList.of(
diff --git a/src/test/java/com/android/tools/r8/naming/whyareyounotobfuscating/WhyAreYouNotObfuscatingClassTest.java b/src/test/java/com/android/tools/r8/naming/whyareyounotobfuscating/WhyAreYouNotObfuscatingClassTest.java new file mode 100644 index 0000000..489c552 --- /dev/null +++ b/src/test/java/com/android/tools/r8/naming/whyareyounotobfuscating/WhyAreYouNotObfuscatingClassTest.java
@@ -0,0 +1,53 @@ +// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. +package com.android.tools.r8.naming.whyareyounotobfuscating; + +import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; +import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType; +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.containsString; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.utils.StringDiagnostic; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class WhyAreYouNotObfuscatingClassTest extends TestBase { + + @Parameter(0) + public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withDefaultDexRuntime().withMaximumApiLevel().build(); + } + + @Test + public void test() throws Exception { + testForR8(parameters) + .addInnerClasses(getClass()) + .addKeepMainRule(Main.class) + .addKeepRules("-whyareyounotobfuscating class " + Main.class.getTypeName()) + .allowDiagnosticWarningMessages() + .compileWithExpectedDiagnostics( + diagnostics -> + diagnostics.assertWarningsMatch( + allOf( + diagnosticType(StringDiagnostic.class), + diagnosticMessage( + containsString( + Main.class.getTypeName() + " is not obfuscated due to -keep"))))); + } + + static class Main { + + public static void main(String[] args) {} + } +}
diff --git a/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesCommandTest.java b/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesCommandTest.java index 1807513..f784755 100644 --- a/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesCommandTest.java +++ b/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesCommandTest.java
@@ -6,7 +6,6 @@ import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage; import static com.android.tools.r8.DiagnosticsMatcher.diagnosticOrigin; import static com.android.tools.r8.DiagnosticsMatcher.diagnosticType; -import static com.android.tools.r8.OriginMatcher.hasPart; import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.fail; @@ -15,18 +14,19 @@ import com.android.tools.r8.TestBase; import com.android.tools.r8.TestDiagnosticMessagesImpl; import com.android.tools.r8.TestParameters; -import com.google.common.collect.ImmutableList; +import com.android.tools.r8.origin.Origin; +import com.android.tools.r8.origin.PathOrigin; import com.google.common.collect.ImmutableMap; import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; +import java.nio.file.Paths; import java.util.List; import java.util.Map; +import java.util.function.Consumer; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class ProcessKeepRulesCommandTest extends TestBase { @@ -37,12 +37,13 @@ .put("-dontobfuscate", "-dontobfuscate not allowed in library consumer rules.") .put("-dontshrink", "-dontshrink not allowed in library consumer rules.") .put("-repackageclasses", "-repackageclasses not allowed in library consumer rules.") - .put("-printconfiguration", "-printconfiguration not allowed in library consumer rules.") - .put("-printmapping", "-printmapping not allowed in library consumer rules.") .put("-applymapping foo", "-applymapping not allowed in library consumer rules.") .put("-injars foo", "-injars not allowed in library consumer rules.") .put("-libraryjars foo", "-libraryjars not allowed in library consumer rules.") + .put("-printconfiguration", "-printconfiguration not allowed in library consumer rules.") + .put("-printmapping", "-printmapping not allowed in library consumer rules.") .put("-printseeds", "-printseeds not allowed in library consumer rules.") + .put("-printusage", "-printusage not allowed in library consumer rules.") .put( "-obfuscationdictionary foo", "-obfuscationdictionary not allowed in library consumer rules.") @@ -77,8 +78,23 @@ "-keepattributes SourceFile", "Illegal attempt to keep the attribute 'SourceFile' in library consumer rules.") .put( + "-maximumremovedandroidloglevel 2", + "-maximumremovedandroidloglevel <int> not allowed in library consumer rules.") + .put( "-renamesourcefileattribute", "-renamesourcefileattribute not allowed in library consumer rules.") + .put( + "-shrinkunusedprotofields", + "-shrinkunusedprotofields not allowed in library consumer rules.") + .put( + "-whyareyoukeeping class *", + "-whyareyoukeeping not allowed in library consumer rules.") + .put( + "-whyareyounotobfuscating class *", + "-whyareyounotobfuscating not allowed in library consumer rules.") + .put( + "-whyareyounotinlining class * { *; }", + "-whyareyounotinlining not allowed in library consumer rules.") .build(); @Parameter(1) @@ -87,32 +103,64 @@ @Parameter(0) public Map.Entry<String, String> configAndExpectedDiagnostic; - @Parameterized.Parameters(name = "{1}, configAndExpectedDiagnostic = {0}") + @Parameters(name = "{1}, configAndExpectedDiagnostic = {0}") public static List<Object[]> data() throws IOException { return buildParameters(testRules.entrySet(), getTestParameters().withNoneRuntime().build()); } @Test public void test() throws Exception { - TestDiagnosticMessagesImpl diagnostics = new TestDiagnosticMessagesImpl(); - Path tempFile = getStaticTemp().newFile().toPath(); - Files.write(tempFile, configAndExpectedDiagnostic.getKey().getBytes(StandardCharsets.UTF_8)); - ProcessKeepRulesCommand command = - ProcessKeepRulesCommand.builder(diagnostics) - .addKeepRuleFiles(ImmutableList.of(tempFile)) - .setLibraryConsumerRuleValidation(true) - .build(); + String rules = configAndExpectedDiagnostic.getKey(); + Origin origin = new PathOrigin(Paths.get("keep.txt")); try { - ProcessKeepRules.run(command); + validate( + rules, + origin, + diagnostics -> + diagnostics.assertErrorsMatch( + allOf( + rules.startsWith("-keepattributes") + ? diagnosticType(KeepAttributeLibraryConsumerRuleDiagnostic.class) + : diagnosticType(LibraryConsumerRuleDiagnostic.class), + diagnosticOrigin(equalTo(origin)), + diagnosticMessage(equalTo(configAndExpectedDiagnostic.getValue()))))); fail("Expect the compilation to fail."); } catch (CompilationFailedException e) { - diagnostics.assertErrorsMatch( - allOf( - configAndExpectedDiagnostic.getKey().startsWith("-keepattributes") - ? diagnosticType(KeepAttributeLibraryConsumerRuleDiagnostic.class) - : diagnosticType(GlobalLibraryConsumerRuleDiagnostic.class), - diagnosticOrigin(hasPart(tempFile.toString())), - diagnosticMessage(equalTo(configAndExpectedDiagnostic.getValue())))); + // Expected. + } + + // Rerun validation after filtering. This should succeed without diagnostics. + validate(filter(rules, origin), origin, TestDiagnosticMessagesImpl::assertNoMessages); + } + + private String filter(String rules, Origin origin) throws CompilationFailedException { + TestDiagnosticMessagesImpl diagnostics = new TestDiagnosticMessagesImpl(); + StringBuilder result = new StringBuilder(); + ProcessKeepRulesCommand command = + ProcessKeepRulesCommand.builder(diagnostics) + .addKeepRules(rules, origin) + .setFilteredKeepRulesConsumer((s, h) -> result.append(s)) + .build(); + ProcessKeepRules.run(command); + diagnostics.assertNoMessages(); + return result.toString(); + } + + private void validate( + String rules, Origin origin, Consumer<TestDiagnosticMessagesImpl> diagnosticsInspector) + throws CompilationFailedException { + TestDiagnosticMessagesImpl diagnostics = new TestDiagnosticMessagesImpl(); + try { + ProcessKeepRulesCommand command = + ProcessKeepRulesCommand.builder(diagnostics) + .addKeepRules(rules, origin) + .setLibraryConsumerRuleValidation(true) + .build(); + ProcessKeepRules.run(command); + diagnosticsInspector.accept(diagnostics); + } catch (CompilationFailedException e) { + diagnosticsInspector.accept(diagnostics); + throw e; } } }
diff --git a/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesFilteringTest.java b/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesFilteringTest.java index d834658..bd1cde6 100644 --- a/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesFilteringTest.java +++ b/src/test/java/com/android/tools/r8/processkeeprules/ProcessKeepRulesFilteringTest.java
@@ -36,10 +36,13 @@ String keepRules = StringUtils.unixLines( " -dontobfuscate -dontoptimize -dontshrink -keep class com.example.MainActivity # keep", - "# Keep all attributes", - "-keepattributes *", + "# Keep all annotations", + "-keepattributes AnnotationDefault,*Annotations*", "# Keep all", "-keep class **", + "# Multi line repackageclasses", + "-repackageclasses", + " com.example.internal", "# End"); FilteredKeepRules filteredKeepRules = new FilteredKeepRules(); ProcessKeepRulesCommand command = @@ -52,10 +55,15 @@ StringUtils.unixLines( " #-dontobfuscate -dontoptimize -dontshrink ", "-keep class com.example.MainActivity # keep", - "# Keep all attributes", - "-keepattributes *", + "# Keep all annotations", + "#-keepattributes AnnotationDefault,*Annotations*", + "-keepattributes" + + " AnnotationDefault,RuntimeVisibleAnnotations,RuntimeVisibleParameterAnnotations,RuntimeVisibleTypeAnnotations", "# Keep all", "-keep class **", + "# Multi line repackageclasses", + "#-repackageclasses", + "# com.example.internal", "# End"), filteredKeepRules.get()); diagnostics.assertNoMessages();
diff --git a/src/test/java/com/android/tools/r8/resolution/SingleResolutionWithFailingDispatchTest.java b/src/test/java/com/android/tools/r8/resolution/SingleResolutionWithFailingDispatchTest.java index 7227684..649069b 100644 --- a/src/test/java/com/android/tools/r8/resolution/SingleResolutionWithFailingDispatchTest.java +++ b/src/test/java/com/android/tools/r8/resolution/SingleResolutionWithFailingDispatchTest.java
@@ -49,7 +49,9 @@ } private void inspectRunResult(TestRunResult<?> runResult) { - if (parameters.isCfRuntime() && parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK11)) { + if (parameters.isCfRuntime() + && parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK11) + && parameters.asCfRuntime().isOlderThan(CfVm.JDK25)) { runResult.assertFailureWithErrorThatThrows(AbstractMethodError.class); } else { runResult.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
diff --git a/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleMaximallySpecificTest.java b/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleMaximallySpecificTest.java index 082d51b..5770963 100644 --- a/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleMaximallySpecificTest.java +++ b/src/test/java/com/android/tools/r8/resolution/interfacetargets/MultipleMaximallySpecificTest.java
@@ -43,8 +43,10 @@ @RunWith(Parameterized.class) public class MultipleMaximallySpecificTest extends TestBase { - private static final String EXPECTED_UPTO_JDK9 = StringUtils.lines("A.foo", "Got ICCE"); - private static final String EXPECTED_AFTER_JDK9 = StringUtils.lines("A.foo", "Got AME"); + private static final String EXPECTED_UPTO_JDK9_AND_AFTER_25 = + StringUtils.lines("A.foo", "Got ICCE"); + private static final String EXPECTED_AFTER_JDK9_AND_BEFORE_25_AND_ART = + StringUtils.lines("A.foo", "Got AME"); private final TestParameters parameters; @@ -118,22 +120,18 @@ } } - private boolean isDesugaring() { - return parameters.isDexRuntime() - && parameters.getApiLevel().isLessThan(apiLevelWithDefaultInterfaceMethodsSupport()); - } - - private boolean isNewCfRuntime() { - return parameters.isCfRuntime() && parameters.asCfRuntime().isNewerThan(CfVm.JDK9); - } - @Test public void testRuntime() throws Exception { testForRuntime(parameters) .addProgramClasses(getInputClasses()) .addProgramClassFileData(getTransformedClasses()) .run(parameters.getRuntime(), Main.class) - .assertSuccessWithOutput(isNewCfRuntime() ? EXPECTED_AFTER_JDK9 : EXPECTED_UPTO_JDK9); + .assertSuccessWithOutput( + parameters.isCfRuntime() + && parameters.asCfRuntime().isNewerThan(CfVm.JDK9) + && parameters.asCfRuntime().isOlderThan(CfVm.JDK25) + ? EXPECTED_AFTER_JDK9_AND_BEFORE_25_AND_ART + : EXPECTED_UPTO_JDK9_AND_AFTER_25); } @Test
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintConfigurationTest.java b/src/test/java/com/android/tools/r8/shaking/PrintConfigurationTest.java index 9dd9eb6..075edcd 100644 --- a/src/test/java/com/android/tools/r8/shaking/PrintConfigurationTest.java +++ b/src/test/java/com/android/tools/r8/shaking/PrintConfigurationTest.java
@@ -130,29 +130,30 @@ containsString( "-printconfiguration " + (ToolHelper.isWindows() - ? ("'" + proguardConfigOutFile.toAbsolutePath().toString() + "'") + ? ("'" + proguardConfigOutFile.toAbsolutePath() + "'") : proguardConfigOutFile.toAbsolutePath().toString()))); } @Test public void testIncludeFile() throws Exception { - Class mainClass = PrintConfigurationTestClass.class; + Class<?> mainClass = PrintConfigurationTestClass.class; String includeProguardConfig = keepMainProguardConfiguration(mainClass); Path includeFile = temp.newFile().toPath(); FileUtils.writeTextFile(includeFile, includeProguardConfig); Path printConfigurationFile = temp.newFile().toPath(); - String proguardConfig = String.join(System.lineSeparator(), ImmutableList.of( - "-include " + includeFile.toString(), - "-printconfiguration " + printConfigurationFile.toString() - )); + String proguardConfig = + String.join( + System.lineSeparator(), + ImmutableList.of( + "-include " + includeFile, "-printconfiguration " + printConfigurationFile)); - String expected = String.join(System.lineSeparator(), ImmutableList.of( - "", // The -include line turns into an empty line. - includeProguardConfig, - "", // Writing to the file adds an ending line separator - "", // An empty line is emitted between two parts - "-printconfiguration " + printConfigurationFile.toString() - )); + String expected = + StringUtils.joinLines( + "-printconfiguration " + printConfigurationFile, + "", // The -include line turns into an empty line. + includeProguardConfig, + "", // Writing to the file adds an ending line separator + ""); // An empty line is emitted between two parts compileWithR8(ImmutableList.of(mainClass), proguardConfig); assertEqualsStripOrigin( expected, FileUtils.readTextFile(printConfigurationFile, Charsets.UTF_8));
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java index cb788dc..5d1c257 100644 --- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java +++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -184,7 +184,6 @@ reporter, ProguardConfigurationParserOptions.builder() .setEnableLegacyFullModeForKeepRules(false) - .setEnableExperimentalCheckEnumUnboxed(false) .setEnableTestingOptions(false) .build(), null, @@ -202,7 +201,6 @@ dexItemFactory, reporter, ProguardConfigurationParserOptions.builder() - .setEnableExperimentalCheckEnumUnboxed(false) .setEnableTestingOptions(true) .build(), null, @@ -723,7 +721,6 @@ dexItemFactory, reporter, ProguardConfigurationParserOptions.builder() - .setEnableExperimentalCheckEnumUnboxed(false) .setEnableTestingOptions(false) .build(), null, @@ -743,7 +740,6 @@ dexItemFactory, reporter, ProguardConfigurationParserOptions.builder() - .setEnableExperimentalCheckEnumUnboxed(false) .setEnableTestingOptions(false) .build(), null, @@ -861,7 +857,7 @@ verifyParserEndsCleanly(); ProguardConfiguration config = builder.build(); assertEquals( - "-keepattributes RuntimeVisibleAnnotations,RuntimeInvisibleAnnotations", + "-keepattributes RuntimeInvisibleAnnotations,RuntimeVisibleAnnotations", config.getKeepAttributes().toString()); assertEquals( StringUtils.joinLines("-keep class kotlin.Metadata {", " *;", "}"), @@ -881,7 +877,7 @@ parser.parse(path); fail(); } catch (RuntimeException e) { - checkDiagnostics(handler.errors, path, 6, 10,"does-not-exist.flags"); + checkDiagnostics(handler.errors, path, 6, 1, "does-not-exist.flags"); } } @@ -892,7 +888,7 @@ parser.parse(path); fail(); } catch (RuntimeException e) { - checkDiagnostics(handler.errors, path, 6,2, "does-not-exist.flags"); + checkDiagnostics(handler.errors, path, 6, 1, "does-not-exist.flags"); } } @@ -3023,4 +3019,48 @@ assertEquals(MaximumRemovedAndroidLogLevelRule.VERBOSE, rule.getMaxRemovedAndroidLogLevel()); } } + + @Test + public void testParsedConfigurationWithInclude() throws Exception { + Path config = temp.newFile("config.txt").toPath().toAbsolutePath(); + Path include1 = temp.newFile("include1.txt").toPath().toAbsolutePath(); + Path include11 = temp.newFile("include1_1.txt").toPath().toAbsolutePath(); + Path include2 = temp.newFile("include2.txt").toPath().toAbsolutePath(); + FileUtils.writeTextFile( + config, + StringUtils.joinLines( + "# Before Include 1", + "-include " + include1, + "# After Include 1", + "-include " + include2, + "# After Include 2")); + FileUtils.writeTextFile( + include1, StringUtils.joinLines("# Include 1", "-include " + include11)); + FileUtils.writeTextFile(include11, "# Include 1.1"); + FileUtils.writeTextFile(include2, "# Include 2"); + parser.parse(config); + verifyParserEndsCleanly(); + String parsedConfiguration = builder.build().getParsedConfiguration(); + String separator = ToolHelper.isWindows() ? "\\" : "/"; + assertEquals( + StringUtils.lines( + "# The proguard configuration file for the following section is config.txt", + "# Before Include 1", + "", // -include include1.txt + "# After Include 1", + "", // -include include2.txt + "# After Include 2", + "# End of content from config.txt", + "# The proguard configuration file for the following section is include1.txt", + "# Include 1", + "", // -include include1_1.txt. + "# End of content from include1.txt", + "# The proguard configuration file for the following section is include1_1.txt", + "# Include 1.1", + "# End of content from include1_1.txt", + "# The proguard configuration file for the following section is include2.txt", + "# Include 2", + "# End of content from include2.txt"), + StringUtils.replaceAll(parsedConfiguration, temp.getRoot().toString() + separator, "")); + } } \ No newline at end of file
diff --git a/src/test/java17/com/android/tools/r8/jdk17/records/RecordComponentAnnotationsTest.java b/src/test/java17/com/android/tools/r8/jdk17/records/RecordComponentAnnotationsTest.java index 17666e8..524207d 100644 --- a/src/test/java17/com/android/tools/r8/jdk17/records/RecordComponentAnnotationsTest.java +++ b/src/test/java17/com/android/tools/r8/jdk17/records/RecordComponentAnnotationsTest.java
@@ -222,7 +222,8 @@ .addInnerClassesAndStrippedOuter(getClass()) .run(parameters.getRuntime(), RecordWithAnnotations.class) .assertSuccessWithOutput( - parameters.getRuntime().asCf().getVm().isLessThanOrEqualTo(CfVm.JDK20) + // Result changed from JDK-20 to JDK-21, but we only test LTS, so check for JDK-17 here. + parameters.getRuntime().asCf().getVm().isLessThanOrEqualTo(CfVm.JDK17) ? JVM_UNTIL_20_EXPECTED_RESULT : JVM_FROM_21_EXPECTED_RESULT); }
diff --git a/src/test/java21/com/android/tools/r8/jdk21/assistant/JavaLangClass21JsonTest.java b/src/test/java21/com/android/tools/r8/jdk21/assistant/JavaLangClass21JsonTest.java new file mode 100644 index 0000000..3f682f7 --- /dev/null +++ b/src/test/java21/com/android/tools/r8/jdk21/assistant/JavaLangClass21JsonTest.java
@@ -0,0 +1,88 @@ +// Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file +// for details. 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.jdk21.assistant; + +import static org.junit.Assert.assertEquals; + +import com.android.tools.r8.TestBase; +import com.android.tools.r8.TestParameters; +import com.android.tools.r8.TestParametersCollection; +import com.android.tools.r8.assistant.postprocessing.ReflectiveOperationJsonParser; +import com.android.tools.r8.assistant.postprocessing.model.ClassFlagEvent; +import com.android.tools.r8.assistant.postprocessing.model.ReflectiveEvent; +import com.android.tools.r8.assistant.runtime.ReflectiveEventType; +import com.android.tools.r8.assistant.runtime.ReflectiveOperationJsonLogger; +import com.android.tools.r8.assistant.runtime.ReflectiveOperationReceiver.ClassFlag; +import com.android.tools.r8.graph.DexItemFactory; +import com.android.tools.r8.jdk21.assistant.JavaLangTestClass21.Foo; +import com.android.tools.r8.utils.Box; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class JavaLangClass21JsonTest extends TestBase { + + @Parameter(0) + public TestParameters parameters; + + @Parameters(name = "{0}") + public static TestParametersCollection data() { + return getTestParameters().withNativeMultidexDexRuntimes().withMaximumApiLevel().build(); + } + + @Test + public void testInstrumentationWithCustomOracle() throws Exception { + Path path = Paths.get(temp.newFile().getAbsolutePath()); + Box<DexItemFactory> factoryBox = new Box<>(); + testForAssistant() + .addProgramClasses(JavaLangTestClass21.class, Foo.class) + .addStrippedOuter(getClass()) + .addInstrumentationClasses(Instrumentation.class) + .setCustomReflectiveOperationReceiver(Instrumentation.class) + .setMinApi(parameters) + .addOptionsModification(opt -> factoryBox.set(opt.itemFactory)) + .compile() + .addVmArguments("-Dcom.android.tools.r8.reflectiveJsonLogger=" + path) + .run(parameters.getRuntime(), JavaLangTestClass21.class) + .assertSuccess(); + List<ReflectiveEvent> reflectiveEvents = + new ReflectiveOperationJsonParser(factoryBox.get()).parse(path); + assertEquals(14, reflectiveEvents.size()); + + ClassFlag[] expectedClassFlags = { + ClassFlag.ANNOTATION, + ClassFlag.ANONYMOUS_CLASS, + ClassFlag.ARRAY, + ClassFlag.ENUM, + ClassFlag.HIDDEN, + ClassFlag.INTERFACE, + ClassFlag.LOCAL_CLASS, + ClassFlag.MEMBER_CLASS, + ClassFlag.PRIMITIVE, + ClassFlag.RECORD, + ClassFlag.SEALED, + ClassFlag.SYNTHETIC + }; + + for (int i = 2; i < reflectiveEvents.size(); i++) { + ReflectiveEvent event = reflectiveEvents.get(i); + ClassFlagEvent classFlagEvent = event.asClassFlagEvent(); + assertEquals(ReflectiveEventType.CLASS_FLAG, classFlagEvent.getEventType()); + assertEquals(expectedClassFlags[i - 2], classFlagEvent.getClassFlag()); + } + } + + public static class Instrumentation extends ReflectiveOperationJsonLogger { + + public Instrumentation() throws IOException {} + } +}
diff --git a/src/test/java21/com/android/tools/r8/jdk21/jdk8272564/Jdk8272564Test.java b/src/test/java21/com/android/tools/r8/jdk21/jdk8272564/Jdk8272564Test.java index 55b4111..4d20227 100644 --- a/src/test/java21/com/android/tools/r8/jdk21/jdk8272564/Jdk8272564Test.java +++ b/src/test/java21/com/android/tools/r8/jdk21/jdk8272564/Jdk8272564Test.java
@@ -30,7 +30,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK20) + .withCfRuntimesStartingFromIncluding(CfVm.JDK21) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .withPartialCompilation()
diff --git a/src/test/java24/com/android/tools/r8/jdk24/Java24ValidationTest.java b/src/test/java25/com/android/tools/r8/jdk25/Java25ValidationTest.java similarity index 90% rename from src/test/java24/com/android/tools/r8/jdk24/Java24ValidationTest.java rename to src/test/java25/com/android/tools/r8/jdk25/Java25ValidationTest.java index 05ef994..65ac630 100644 --- a/src/test/java24/com/android/tools/r8/jdk24/Java24ValidationTest.java +++ b/src/test/java25/com/android/tools/r8/jdk25/Java25ValidationTest.java
@@ -2,7 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -package com.android.tools.r8.jdk24; +package com.android.tools.r8.jdk25; import static com.android.tools.r8.utils.InternalOptions.ASM_VERSION; import static junit.framework.TestCase.assertEquals; @@ -24,9 +24,9 @@ import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; -// Test to validate that the tests_java_23 module is built with JDK-23. +// Test to validate that the tests_java_25 module is built with JDK-25. @RunWith(Parameterized.class) -public class Java24ValidationTest extends TestBase { +public class Java25ValidationTest extends TestBase { static final String EXPECTED = StringUtils.lines("Hello, world"); @@ -37,7 +37,7 @@ return getTestParameters().withCfRuntimes().build(); } - public Java24ValidationTest(TestParameters parameters) { + public Java25ValidationTest(TestParameters parameters) { this.parameters = parameters; } @@ -88,7 +88,7 @@ .addInnerClasses(getClass()) .run(parameters.getRuntime(), TestClass.class) .applyIf( - parameters.getCfRuntime().isOlderThan(CfVm.JDK24), + parameters.getCfRuntime().isOlderThan(CfVm.JDK25), r -> r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class), r -> r.assertSuccessWithOutput(EXPECTED)); }
diff --git a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/DexNumberValueSwitchTest.java b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/DexNumberValueSwitchTest.java similarity index 97% rename from src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/DexNumberValueSwitchTest.java rename to src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/DexNumberValueSwitchTest.java index 9221d84..9f9d2c1 100644 --- a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/DexNumberValueSwitchTest.java +++ b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/DexNumberValueSwitchTest.java
@@ -2,7 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -package com.android.tools.r8.jdk24.switchpatternmatching; +package com.android.tools.r8.jdk25.switchpatternmatching; import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch; import static org.junit.Assert.assertTrue; @@ -30,7 +30,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK24) + .withCfRuntimesStartingFromIncluding(CfVm.JDK25) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .withPartialCompilation()
diff --git a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/DexValueSwitchTest.java b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/DexValueSwitchTest.java similarity index 98% rename from src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/DexValueSwitchTest.java rename to src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/DexValueSwitchTest.java index 0bb2ee9..633b792 100644 --- a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/DexValueSwitchTest.java +++ b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/DexValueSwitchTest.java
@@ -2,7 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -package com.android.tools.r8.jdk24.switchpatternmatching; +package com.android.tools.r8.jdk25.switchpatternmatching; import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch; import static org.junit.Assert.assertTrue; @@ -30,7 +30,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK24) + .withCfRuntimesStartingFromIncluding(CfVm.JDK25) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .withPartialCompilation()
diff --git a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java similarity index 97% rename from src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java rename to src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java index e1f549a..b24f759 100644 --- a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java +++ b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumLessCasesAtRuntimeSwitchTest.java
@@ -1,7 +1,7 @@ // Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -package com.android.tools.r8.jdk24.switchpatternmatching; +package com.android.tools.r8.jdk25.switchpatternmatching; import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21EnumSwitch; import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch; @@ -34,7 +34,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK24) + .withCfRuntimesStartingFromIncluding(CfVm.JDK25) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .withPartialCompilation()
diff --git a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java similarity index 98% rename from src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java rename to src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java index 980d8d4..578bc5f 100644 --- a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java +++ b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumMoreCasesAtRuntimeSwitchTest.java
@@ -1,7 +1,7 @@ // Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -package com.android.tools.r8.jdk24.switchpatternmatching; +package com.android.tools.r8.jdk25.switchpatternmatching; import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.desugarMatchException; import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21EnumSwitch; @@ -36,7 +36,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK24) + .withCfRuntimesStartingFromIncluding(CfVm.JDK25) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .withPartialCompilation()
diff --git a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumSwitchOldSyntaxTest.java b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumSwitchOldSyntaxTest.java similarity index 96% rename from src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumSwitchOldSyntaxTest.java rename to src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumSwitchOldSyntaxTest.java index 5774937..95afb4f 100644 --- a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumSwitchOldSyntaxTest.java +++ b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumSwitchOldSyntaxTest.java
@@ -1,7 +1,7 @@ // Copyright (c) 2025 the R8 project authors. Please see the AUTHORS file // for details. 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.jdk24.switchpatternmatching; +package com.android.tools.r8.jdk25.switchpatternmatching; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -27,7 +27,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK24) + .withCfRuntimesStartingFromIncluding(CfVm.JDK25) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .withPartialCompilation()
diff --git a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumSwitchOldSyntaxV2Test.java b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumSwitchOldSyntaxV2Test.java similarity index 96% rename from src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumSwitchOldSyntaxV2Test.java rename to src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumSwitchOldSyntaxV2Test.java index fede4cb..30fc275 100644 --- a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumSwitchOldSyntaxV2Test.java +++ b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumSwitchOldSyntaxV2Test.java
@@ -1,7 +1,7 @@ // Copyright (c) 2025 the R8 project authors. Please see the AUTHORS file // for details. 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.jdk24.switchpatternmatching; +package com.android.tools.r8.jdk25.switchpatternmatching; import static org.junit.Assert.assertEquals; @@ -26,7 +26,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK24) + .withCfRuntimesStartingFromIncluding(CfVm.JDK25) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .withPartialCompilation()
diff --git a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumSwitchTest.java b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumSwitchTest.java similarity index 96% rename from src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumSwitchTest.java rename to src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumSwitchTest.java index 8ba7a03..b2063b7 100644 --- a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumSwitchTest.java +++ b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumSwitchTest.java
@@ -1,7 +1,7 @@ // Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -package com.android.tools.r8.jdk24.switchpatternmatching; +package com.android.tools.r8.jdk25.switchpatternmatching; import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.desugarMatchException; import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch; @@ -35,7 +35,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK24) + .withCfRuntimesStartingFromIncluding(CfVm.JDK25) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .withPartialCompilation() @@ -61,7 +61,7 @@ private <T extends TestBuilder<?, T>> void addModifiedProgramClasses( TestBuilder<?, T> testBuilder) throws Exception { - String d = "com/android/tools/r8/jdk24/switchpatternmatching/EnumSwitchTest$D"; + String d = "com/android/tools/r8/jdk25/switchpatternmatching/EnumSwitchTest$D"; testBuilder .addStrippedOuter(getClass()) .addProgramClasses(FakeI.class, E.class, C.class)
diff --git a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethodTest.java b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethodTest.java similarity index 97% rename from src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethodTest.java rename to src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethodTest.java index 6e1e2b0..4a30ba3 100644 --- a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethodTest.java +++ b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/EnumSwitchUsingEnumSwitchBootstrapMethodTest.java
@@ -1,7 +1,7 @@ // Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -package com.android.tools.r8.jdk24.switchpatternmatching; +package com.android.tools.r8.jdk25.switchpatternmatching; import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21EnumSwitch; import static org.junit.Assert.assertTrue; @@ -32,7 +32,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK24) + .withCfRuntimesStartingFromIncluding(CfVm.JDK25) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .withPartialCompilation()
diff --git a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/StringSwitchOldSyntaxTest.java b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/StringSwitchOldSyntaxTest.java similarity index 95% rename from src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/StringSwitchOldSyntaxTest.java rename to src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/StringSwitchOldSyntaxTest.java index b83c3e5..c64ea5e 100644 --- a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/StringSwitchOldSyntaxTest.java +++ b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/StringSwitchOldSyntaxTest.java
@@ -1,7 +1,7 @@ // Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file // for details. 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.jdk24.switchpatternmatching; +package com.android.tools.r8.jdk25.switchpatternmatching; import com.android.tools.r8.JdkClassFileProvider; import com.android.tools.r8.TestBase; @@ -23,7 +23,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK24) + .withCfRuntimesStartingFromIncluding(CfVm.JDK25) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .withPartialCompilation()
diff --git a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/StringSwitchRegress382880986Test.java b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/StringSwitchRegress382880986Test.java similarity index 96% rename from src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/StringSwitchRegress382880986Test.java rename to src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/StringSwitchRegress382880986Test.java index eb9f60b..6ad8e58 100644 --- a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/StringSwitchRegress382880986Test.java +++ b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/StringSwitchRegress382880986Test.java
@@ -1,7 +1,7 @@ // Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -package com.android.tools.r8.jdk24.switchpatternmatching; +package com.android.tools.r8.jdk25.switchpatternmatching; import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch; import static org.junit.Assert.assertTrue; @@ -29,7 +29,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK24) + .withCfRuntimesStartingFromIncluding(CfVm.JDK25) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .withPartialCompilation()
diff --git a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/StringSwitchTest.java b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/StringSwitchTest.java similarity index 96% rename from src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/StringSwitchTest.java rename to src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/StringSwitchTest.java index 3387976..219391c 100644 --- a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/StringSwitchTest.java +++ b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/StringSwitchTest.java
@@ -1,7 +1,7 @@ // Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -package com.android.tools.r8.jdk24.switchpatternmatching; +package com.android.tools.r8.jdk25.switchpatternmatching; import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch; import static org.junit.Assert.assertTrue; @@ -32,7 +32,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK24) + .withCfRuntimesStartingFromIncluding(CfVm.JDK25) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .withPartialCompilation()
diff --git a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/TypeSwitchEnumAsClassTest.java b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/TypeSwitchEnumAsClassTest.java similarity index 96% rename from src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/TypeSwitchEnumAsClassTest.java rename to src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/TypeSwitchEnumAsClassTest.java index 1e99ca6..df14fe2 100644 --- a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/TypeSwitchEnumAsClassTest.java +++ b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/TypeSwitchEnumAsClassTest.java
@@ -1,7 +1,7 @@ // Copyright (c) 2025, the R8 project authors. Please see the AUTHORS file // for details. 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.jdk24.switchpatternmatching; +package com.android.tools.r8.jdk25.switchpatternmatching; import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch; import static org.junit.Assert.assertTrue; @@ -32,7 +32,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK24) + .withCfRuntimesStartingFromIncluding(CfVm.JDK25) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .withPartialCompilation()
diff --git a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/TypeSwitchMissingClassTest.java b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/TypeSwitchMissingClassTest.java similarity index 97% rename from src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/TypeSwitchMissingClassTest.java rename to src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/TypeSwitchMissingClassTest.java index dcf4812..175bc9b 100644 --- a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/TypeSwitchMissingClassTest.java +++ b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/TypeSwitchMissingClassTest.java
@@ -1,7 +1,7 @@ // Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -package com.android.tools.r8.jdk24.switchpatternmatching; +package com.android.tools.r8.jdk25.switchpatternmatching; import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch; import static org.junit.Assert.assertTrue; @@ -37,7 +37,7 @@ public static List<Object[]> data() { return buildParameters( getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK24) + .withCfRuntimesStartingFromIncluding(CfVm.JDK25) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .build(),
diff --git a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/TypeSwitchTest.java b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/TypeSwitchTest.java similarity index 96% rename from src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/TypeSwitchTest.java rename to src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/TypeSwitchTest.java index 97067ee..8258d20 100644 --- a/src/test/java24/com/android/tools/r8/jdk24/switchpatternmatching/TypeSwitchTest.java +++ b/src/test/java25/com/android/tools/r8/jdk25/switchpatternmatching/TypeSwitchTest.java
@@ -1,7 +1,7 @@ // Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -package com.android.tools.r8.jdk24.switchpatternmatching; +package com.android.tools.r8.jdk25.switchpatternmatching; import static com.android.tools.r8.desugar.switchpatternmatching.SwitchTestHelper.hasJdk21TypeSwitch; import static org.junit.Assert.assertTrue; @@ -32,7 +32,7 @@ @Parameters(name = "{0}") public static TestParametersCollection data() { return getTestParameters() - .withCfRuntimesStartingFromIncluding(CfVm.JDK24) + .withCfRuntimesStartingFromIncluding(CfVm.JDK25) .withDexRuntimes() .withAllApiLevelsAlsoForCf() .withPartialCompilation()
diff --git a/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java b/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java index 58f7a23..b5588f1 100644 --- a/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java +++ b/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java
@@ -622,8 +622,7 @@ public T enableCheckEnumUnboxedAnnotations() { return addCheckEnumUnboxedAnnotation() - .addInternalMatchInterfaceRule(CheckEnumUnboxedRule.RULE_NAME, CheckEnumUnboxed.class) - .enableExperimentalCheckEnumUnboxed(); + .addInternalMatchInterfaceRule(CheckEnumUnboxedRule.RULE_NAME, CheckEnumUnboxed.class); } public T enableKeepUnusedReturnValueAnnotations() { @@ -813,11 +812,6 @@ return self(); } - public T enableExperimentalCheckEnumUnboxed() { - builder.setEnableExperimentalCheckEnumUnboxed(); - return self(); - } - public T enableExperimentalKeepAnnotations(KeepAnnotationLibrary keepAnnotationLibrary) { return addOptionsModification(o -> o.testing.enableEmbeddedKeepAnnotations = true) .addKeepAnnoLibToClasspath(keepAnnotationLibrary);
diff --git a/src/test/testbase/java/com/android/tools/r8/TestBase.java b/src/test/testbase/java/com/android/tools/r8/TestBase.java index 31158a3..90c65bd 100644 --- a/src/test/testbase/java/com/android/tools/r8/TestBase.java +++ b/src/test/testbase/java/com/android/tools/r8/TestBase.java
@@ -948,7 +948,7 @@ factory -> { ProguardConfiguration.Builder builder = ProguardConfiguration.builder(factory, new Reporter()); - builder.addRule(ProguardKeepRule.defaultKeepAllRule(unused -> {})); + builder.addRule(ProguardKeepRule.defaultKeepAllRule(unused -> {}), null, null); return builder .addKeepAttributePatterns(ImmutableList.of(ProguardKeepAttributes.SIGNATURE)) .build(); @@ -1095,7 +1095,7 @@ protected static ProguardConfiguration buildConfigForRules( DexItemFactory factory, Reporter reporter, Collection<ProguardConfigurationRule> rules) { ProguardConfiguration.Builder builder = ProguardConfiguration.builder(factory, reporter); - rules.forEach(builder::addRule); + rules.forEach(rule -> builder.addRule(rule, null, null)); return builder.build(); }
diff --git a/src/test/testbase/java/com/android/tools/r8/TestRuntime.java b/src/test/testbase/java/com/android/tools/r8/TestRuntime.java index 84059ff..01a80bc 100644 --- a/src/test/testbase/java/com/android/tools/r8/TestRuntime.java +++ b/src/test/testbase/java/com/android/tools/r8/TestRuntime.java
@@ -34,15 +34,11 @@ JDK8("jdk8", 52), JDK9("jdk9", 53), JDK10("jdk10", 54), + // From JDK-11 only include LTS and latest non-LTS. JDK11("jdk11", 55), JDK17("jdk17", 61), - JDK18("jdk18", 62), - JDK20("jdk20", 64), - // From JDK-21 only include LTS and latest non-LTS. JDK21("jdk21", 65), - // TODO(b/383073689) Remove JDK-23 when bots test JDK-23. - JDK23("jdk23", 67), - JDK24("jdk24", 68); + JDK25("jdk25", 69); // Keep JDK-25 (LTS) when adding JDK-26. /** This should generally be the latest checked in CF runtime we fully support. */ private static final CfVm DEFAULT = JDK11; @@ -101,8 +97,7 @@ private static final Path JDK11_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "jdk-11"); private static final Path JDK17_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "jdk-17"); private static final Path JDK21_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "jdk-21"); - private static final Path JDK23_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "jdk-23"); - private static final Path JDK24_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "jdk-24"); + private static final Path JDK25_PATH = Paths.get(ToolHelper.THIRD_PARTY_DIR, "openjdk", "jdk-25"); private static final Map<CfVm, Path> jdkPaths = ImmutableMap.of( CfVm.JDK8, JDK8_PATH, @@ -110,8 +105,7 @@ CfVm.JDK11, JDK11_PATH, CfVm.JDK17, JDK17_PATH, CfVm.JDK21, JDK21_PATH, - CfVm.JDK23, JDK23_PATH, - CfVm.JDK24, JDK24_PATH); + CfVm.JDK25, JDK25_PATH); public static CfRuntime getCheckedInJdk(CfVm vm) { if (vm == CfVm.JDK8) { @@ -164,8 +158,8 @@ return new CfRuntime(CfVm.JDK21, getCheckedInJdkHome(CfVm.JDK21)); } - public static CfRuntime getCheckedInJdk24() { - return new CfRuntime(CfVm.JDK24, getCheckedInJdkHome(CfVm.JDK24)); + public static CfRuntime getCheckedInJdk25() { + return new CfRuntime(CfVm.JDK25, getCheckedInJdkHome(CfVm.JDK25)); } public static List<CfRuntime> getCheckedInCfRuntimes() { @@ -176,7 +170,7 @@ getCheckedInJdk11(), getCheckedInJdk17(), getCheckedInJdk21(), - getCheckedInJdk24() + getCheckedInJdk25() }; Builder<CfRuntime> builder = ImmutableList.builder(); for (CfRuntime jdk : jdks) { @@ -237,8 +231,8 @@ if (version.equals("21") || version.startsWith("21.")) { return new CfRuntime(CfVm.JDK21, Paths.get(home)); } - if (version.equals("24") || version.startsWith("24.")) { - return new CfRuntime(CfVm.JDK24, Paths.get(home)); + if (version.equals("25") || version.startsWith("25.")) { + return new CfRuntime(CfVm.JDK25, Paths.get(home)); } throw new Unimplemented("No support for JDK version: " + version); }
diff --git a/third_party/openjdk/jdk-25/linux.tar.gz.sha1 b/third_party/openjdk/jdk-25/linux.tar.gz.sha1 new file mode 100644 index 0000000..d91d054 --- /dev/null +++ b/third_party/openjdk/jdk-25/linux.tar.gz.sha1
@@ -0,0 +1 @@ +9e335e4adb9d23d93cd8f3d47205d4d4aa6f23fa \ No newline at end of file
diff --git a/third_party/openjdk/jdk-25/osx.tar.gz.sha1 b/third_party/openjdk/jdk-25/osx.tar.gz.sha1 new file mode 100644 index 0000000..644b0f9 --- /dev/null +++ b/third_party/openjdk/jdk-25/osx.tar.gz.sha1
@@ -0,0 +1 @@ +46fcab6f0c50aca73b45cb2e3484f91ec7a0d667 \ No newline at end of file
diff --git a/third_party/openjdk/jdk-25/windows.tar.gz.sha1 b/third_party/openjdk/jdk-25/windows.tar.gz.sha1 new file mode 100644 index 0000000..bec4649 --- /dev/null +++ b/third_party/openjdk/jdk-25/windows.tar.gz.sha1
@@ -0,0 +1 @@ +adcf71c4772ec35c4a9c00512e0a5fdfc53b02f0 \ No newline at end of file
diff --git a/third_party/youtube/youtube.android_17.19.tar.gz.sha1 b/third_party/youtube/youtube.android_17.19.tar.gz.sha1 index e672542..fbf7aa7 100644 --- a/third_party/youtube/youtube.android_17.19.tar.gz.sha1 +++ b/third_party/youtube/youtube.android_17.19.tar.gz.sha1
@@ -1 +1 @@ -474aefd92152017a7e0ad54020b95c6d5045a0bb \ No newline at end of file +741cbc7f6f1a80956e0ff6d25b43242e21d8225a \ No newline at end of file
diff --git a/tools/archive_desugar_jdk_libs.py b/tools/archive_desugar_jdk_libs.py index 1a9dd57..25e42ec 100755 --- a/tools/archive_desugar_jdk_libs.py +++ b/tools/archive_desugar_jdk_libs.py
@@ -154,9 +154,9 @@ def GetJavaEnv(androidHomeTemp): - java_env = dict(os.environ, JAVA_HOME=jdk.GetJdk11Home()) + java_env = dict(os.environ, JAVA_HOME=jdk.GetJdk17Home()) java_env['PATH'] = java_env['PATH'] + os.pathsep + os.path.join( - jdk.GetJdk11Home(), 'bin') + jdk.GetJdk17Home(), 'bin') java_env['GRADLE_OPTS'] = '-Xmx1g' java_env['ANDROID_HOME'] = androidHomeTemp return java_env @@ -392,7 +392,7 @@ # Make sure bazel is extracted in third_party. utils.DownloadFromGoogleCloudStorage(utils.BAZEL_SHA_FILE) utils.DownloadFromGoogleCloudStorage(utils.JAVA8_SHA_FILE) - utils.DownloadFromGoogleCloudStorage(utils.JAVA11_SHA_FILE) + utils.DownloadFromGoogleCloudStorage(utils.JAVA17_SHA_FILE) utils.DownloadFromGoogleCloudStorage(utils.DESUGAR_JDK_LIBS_11_SHA_FILE) for v in options.variant:
diff --git a/tools/jdk.py b/tools/jdk.py index e2454eb..bff5a96 100755 --- a/tools/jdk.py +++ b/tools/jdk.py
@@ -10,7 +10,7 @@ JDK_DIRS = os.path.join(defines.THIRD_PARTY, 'openjdk') -ALL_JDKS = ['openjdk-9.0.4', 'jdk-11', 'jdk-17', 'jdk-21', 'jdk-24'] +ALL_JDKS = ['openjdk-9.0.4', 'jdk-11', 'jdk-17', 'jdk-21', 'jdk-25'] def GetDefaultJdkHome(): @@ -62,6 +62,10 @@ return dirs +def GetJdk17Home(): + return GetJdkHome('jdk-17') + + def GetJdk11Home(): return GetJdkHome('jdk-11')
diff --git a/tools/test.py b/tools/test.py index 811c95e..3ed2c71 100755 --- a/tools/test.py +++ b/tools/test.py
@@ -45,7 +45,7 @@ NUMBER_OF_TEST_REPORTS = 5 REPORTS_PATH = os.path.join(utils.BUILD, 'reports') REPORT_INDEX = ['tests', 'test', 'index.html'] -VALID_RUNTIMES = ['none', 'jdk8', 'jdk9', 'jdk11', 'jdk17', 'jdk21', 'jdk24' +VALID_RUNTIMES = ['none', 'jdk8', 'jdk9', 'jdk11', 'jdk17', 'jdk21', 'jdk25' ] + ['dex-%s' % dexvm for dexvm in ALL_ART_VMS]
diff --git a/tools/utils.py b/tools/utils.py index 23453aa..29ecb79 100644 --- a/tools/utils.py +++ b/tools/utils.py
@@ -146,6 +146,8 @@ 'linux-x86.tar.gz.sha1') JAVA11_SHA_FILE = os.path.join(THIRD_PARTY, 'openjdk', 'jdk-11', 'linux.tar.gz.sha1') +JAVA17_SHA_FILE = os.path.join(THIRD_PARTY, 'openjdk', 'jdk-17', + 'linux.tar.gz.sha1') DESUGAR_JDK_LIBS_11_SHA_FILE = os.path.join(THIRD_PARTY, 'openjdk', 'desugar_jdk_libs_11.tar.gz.sha1') IGNORE_WARNINGS_RULES = os.path.join(REPO_ROOT, 'src', 'test',