Add support for package prefix in partial optimization configuration API
Bug: b/309743298
Change-Id: Ib1adaea5b175a37d7628a57758c4cff4186f7a4e
diff --git a/src/main/java/com/android/tools/r8/PartialOptimizationConfigurationBuilder.java b/src/main/java/com/android/tools/r8/PartialOptimizationConfigurationBuilder.java
index 63604f1..cd34a0f 100644
--- a/src/main/java/com/android/tools/r8/PartialOptimizationConfigurationBuilder.java
+++ b/src/main/java/com/android/tools/r8/PartialOptimizationConfigurationBuilder.java
@@ -28,4 +28,13 @@
* @return instance which received the call for chaining of calls.
*/
PartialOptimizationConfigurationBuilder addPackage(PackageReference packageReference);
+
+ /**
+ * Add a complete package including its sub-packages to be optimized with partial optimization.
+ *
+ * @param packageReference root package to be optimized.
+ * @return instance which received the call for chaining of calls.
+ */
+ PartialOptimizationConfigurationBuilder addPackageAndSubPackages(
+ PackageReference packageReference);
}
diff --git a/src/main/java/com/android/tools/r8/partial/R8PartialCompilationConfiguration.java b/src/main/java/com/android/tools/r8/partial/R8PartialCompilationConfiguration.java
index 961e95f..89871c3 100644
--- a/src/main/java/com/android/tools/r8/partial/R8PartialCompilationConfiguration.java
+++ b/src/main/java/com/android/tools/r8/partial/R8PartialCompilationConfiguration.java
@@ -275,13 +275,38 @@
@Override
public PartialOptimizationConfigurationBuilder addClass(ClassReference classReference) {
+ if (classReference.getBinaryName().contains("*")) {
+ throw new IllegalArgumentException("Illegal package name: " + classReference.getTypeName());
+ }
includePredicates.add(createPredicate("L" + classReference.getBinaryName()));
return this;
}
@Override
public PartialOptimizationConfigurationBuilder addPackage(PackageReference packageReference) {
- includePredicates.add(createPredicate("L" + packageReference.getPackageBinaryName() + "/*"));
+ if (packageReference.getPackageBinaryName().contains("*")) {
+ throw new IllegalArgumentException(
+ "Illegal package name: " + packageReference.getPackageName());
+ }
+ if (packageReference.getPackageBinaryName().isEmpty()) {
+ includePredicates.add(new UnnamedPackageMatcher());
+ } else {
+ includePredicates.add(
+ createPredicate("L" + packageReference.getPackageBinaryName() + "/*"));
+ }
+ return this;
+ }
+
+ @Override
+ public PartialOptimizationConfigurationBuilder addPackageAndSubPackages(
+ PackageReference packageReference) {
+ if (packageReference.getPackageBinaryName().isEmpty()) {
+ includePredicates.add(new AllClassesMatcher());
+ } else {
+ includePredicates.add(
+ new PackageAndSubpackagePrefixMatcher(
+ "L" + packageReference.getPackageBinaryName() + "/"));
+ }
return this;
}
}
diff --git a/src/test/java/com/android/tools/r8/partial/PartialOptimizationConfigurationTest.java b/src/test/java/com/android/tools/r8/partial/PartialOptimizationConfigurationTest.java
new file mode 100644
index 0000000..1605da6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialOptimizationConfigurationTest.java
@@ -0,0 +1,172 @@
+// 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.partial;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.partial.R8PartialCompilationConfiguration.Builder;
+import com.android.tools.r8.partial.predicate.R8PartialPredicateCollection;
+import com.android.tools.r8.references.Reference;
+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 PartialOptimizationConfigurationTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ @Test
+ public void testAddClass() throws Exception {
+ DexItemFactory factory = new DexItemFactory();
+ Builder builder = R8PartialCompilationConfiguration.builder();
+ builder.addClass(Reference.classFromTypeName("A"));
+ builder.addClass(Reference.classFromTypeName("a.a.A"));
+ builder.addClass(Reference.classFromTypeName("aa.bb.cc.DD"));
+ R8PartialCompilationConfiguration configuration = builder.build();
+ R8PartialPredicateCollection predicates = configuration.getIncludePredicates();
+ assertEquals(3, predicates.size());
+ assertTrue(configuration.getExcludePredicates().isEmpty());
+ assertTrue(predicates.test(factory.createString("LA;")));
+ assertFalse(predicates.test(factory.createString("La;")));
+ assertFalse(predicates.test(factory.createString("La/A;")));
+ assertTrue(predicates.test(factory.createString("La/a/A;")));
+ assertFalse(predicates.test(factory.createString("La/b/B;")));
+ assertFalse(predicates.test(factory.createString("La/a/a/A;")));
+ assertFalse(predicates.test(factory.createString("Laa/A;")));
+ assertFalse(predicates.test(factory.createString("Laa/bb;")));
+ assertFalse(predicates.test(factory.createString("Laa/bb/CC;")));
+ assertTrue(predicates.test(factory.createString("Laa/bb/cc/DD;")));
+ assertFalse(predicates.test(factory.createString("Laa/bb/cc/dd/EE;")));
+ }
+
+ @Test
+ public void testAddPackage() throws Exception {
+ DexItemFactory factory = new DexItemFactory();
+ Builder builder = R8PartialCompilationConfiguration.builder();
+ builder.addPackage(Reference.packageFromString("a"));
+ builder.addPackage(Reference.packageFromString("aa.bb"));
+ R8PartialCompilationConfiguration configuration = builder.build();
+ R8PartialPredicateCollection predicates = configuration.getIncludePredicates();
+ assertEquals(2, predicates.size());
+ assertTrue(configuration.getExcludePredicates().isEmpty());
+ assertFalse(predicates.test(factory.createString("LA;")));
+ assertFalse(predicates.test(factory.createString("La;")));
+ assertTrue(predicates.test(factory.createString("La/A;")));
+ assertFalse(predicates.test(factory.createString("La/a/A;")));
+ assertFalse(predicates.test(factory.createString("La/b/B;")));
+ assertFalse(predicates.test(factory.createString("La/a/a/A;")));
+ assertFalse(predicates.test(factory.createString("Laa/A;")));
+ assertFalse(predicates.test(factory.createString("Laa/bb;")));
+ assertTrue(predicates.test(factory.createString("Laa/bb/CC;")));
+ assertFalse(predicates.test(factory.createString("Laa/bb/cc/DD;")));
+ assertFalse(predicates.test(factory.createString("Laa/bb/cc/dd/EE;")));
+ }
+
+ @Test
+ public void testAddPackagePackageAndSubpackages() throws Exception {
+ DexItemFactory factory = new DexItemFactory();
+ Builder builder = R8PartialCompilationConfiguration.builder();
+ builder.addPackageAndSubPackages(Reference.packageFromString("a"));
+ builder.addPackageAndSubPackages(Reference.packageFromString("aa.bb"));
+ R8PartialCompilationConfiguration configuration = builder.build();
+ R8PartialPredicateCollection predicates = configuration.getIncludePredicates();
+ assertEquals(2, predicates.size());
+ assertTrue(configuration.getExcludePredicates().isEmpty());
+ assertFalse(predicates.test(factory.createString("LA;")));
+ assertFalse(predicates.test(factory.createString("La;")));
+ assertTrue(predicates.test(factory.createString("La/A;")));
+ assertTrue(predicates.test(factory.createString("La/a/A;")));
+ assertTrue(predicates.test(factory.createString("La/b/B;")));
+ assertTrue(predicates.test(factory.createString("La/a/a/A;")));
+ assertFalse(predicates.test(factory.createString("Laa/A;")));
+ assertFalse(predicates.test(factory.createString("Laa/bb;")));
+ assertTrue(predicates.test(factory.createString("Laa/bb/CC;")));
+ assertTrue(predicates.test(factory.createString("Laa/bb/cc/DD;")));
+ assertTrue(predicates.test(factory.createString("Laa/bb/cc/dd/EE;")));
+ }
+
+ @Test
+ public void testMix() throws Exception {
+ DexItemFactory factory = new DexItemFactory();
+ Builder builder = R8PartialCompilationConfiguration.builder();
+ builder.addClass(Reference.classFromTypeName("aa.A"));
+ builder.addClass(Reference.classFromTypeName("a.b.B"));
+ builder.addPackage(Reference.packageFromString("a"));
+ builder.addPackageAndSubPackages(Reference.packageFromString("aa.bb"));
+ R8PartialCompilationConfiguration configuration = builder.build();
+ R8PartialPredicateCollection predicates = configuration.getIncludePredicates();
+ assertEquals(4, predicates.size());
+ assertTrue(configuration.getExcludePredicates().isEmpty());
+ assertFalse(predicates.test(factory.createString("LA;")));
+ assertFalse(predicates.test(factory.createString("La;")));
+ assertTrue(predicates.test(factory.createString("La/A;")));
+ assertFalse(predicates.test(factory.createString("La/a/A;")));
+ assertTrue(predicates.test(factory.createString("La/b/B;")));
+ assertFalse(predicates.test(factory.createString("La/a/a/A;")));
+ assertTrue(predicates.test(factory.createString("Laa/A;")));
+ assertFalse(predicates.test(factory.createString("Laa/bb;")));
+ assertTrue(predicates.test(factory.createString("Laa/bb/CC;")));
+ assertTrue(predicates.test(factory.createString("Laa/bb/cc/DD;")));
+ assertTrue(predicates.test(factory.createString("Laa/bb/cc/dd/EE;")));
+ }
+
+ @Test
+ public void testUnnamedPackage() throws Exception {
+ DexItemFactory factory = new DexItemFactory();
+ Builder builder = R8PartialCompilationConfiguration.builder();
+ builder.addPackage(Reference.packageFromString(""));
+ R8PartialCompilationConfiguration configuration = builder.build();
+ R8PartialPredicateCollection predicates = configuration.getIncludePredicates();
+ assertEquals(1, predicates.size());
+ assertTrue(configuration.getExcludePredicates().isEmpty());
+ assertTrue(predicates.test(factory.createString("LA;")));
+ assertTrue(predicates.test(factory.createString("La;")));
+ assertFalse(predicates.test(factory.createString("La/A;")));
+ assertFalse(predicates.test(factory.createString("La/a/A;")));
+ assertFalse(predicates.test(factory.createString("La/b/A;")));
+ assertFalse(predicates.test(factory.createString("La/a/a/A;")));
+ assertFalse(predicates.test(factory.createString("Laa/A;")));
+ assertFalse(predicates.test(factory.createString("Laa/bb;")));
+ assertFalse(predicates.test(factory.createString("Laa/bb/CC;")));
+ assertFalse(predicates.test(factory.createString("Laa/bb/cc/DD;")));
+ assertFalse(predicates.test(factory.createString("Laa/bb/cc/dd/EE;")));
+ }
+
+ @Test
+ public void testUnnamedPackageAndSubpackages() throws Exception {
+ DexItemFactory factory = new DexItemFactory();
+ Builder builder = R8PartialCompilationConfiguration.builder();
+ builder.addPackageAndSubPackages(Reference.packageFromString(""));
+ R8PartialCompilationConfiguration configuration = builder.build();
+ R8PartialPredicateCollection predicates = configuration.getIncludePredicates();
+ assertEquals(1, predicates.size());
+ assertTrue(configuration.getExcludePredicates().isEmpty());
+ assertTrue(predicates.test(factory.createString("LA;")));
+ assertTrue(predicates.test(factory.createString("La;")));
+ assertTrue(predicates.test(factory.createString("La/A;")));
+ assertTrue(predicates.test(factory.createString("La/a/A;")));
+ assertTrue(predicates.test(factory.createString("La/b/A;")));
+ assertTrue(predicates.test(factory.createString("La/a/a/A;")));
+ assertTrue(predicates.test(factory.createString("Laa/A;")));
+ assertTrue(predicates.test(factory.createString("Laa/bb;")));
+ assertTrue(predicates.test(factory.createString("Laa/bb/CC;")));
+ assertTrue(predicates.test(factory.createString("Laa/bb/cc/DD;")));
+ assertTrue(predicates.test(factory.createString("Laa/bb/cc/dd/EE;")));
+ }
+}