Add preview API for R8 partial shrinking
Bug: b/309743298
Change-Id: Iec44c1d485365e93544153a9a745167091c72b74
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 24d8545..93926dd 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -247,7 +247,7 @@
static void runInternal(AndroidApp app, InternalOptions options, ExecutorService executor)
throws IOException {
- if (options.r8PartialCompilationOptions.enabled) {
+ if (options.partialCompilationConfiguration.isEnabled()) {
try {
new R8Partial(options).runInternal(app, executor);
} catch (ResourceException e) {
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 1077122..6a7475f 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -51,6 +51,7 @@
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.InternalOptions.MappingComposeOptions;
import com.android.tools.r8.utils.ProgramClassCollection;
+import com.android.tools.r8.utils.R8PartialCompilationConfiguration;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.SemanticVersion;
import com.android.tools.r8.utils.SetUtils;
@@ -149,6 +150,7 @@
private AndroidResourceConsumer androidResourceConsumer = null;
private ResourceShrinkerConfiguration resourceShrinkerConfiguration =
ResourceShrinkerConfiguration.DEFAULT_CONFIGURATION;
+ private R8PartialCompilationConfiguration partialCompilationConfiguration = null;
private final ProguardConfigurationParserOptions.Builder parserOptionsBuilder =
ProguardConfigurationParserOptions.builder().readEnvironment();
@@ -554,6 +556,43 @@
this.readEmbeddedRulesFromClasspathAndLibrary = enable;
}
+ /**
+ * Configure partial shrinking in R8, where R8 is only applied to a part of the input.
+ *
+ * <p>The patterns in {@code includePatterns} and {@code excludePatterns} are comma separated
+ * lists of string patterns of fully qualified names of packages/classes. The patterns support
+ * the wildcards {@code *} and {@code **}. The wildcards are only supported at the end of the
+ * pattern, so only prefix matching. If the character just before the wildcard is a {@code .}
+ * (package separator) then the difference between {@code *} and {@code **} is that {@code *}
+ * only includes classes in the same package, whereas {@code **} includes classes in subpackages
+ * as well. If the character before the wildcard is not a {@code .} (package separator) then
+ * {@code *} and {@code **} will both match all classes with that prefix.
+ *
+ * <p>If {@code includePatterns} is not specified ({@code null} or an empty string), the default
+ * is {@code "androidx.**,kotlin.**,kotlinx.**"}.
+ *
+ * <p>The include patterns are processed first collecting all possible include classes. Then the
+ * exclude patterns are applied removing all matching classes from the collected include
+ * classes.
+ *
+ * @param includePatterns patterns for classes to include in R8 shrinking (see above for
+ * semantics)
+ * @param excludePatterns patterns for classes to exclude from R8 shrinking (see above for
+ * semantics)
+ * @return
+ */
+ @Deprecated
+ public Builder enableExperimentalPartialShrinking(
+ String includePatterns, String excludePatterns) {
+ if (includePatterns == null || includePatterns.isEmpty()) {
+ includePatterns = "androidx.**,kotlin.**,kotlinx.**";
+ }
+ partialCompilationConfiguration =
+ R8PartialCompilationConfiguration.fromIncludeExcludePatterns(
+ includePatterns, excludePatterns);
+ return self();
+ }
+
@Override
protected InternalProgramOutputPathConsumer createProgramOutputConsumer(
Path path,
@@ -799,7 +838,8 @@
androidResourceConsumer,
resourceShrinkerConfiguration,
keepSpecifications,
- buildMetadataConsumer);
+ buildMetadataConsumer,
+ partialCompilationConfiguration);
if (inputDependencyGraphConsumer != null) {
inputDependencyGraphConsumer.finished();
@@ -1016,6 +1056,7 @@
private final AndroidResourceConsumer androidResourceConsumer;
private final ResourceShrinkerConfiguration resourceShrinkerConfiguration;
private final Consumer<? super R8BuildMetadata> buildMetadataConsumer;
+ private final R8PartialCompilationConfiguration partialCompilationConfiguration;
/** Get a new {@link R8Command.Builder}. */
public static Builder builder() {
@@ -1115,7 +1156,8 @@
AndroidResourceConsumer androidResourceConsumer,
ResourceShrinkerConfiguration resourceShrinkerConfiguration,
List<KeepSpecificationSource> keepSpecifications,
- Consumer<? super R8BuildMetadata> buildMetadataConsumer) {
+ Consumer<? super R8BuildMetadata> buildMetadataConsumer,
+ R8PartialCompilationConfiguration partialCompilationConfiguration) {
super(
inputApp,
mode,
@@ -1165,6 +1207,7 @@
this.androidResourceConsumer = androidResourceConsumer;
this.resourceShrinkerConfiguration = resourceShrinkerConfiguration;
this.buildMetadataConsumer = buildMetadataConsumer;
+ this.partialCompilationConfiguration = partialCompilationConfiguration;
}
private R8Command(boolean printHelp, boolean printVersion) {
@@ -1194,6 +1237,7 @@
androidResourceConsumer = null;
resourceShrinkerConfiguration = null;
buildMetadataConsumer = null;
+ partialCompilationConfiguration = null;
}
public DexItemFactory getDexItemFactory() {
@@ -1343,6 +1387,10 @@
}
// EXPERIMENTAL flags.
+ if (partialCompilationConfiguration != null) {
+ internal.partialCompilationConfiguration = partialCompilationConfiguration;
+ }
+
assert !internal.forceProguardCompatibility;
internal.forceProguardCompatibility = forceProguardCompatibility;
diff --git a/src/main/java/com/android/tools/r8/R8Partial.java b/src/main/java/com/android/tools/r8/R8Partial.java
index ff1d8a6..42b5b5b 100644
--- a/src/main/java/com/android/tools/r8/R8Partial.java
+++ b/src/main/java/com/android/tools/r8/R8Partial.java
@@ -38,23 +38,22 @@
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
-import java.util.function.Predicate;
import java.util.stream.Collectors;
class R8Partial {
private final InternalOptions options;
- private final Consumer<AndroidApp> r8InputApp;
- private final Consumer<AndroidApp> d8InputApp;
- private final Consumer<AndroidApp> r8OutputApp;
- private final Consumer<AndroidApp> d8OutputApp;
+ private final Consumer<AndroidApp> r8InputAppConsumer;
+ private final Consumer<AndroidApp> d8InputAppConsumer;
+ private final Consumer<AndroidApp> r8OutputAppConsumer;
+ private final Consumer<AndroidApp> d8OutputAppConsumer;
R8Partial(InternalOptions options) {
this.options = options;
- this.r8InputApp = options.r8PartialCompilationOptions.r8InputApp;
- this.d8InputApp = options.r8PartialCompilationOptions.d8InputApp;
- this.r8OutputApp = options.r8PartialCompilationOptions.r8OutputApp;
- this.d8OutputApp = options.r8PartialCompilationOptions.d8OutputApp;
+ this.r8InputAppConsumer = options.partialCompilationConfiguration.r8InputAppConsumer;
+ this.d8InputAppConsumer = options.partialCompilationConfiguration.d8InputAppConsumer;
+ this.r8OutputAppConsumer = options.partialCompilationConfiguration.r8OutputAppConsumer;
+ this.d8OutputAppConsumer = options.partialCompilationConfiguration.d8OutputAppConsumer;
}
static void runForTesting(AndroidApp app, InternalOptions options)
@@ -77,8 +76,8 @@
ProgramConsumer originalProgramConsumer = options.programConsumer;
MapConsumer originalMapConsumer = options.mapConsumer;
- Path tmp = options.r8PartialCompilationOptions.getTemp();
- Path dumpFile = options.r8PartialCompilationOptions.getDumpFile();
+ Path tmp = options.partialCompilationConfiguration.getTempDir();
+ Path dumpFile = options.partialCompilationConfiguration.getDumpFile();
// Create a dump of the compiler input.
// TODO(b/309743298): Do not use compiler dump to handle splitting the compilation. This should
@@ -120,15 +119,15 @@
AppInfoWithClassHierarchy.createForDesugaring(
AppInfo.createInitialAppInfo(dapp, GlobalSyntheticsStrategy.forNonSynthesizing()));
- Predicate<String> isR8 = options.r8PartialCompilationOptions.isR8;
- Set<String> d8classes = new HashSet<>();
+ Set<DexProgramClass> d8classes = new HashSet<>();
appInfo
.classes()
.forEach(
clazz -> {
- String key = clazz.toSourceString();
- if (!d8classes.contains(key) && !isR8.test(key)) {
- d8classes.add(key);
+ if (!d8classes.contains(clazz)
+ && !options.partialCompilationConfiguration.test(
+ clazz.getType().getDescriptor())) {
+ d8classes.add(clazz);
// TODO(b/309743298): Improve this to only visit each class once and stop at
// library boundary.
appInfo.forEachSuperType(
@@ -137,7 +136,7 @@
DexProgramClass superClass =
asProgramClassOrNull(appInfo.definitionFor(superType));
if (superClass != null) {
- d8classes.add(superClass.toSourceString());
+ d8classes.add(superClass);
}
});
}
@@ -146,7 +145,7 @@
// Filter the program input into the D8 and R8 parts.
Set<String> d8ZipEntries =
d8classes.stream()
- .map(name -> name.replace('.', '/') + ".class")
+ .map(clazz -> ZipUtils.zipEntryNameForClass(clazz.getClassReference()))
.collect(Collectors.toSet());
ZipBuilder d8ProgramBuilder = ZipBuilder.builder(tmp.resolve("d8-program.jar"));
ZipBuilder r8ProgramBuilder = ZipBuilder.builder(tmp.resolve("r8-program.jar"));
@@ -178,21 +177,21 @@
Files.readString(dump.getDesugaredLibraryFile(), UTF_8));
}
AndroidAppConsumers d8OutputAppSink = null;
- if (d8OutputApp != null) {
+ if (d8OutputAppConsumer != null) {
d8OutputAppSink = new AndroidAppConsumers(d8Builder);
}
d8Builder.validate();
D8Command d8command = d8Builder.makeCommand();
AndroidApp d8App = d8command.getInputApp();
- if (d8InputApp != null) {
- d8InputApp.accept(d8App);
+ if (d8InputAppConsumer != null) {
+ d8InputAppConsumer.accept(d8App);
}
InternalOptions d8Options = d8command.getInternalOptions();
assert d8Options.getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)
: "Default interface methods not yet supported";
D8.runInternal(d8App, d8Options, executor);
- if (d8OutputApp != null) {
- d8OutputApp.accept(d8OutputAppSink.build());
+ if (d8OutputAppConsumer != null) {
+ d8OutputAppConsumer.accept(d8OutputAppSink.build());
}
// Run trace references to produce keep rules for the D8 compiled part.
@@ -235,15 +234,15 @@
r8Builder.validate();
R8Command r8Command = r8Builder.makeCommand();
AndroidApp r8App = r8Command.getInputApp();
- if (r8InputApp != null) {
- r8InputApp.accept(r8App);
+ if (r8InputAppConsumer != null) {
+ r8InputAppConsumer.accept(r8App);
}
InternalOptions r8Options = r8Command.getInternalOptions();
r8Options.mapConsumer = originalMapConsumer;
r8Options.quiet = true; // Don't write the R8 version.
R8.runInternal(r8App, r8Options, executor);
- if (r8OutputApp != null) {
- r8OutputApp.accept(r8OutputAppSink.build());
+ if (r8OutputAppConsumer != null) {
+ r8OutputAppConsumer.accept(r8OutputAppSink.build());
}
// Emit resources and merged DEX to the output consumer.
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 7b907e9..763198e 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -114,14 +114,11 @@
import com.android.tools.r8.verticalclassmerging.VerticallyMergedClasses;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Predicates;
-import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
@@ -1022,9 +1019,10 @@
private final ArtProfileOptions artProfileOptions = new ArtProfileOptions(this);
private final StartupOptions startupOptions = new StartupOptions();
private final InstrumentationOptions instrumentationOptions;
- public final R8PartialCompilationOptions r8PartialCompilationOptions =
- new R8PartialCompilationOptions(
- System.getProperty("com.android.tools.r8.r8PartialCompilation"));
+ public R8PartialCompilationConfiguration partialCompilationConfiguration =
+ R8PartialCompilationConfiguration.fromIncludeExcludePatterns(
+ System.getProperty("com.android.tools.r8.experimentalPartialShrinkingIncludePatterns"),
+ System.getProperty("com.android.tools.r8.experimentalPartialShrinkingExcludePatterns"));
public final TestingOptions testing = new TestingOptions();
public List<ProguardConfigurationRule> mainDexKeepRules = ImmutableList.of();
@@ -2282,43 +2280,6 @@
}
}
- public static class R8PartialCompilationOptions {
- public boolean enabled;
- public Path tempDir = null;
- public Predicate<String> isR8 = null;
- public Consumer<AndroidApp> r8InputApp;
- public Consumer<AndroidApp> d8InputApp;
- public Consumer<AndroidApp> r8OutputApp;
- public Consumer<AndroidApp> d8OutputApp;
-
- R8PartialCompilationOptions(String partialR8) {
- this.enabled = partialR8 != null;
- if (this.enabled) {
- final List<String> prefixes = Splitter.on(",").splitToList(partialR8);
- this.isR8 =
- name -> {
- for (int i = 0; i < prefixes.size(); i++) {
- if (name.startsWith(prefixes.get(i))) {
- return true;
- }
- }
- return false;
- };
- }
- }
-
- public synchronized Path getTemp() throws IOException {
- if (tempDir == null) {
- tempDir = Files.createTempDirectory("r8PartialCompilation");
- }
- return tempDir;
- }
-
- public Path getDumpFile() throws IOException {
- return getTemp().resolve("dump.zip");
- }
- }
-
public static class TestingOptions {
public boolean enableEmbeddedKeepAnnotations =
diff --git a/src/main/java/com/android/tools/r8/utils/R8PartialCompilationConfiguration.java b/src/main/java/com/android/tools/r8/utils/R8PartialCompilationConfiguration.java
new file mode 100644
index 0000000..a1c3b2d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/R8PartialCompilationConfiguration.java
@@ -0,0 +1,283 @@
+// 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.utils;
+
+import com.android.tools.r8.graph.DexString;
+import com.google.common.base.Predicates;
+import com.google.common.base.Splitter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+public class R8PartialCompilationConfiguration implements Predicate<DexString> {
+
+ private final boolean enabled;
+ private Path tempDir = null;
+ private final List<Predicate<DexString>> includePredicates;
+ private final List<Predicate<DexString>> excludePredicates;
+
+ public Consumer<AndroidApp> r8InputAppConsumer;
+ public Consumer<AndroidApp> d8InputAppConsumer;
+ public Consumer<AndroidApp> r8OutputAppConsumer;
+ public Consumer<AndroidApp> d8OutputAppConsumer;
+
+ private static final R8PartialCompilationConfiguration disabledConfiguration =
+ new R8PartialCompilationConfiguration(false, null, null);
+
+ private R8PartialCompilationConfiguration(
+ boolean enabled,
+ List<Predicate<DexString>> includePredicates,
+ List<Predicate<DexString>> excludePredicates) {
+ assert !enabled || !includePredicates.isEmpty();
+ assert !enabled || excludePredicates != null;
+ this.enabled = enabled;
+ this.includePredicates = includePredicates;
+ this.excludePredicates = excludePredicates;
+ }
+
+ @Override
+ public boolean test(DexString name) {
+ for (Predicate<DexString> isR8ClassPredicate : includePredicates) {
+ if (isR8ClassPredicate.test(name)) {
+ for (Predicate<DexString> isD8ClassPredicate : excludePredicates) {
+ if (isD8ClassPredicate.test(name)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static R8PartialCompilationConfiguration disabledConfiguration() {
+ return disabledConfiguration;
+ }
+
+ public static R8PartialCompilationConfiguration fromIncludeExcludePatterns(
+ String includePatterns, String excludePatterns) {
+ boolean enabled = includePatterns != null || excludePatterns != null;
+ if (!enabled) {
+ return disabledConfiguration();
+ }
+ Builder builder = builder();
+ if (includePatterns != null) {
+ Splitter.on(",").splitToList(includePatterns).forEach(builder::addJavaTypeIncludePattern);
+ }
+ if (excludePatterns != null) {
+ Splitter.on(",").splitToList(excludePatterns).forEach(builder::addJavaTypeExcludePattern);
+ }
+ return builder.build();
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public synchronized Path getTempDir() throws IOException {
+ if (tempDir == null) {
+ setTempDir(Files.createTempDirectory("r8PartialCompilation"));
+ }
+ return tempDir;
+ }
+
+ public void setTempDir(Path tempDir) {
+ this.tempDir = tempDir;
+ }
+
+ public Path getDumpFile() throws IOException {
+ return getTempDir().resolve("dump.zip");
+ }
+
+ public static class Builder {
+ private final List<Predicate<DexString>> includePredicates = new ArrayList<>();
+ private final List<Predicate<DexString>> excludePredicates = new ArrayList<>();
+
+ private Builder() {}
+
+ public R8PartialCompilationConfiguration build() {
+ return new R8PartialCompilationConfiguration(
+ !includePredicates.isEmpty(), includePredicates, excludePredicates);
+ }
+
+ public Builder includeAll() {
+ includePredicates.add(Predicates.alwaysTrue());
+ return this;
+ }
+
+ public Builder addJavaTypeIncludePattern(String pattern) {
+ includePredicates.add(
+ createMatcher("L" + DescriptorUtils.getBinaryNameFromJavaType(pattern)));
+ return this;
+ }
+
+ public Builder addJavaTypeExcludePattern(String pattern) {
+ excludePredicates.add(
+ createMatcher("L" + DescriptorUtils.getBinaryNameFromJavaType(pattern)));
+ return this;
+ }
+
+ public Builder addDescriptorIncludePattern(String pattern) {
+ includePredicates.add(createMatcher(pattern));
+ return this;
+ }
+
+ public Builder addDescriptorExcludePattern(String pattern) {
+ excludePredicates.add(createMatcher(pattern));
+ return this;
+ }
+
+ private Predicate<DexString> createMatcher(String descriptorPrefix) {
+ assert descriptorPrefix.startsWith("L");
+ assert descriptorPrefix.indexOf('.') == -1;
+
+ if (descriptorPrefix.equals("L**")) {
+ return new AllClassesMatcher();
+ } else if (descriptorPrefix.equals("L*")) {
+ return new UnnamedPackageMatcher();
+ } else if (descriptorPrefix.endsWith("/**")) {
+ return new PackageAndSubpackagePrefixMatcher(
+ descriptorPrefix.substring(0, descriptorPrefix.length() - 2));
+ } else if (descriptorPrefix.endsWith("/*")) {
+ return new PackagePrefixMatcher(
+ descriptorPrefix.substring(0, descriptorPrefix.length() - 1));
+ }
+ if (descriptorPrefix.endsWith("*")) {
+ return new ClassPrefixMatcher(descriptorPrefix.substring(0, descriptorPrefix.length() - 1));
+ } else {
+ return new ClassNameMatcher(descriptorPrefix + ';');
+ }
+ }
+
+ public Builder includeClasses(Class<?>... classes) {
+ return includeClasses(Arrays.asList(classes));
+ }
+
+ public Builder includeClasses(Collection<Class<?>> classes) {
+ classes.forEach(
+ clazz ->
+ includePredicates.add(
+ descriptor ->
+ descriptor.toString().equals(DescriptorUtils.javaClassToDescriptor(clazz))));
+ return this;
+ }
+
+ public Builder includeJavaType(Predicate<String> include) {
+ includePredicates.add(
+ descriptor -> include.test(DescriptorUtils.descriptorToJavaType(descriptor.toString())));
+ return this;
+ }
+
+ public Builder excludeClasses(Class<?>... classes) {
+ return excludeClasses(Arrays.asList(classes));
+ }
+
+ public Builder excludeClasses(Collection<Class<?>> classes) {
+ classes.forEach(
+ clazz ->
+ excludePredicates.add(
+ descriptor ->
+ descriptor.toString().equals(DescriptorUtils.javaClassToDescriptor(clazz))));
+ return this;
+ }
+
+ public Builder excludeJavaType(Predicate<String> exclude) {
+ excludePredicates.add(
+ descriptor -> exclude.test(DescriptorUtils.descriptorToJavaType(descriptor.toString())));
+ return this;
+ }
+ }
+
+ private static class AllClassesMatcher implements Predicate<DexString> {
+
+ AllClassesMatcher() {}
+
+ @Override
+ public boolean test(DexString descriptor) {
+ return true;
+ }
+ }
+
+ private static class UnnamedPackageMatcher implements Predicate<DexString> {
+
+ UnnamedPackageMatcher() {}
+
+ @Override
+ public boolean test(DexString descriptor) {
+ return descriptor.indexOf('/') == -1;
+ }
+ }
+
+ private static class PackageAndSubpackagePrefixMatcher implements Predicate<DexString> {
+
+ private final byte[] descriptorPrefix;
+
+ PackageAndSubpackagePrefixMatcher(String descriptorPrefix) {
+ this.descriptorPrefix = DexString.encodeToMutf8(descriptorPrefix);
+ }
+
+ @Override
+ public boolean test(DexString descriptor) {
+ return descriptor.startsWith(descriptorPrefix);
+ }
+ }
+
+ private static class PackagePrefixMatcher implements Predicate<DexString> {
+
+ private final byte[] descriptorPrefix;
+ private final int descriptorPrefixLength;
+
+ PackagePrefixMatcher(String descriptorPrefix) {
+ this.descriptorPrefix = DexString.encodeToMutf8(descriptorPrefix);
+ this.descriptorPrefixLength = descriptorPrefix.length();
+ }
+
+ @Override
+ public boolean test(DexString descriptor) {
+ return descriptor.startsWith(descriptorPrefix)
+ && descriptor.lastIndexOf('/') == descriptorPrefixLength - 1;
+ }
+ }
+
+ private static class ClassPrefixMatcher implements Predicate<DexString> {
+
+ private final byte[] descriptorPrefix;
+ private final int descriptorPrefixLength;
+
+ ClassPrefixMatcher(String descriptorPrefix) {
+ this.descriptorPrefix = DexString.encodeToMutf8(descriptorPrefix);
+ this.descriptorPrefixLength = descriptorPrefix.length();
+ }
+
+ @Override
+ public boolean test(DexString descriptor) {
+ return descriptor.startsWith(descriptorPrefix)
+ && descriptor.lastIndexOf('/') < descriptorPrefixLength - 1;
+ }
+ }
+
+ private static class ClassNameMatcher implements Predicate<DexString> {
+
+ private final String descriptor;
+
+ ClassNameMatcher(String descriptor) {
+ this.descriptor = descriptor;
+ }
+
+ @Override
+ public boolean test(DexString descriptor) {
+ return descriptor.toString().equals(this.descriptor);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
index 5d349fc..bd1aace 100644
--- a/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
+++ b/src/test/java/com/android/tools/r8/compilerapi/CompilerApiTestCollection.java
@@ -36,6 +36,7 @@
import com.android.tools.r8.compilerapi.testsetup.ApiTestingSetUpTest;
import com.android.tools.r8.compilerapi.wrappers.CommandLineParserTest;
import com.android.tools.r8.compilerapi.wrappers.EnableMissingLibraryApiModelingTest;
+import com.android.tools.r8.partial.PartialShrinkingPreviewApiTest;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -79,7 +80,7 @@
MainDexRulesTest.ApiTest.class);
private static final List<Class<? extends CompilerApiTest>> CLASSES_PENDING_BINARY_COMPATIBILITY =
- ImmutableList.of();
+ ImmutableList.of(PartialShrinkingPreviewApiTest.ApiTest.class);
private final TemporaryFolder temp;
diff --git a/src/test/java/com/android/tools/r8/partial/ClassHierarchyInterleavedD8AndR8Test.java b/src/test/java/com/android/tools/r8/partial/ClassHierarchyInterleavedD8AndR8Test.java
index a44f2bb..32bfa65 100644
--- a/src/test/java/com/android/tools/r8/partial/ClassHierarchyInterleavedD8AndR8Test.java
+++ b/src/test/java/com/android/tools/r8/partial/ClassHierarchyInterleavedD8AndR8Test.java
@@ -14,9 +14,10 @@
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm;
import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.R8PartialCompilationConfiguration;
import com.android.tools.r8.utils.ThrowingConsumer;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import java.util.function.Predicate;
+import java.util.function.Consumer;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -41,7 +42,7 @@
}
private void runTest(
- Predicate<String> isR8,
+ Consumer<R8PartialCompilationConfiguration.Builder> partialConfigurationBuilderConsumer,
ThrowingConsumer<CodeInspector, RuntimeException> d8Inspector,
ThrowingConsumer<CodeInspector, RuntimeException> inspector)
throws Exception {
@@ -50,7 +51,7 @@
.setMinApi(parameters)
.addProgramClasses(A.class, B.class, C.class, Main.class)
.addKeepMainRule(Main.class)
- .setR8PartialConfigurationPredicate(isR8)
+ .setR8PartialConfiguration(partialConfigurationBuilderConsumer)
.compile()
.inspectD8Input(d8Inspector)
.inspect(inspector)
@@ -61,7 +62,8 @@
@Test
public void testD8Top() throws Exception {
runTest(
- name -> !name.equals(A.class.getTypeName()),
+ partialConfigurationBuilder ->
+ partialConfigurationBuilder.includeAll().excludeClasses(A.class),
inspector -> {
assertThat(inspector.programClass(A.class), isPresent());
assertThat(inspector.programClass(B.class), isAbsent());
@@ -77,7 +79,8 @@
@Test
public void testD8Middle() throws Exception {
runTest(
- name -> !name.equals(B.class.getTypeName()),
+ partialConfigurationBuilder ->
+ partialConfigurationBuilder.includeAll().excludeClasses(B.class),
inspector -> {
assertThat(inspector.programClass(A.class), isPresent());
assertThat(inspector.programClass(B.class), isPresent());
@@ -93,7 +96,8 @@
@Test
public void testD8Bottom() throws Exception {
runTest(
- name -> !name.equals(C.class.getTypeName()),
+ partialConfigurationBuilder ->
+ partialConfigurationBuilder.includeAll().excludeClasses(C.class),
inspector -> {
assertThat(inspector.programClass(A.class), isPresent());
assertThat(inspector.programClass(B.class), isPresent());
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicPreviewPatternsTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicPreviewPatternsTest.java
new file mode 100644
index 0000000..b6c55e9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicPreviewPatternsTest.java
@@ -0,0 +1,286 @@
+// 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.partial;
+
+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.ToolHelper.DexVm;
+import com.android.tools.r8.partial.pkg1.A1;
+import com.android.tools.r8.partial.pkg1.A2;
+import com.android.tools.r8.partial.pkg1.subpkg.B;
+import com.android.tools.r8.partial.pkg2.C1;
+import com.android.tools.r8.partial.pkg2.C2;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+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 PartialCompilationBasicPreviewPatternsTest extends TestBase {
+
+ private static String PKG1 = getPackageName(A1.class);
+ private static String SUBPKG = getPackageName(B.class);
+ private static String PKG2 = getPackageName(C2.class);
+
+ private static String getPackageName(Class<?> clazz) {
+ return clazz.getTypeName().substring(0, clazz.getTypeName().lastIndexOf('.'));
+ }
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ // Test with min API level 24.
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntime(DexVm.Version.V7_0_0)
+ .withApiLevel(AndroidApiLevel.N)
+ .build();
+ }
+
+ private static final List<Class<?>> ALL_CLASSES =
+ ImmutableList.of(A1.class, A2.class, B.class, C1.class, C2.class, Main.class);
+ private static final String[] ALL_TYPE_NAMES =
+ new String[] {
+ A1.class.getTypeName(),
+ A2.class.getTypeName(),
+ B.class.getTypeName(),
+ C1.class.getTypeName(),
+ C2.class.getTypeName()
+ };
+
+ @Test
+ public void pkg1AndSubpackagesCompiledWithR8() throws Exception {
+ testForR8Partial(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClasses(ALL_CLASSES)
+ .setR8PartialConfiguration(builder -> builder.addJavaTypeIncludePattern(PKG1 + ".**"))
+ .compile()
+ .inspectD8Input(
+ inspector ->
+ assertTrue(inspector.hasExactlyProgramClasses(C1.class, C2.class, Main.class)))
+ .inspectR8Input(
+ inspector ->
+ assertTrue(inspector.hasExactlyProgramClasses(A1.class, A2.class, B.class)))
+ .inspect(
+ inspector ->
+ assertTrue(inspector.hasExactlyProgramClasses(C1.class, C2.class, Main.class)))
+ .run(parameters.getRuntime(), Main.class, ALL_TYPE_NAMES)
+ .assertSuccessWithOutputLines(
+ "Not instantiated",
+ "Not instantiated",
+ "Not instantiated",
+ "Instantiated",
+ "Instantiated");
+ }
+
+ @Test
+ public void pkg1CompiledWithR8() throws Exception {
+ testForR8Partial(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClasses(ALL_CLASSES)
+ .setR8PartialConfiguration(builder -> builder.addJavaTypeIncludePattern(PKG1 + ".*"))
+ .compile()
+ .inspectD8Input(
+ inspector ->
+ assertTrue(
+ inspector.hasExactlyProgramClasses(B.class, C1.class, C2.class, Main.class)))
+ .inspectR8Input(
+ inspector -> assertTrue(inspector.hasExactlyProgramClasses(A1.class, A2.class)))
+ .inspect(
+ inspector ->
+ assertTrue(
+ inspector.hasExactlyProgramClasses(B.class, C1.class, C2.class, Main.class)))
+ .run(parameters.getRuntime(), Main.class, ALL_TYPE_NAMES)
+ .assertSuccessWithOutputLines(
+ "Not instantiated", "Not instantiated", "Instantiated", "Instantiated", "Instantiated");
+ }
+
+ @Test
+ public void pkg1AndSubpackagesExcludeSubPkgCompiledWithR8() throws Exception {
+ testForR8Partial(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClasses(ALL_CLASSES)
+ .setR8PartialConfiguration(
+ builder ->
+ builder
+ .addJavaTypeIncludePattern(PKG1 + ".**")
+ .addJavaTypeExcludePattern(SUBPKG + ".*"))
+ .compile()
+ .inspectD8Input(
+ inspector ->
+ assertTrue(
+ inspector.hasExactlyProgramClasses(C1.class, C2.class, B.class, Main.class)))
+ .inspectR8Input(
+ inspector -> assertTrue(inspector.hasExactlyProgramClasses(A1.class, A2.class)))
+ .inspect(
+ inspector ->
+ assertTrue(
+ inspector.hasExactlyProgramClasses(B.class, C1.class, C2.class, Main.class)))
+ .run(parameters.getRuntime(), Main.class, ALL_TYPE_NAMES)
+ .assertSuccessWithOutputLines(
+ "Not instantiated", "Not instantiated", "Instantiated", "Instantiated", "Instantiated");
+ }
+
+ @Test
+ public void pkg1AndSubpackagesExcludeAPrefixCompiledWithR8() throws Exception {
+ testForR8Partial(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClasses(ALL_CLASSES)
+ .setR8PartialConfiguration(
+ builder ->
+ builder
+ .addJavaTypeIncludePattern(PKG1 + ".**")
+ .addJavaTypeExcludePattern(PKG1 + ".A*"))
+ .compile()
+ .inspectD8Input(
+ inspector ->
+ assertTrue(
+ inspector.hasExactlyProgramClasses(
+ A1.class, A2.class, C1.class, C2.class, Main.class)))
+ .inspectR8Input(inspector -> assertTrue(inspector.hasExactlyProgramClasses(B.class)))
+ .inspect(
+ inspector ->
+ assertTrue(
+ inspector.hasExactlyProgramClasses(
+ A1.class, A2.class, C1.class, C2.class, Main.class)))
+ .run(parameters.getRuntime(), Main.class, ALL_TYPE_NAMES)
+ .assertSuccessWithOutputLines(
+ "Instantiated", "Instantiated", "Not instantiated", "Instantiated", "Instantiated");
+ }
+
+ @Test
+ public void pkg1AndSubpackagesExcludeA1AndA2CompiledWithR8() throws Exception {
+ testForR8Partial(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClasses(ALL_CLASSES)
+ .setR8PartialConfiguration(
+ builder ->
+ builder
+ .addJavaTypeIncludePattern(PKG1 + ".**")
+ .addJavaTypeExcludePattern(PKG1 + ".A1")
+ .addJavaTypeExcludePattern(PKG1 + ".A2"))
+ .compile()
+ .inspectD8Input(
+ inspector ->
+ assertTrue(
+ inspector.hasExactlyProgramClasses(
+ A1.class, A2.class, C1.class, C2.class, Main.class)))
+ .inspectR8Input(inspector -> assertTrue(inspector.hasExactlyProgramClasses(B.class)))
+ .inspect(
+ inspector ->
+ assertTrue(
+ inspector.hasExactlyProgramClasses(
+ A1.class, A2.class, C1.class, C2.class, Main.class)))
+ .run(parameters.getRuntime(), Main.class, ALL_TYPE_NAMES)
+ .assertSuccessWithOutputLines(
+ "Instantiated", "Instantiated", "Not instantiated", "Instantiated", "Instantiated");
+ }
+
+ @Test
+ public void allExeptC1CompiledWithR8() throws Exception {
+ testForR8Partial(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClasses(ALL_CLASSES)
+ .setR8PartialConfiguration(
+ builder ->
+ builder
+ .addJavaTypeIncludePattern(PKG1 + ".**")
+ .addJavaTypeIncludePattern(PKG2 + ".**")
+ .addJavaTypeExcludePattern(PKG2 + ".C1"))
+ .compile()
+ .inspectD8Input(
+ inspector -> assertTrue(inspector.hasExactlyProgramClasses(C1.class, Main.class)))
+ .inspectR8Input(
+ inspector ->
+ assertTrue(
+ inspector.hasExactlyProgramClasses(A1.class, A2.class, B.class, C2.class)))
+ .inspect(inspector -> assertTrue(inspector.hasExactlyProgramClasses(C1.class, Main.class)))
+ .run(parameters.getRuntime(), Main.class, ALL_TYPE_NAMES)
+ .assertSuccessWithOutputLines(
+ "Not instantiated",
+ "Not instantiated",
+ "Not instantiated",
+ "Instantiated",
+ "Not instantiated");
+ }
+
+ @Test
+ public void allExeptA1CompiledWithR8() throws Exception {
+ testForR8Partial(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClasses(ALL_CLASSES)
+ .setR8PartialConfiguration(
+ builder ->
+ builder
+ .addJavaTypeIncludePattern(PKG1 + ".*")
+ .addJavaTypeIncludePattern(SUBPKG + ".*")
+ .addJavaTypeIncludePattern(PKG2 + ".*")
+ .addJavaTypeExcludePattern(PKG1 + ".A1"))
+ .compile()
+ .inspectD8Input(
+ inspector -> assertTrue(inspector.hasExactlyProgramClasses(A1.class, Main.class)))
+ .inspectR8Input(
+ inspector ->
+ assertTrue(
+ inspector.hasExactlyProgramClasses(A2.class, B.class, C1.class, C2.class)))
+ .inspect(inspector -> assertTrue(inspector.hasExactlyProgramClasses(A1.class, Main.class)))
+ .run(parameters.getRuntime(), Main.class, ALL_TYPE_NAMES)
+ .assertSuccessWithOutputLines(
+ "Instantiated",
+ "Not instantiated",
+ "Not instantiated",
+ "Not instantiated",
+ "Not instantiated");
+ }
+
+ @Test
+ public void allExeptBCompiledWithR8() throws Exception {
+ testForR8Partial(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClasses(ALL_CLASSES)
+ .setR8PartialConfiguration(
+ builder ->
+ builder
+ .addJavaTypeIncludePattern(PKG1 + ".**")
+ .addJavaTypeIncludePattern(PKG2 + ".**")
+ .addJavaTypeExcludePattern(SUBPKG + ".*"))
+ .compile()
+ .inspectD8Input(
+ inspector -> assertTrue(inspector.hasExactlyProgramClasses(B.class, Main.class)))
+ .inspectR8Input(
+ inspector ->
+ assertTrue(
+ inspector.hasExactlyProgramClasses(A1.class, A2.class, C1.class, C2.class)))
+ .inspect(inspector -> assertTrue(inspector.hasExactlyProgramClasses(B.class, Main.class)))
+ .run(parameters.getRuntime(), Main.class, ALL_TYPE_NAMES)
+ .assertSuccessWithOutputLines(
+ "Not instantiated",
+ "Not instantiated",
+ "Instantiated",
+ "Not instantiated",
+ "Not instantiated");
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) throws Exception {
+ for (String arg : args) {
+ try {
+ Class.forName(arg);
+ System.out.println("Instantiated");
+ } catch (ClassNotFoundException e) {
+ System.out.println("Not instantiated");
+ }
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicTest.java
index 6206e13..e55a8d5 100644
--- a/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicTest.java
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicTest.java
@@ -39,7 +39,7 @@
.setMinApi(parameters)
.addProgramClasses(A.class, B.class, Main.class)
.addKeepMainRule(Main.class)
- .setR8PartialConfiguration(builder -> builder.includeAll().excludeClasses(A.class).build())
+ .setR8PartialConfiguration(builder -> builder.includeAll().excludeClasses(A.class))
.compile()
.inspectR8Input(
inspector -> {
@@ -81,7 +81,7 @@
.setMinApi(parameters)
.addProgramClasses(A.class, B.class, Main.class)
.addKeepMainRule(Main.class)
- .setR8PartialConfiguration(builder -> builder.includeAll().excludeClasses(B.class).build())
+ .setR8PartialConfiguration(builder -> builder.includeAll().excludeClasses(B.class))
.compile()
.inspectR8Input(
inspector -> {
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationDemoTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationDemoTest.java
index 0ccd8ce..43ad334 100644
--- a/src/test/java/com/android/tools/r8/partial/PartialCompilationDemoTest.java
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationDemoTest.java
@@ -236,10 +236,10 @@
private void runR8Partial(Path tempDir, CompilerDump dump, Path output, Predicate<String> isR8)
throws IOException, CompilationFailedException {
testForR8Partial(parameters.getBackend())
- .setR8PartialConfigurationPredicate(isR8)
+ .setR8PartialConfigurationJavaTypePredicate(isR8)
.addOptionsModification(
options -> {
- options.r8PartialCompilationOptions.tempDir = tempDir;
+ options.partialCompilationConfiguration.setTempDir(tempDir);
// For compiling nowonandroid.
options.testing.allowUnnecessaryDontWarnWildcards = true;
diff --git a/src/test/java/com/android/tools/r8/partial/PartialShrinkingPreviewApiTest.java b/src/test/java/com/android/tools/r8/partial/PartialShrinkingPreviewApiTest.java
new file mode 100644
index 0000000..f5784c5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialShrinkingPreviewApiTest.java
@@ -0,0 +1,78 @@
+// 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.partial;
+
+import static com.android.tools.r8.MarkerMatcher.markerMinApi;
+import static com.android.tools.r8.MarkerMatcher.markerR8Mode;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.DexIndexedConsumer;
+import com.android.tools.r8.ProgramConsumer;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.compilerapi.CompilerApiTest;
+import com.android.tools.r8.compilerapi.CompilerApiTestRunner;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.nio.file.Path;
+import org.hamcrest.CoreMatchers;
+import org.junit.Test;
+
+public class PartialShrinkingPreviewApiTest extends CompilerApiTestRunner {
+
+ public static final int MIN_API_LEVEL = 31;
+
+ public PartialShrinkingPreviewApiTest(TestParameters parameters) {
+ super(parameters);
+ }
+
+ @Override
+ public Class<? extends CompilerApiTest> binaryTestClass() {
+ return ApiTest.class;
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ ApiTest test = new ApiTest(ApiTest.PARAMETERS);
+ runTest(test::runR8);
+ }
+
+ private void runTest(ThrowingConsumer<ProgramConsumer, Exception> test) throws Exception {
+ Path output = temp.newFolder().toPath().resolve("out.jar");
+ test.accept(new DexIndexedConsumer.ArchiveConsumer(output));
+ assertThat(
+ new CodeInspector(output).getMarkers(),
+ CoreMatchers.everyItem(
+ CoreMatchers.allOf(
+ markerMinApi(AndroidApiLevel.getAndroidApiLevel(MIN_API_LEVEL)),
+ markerR8Mode("full"))));
+ }
+
+ public static class ApiTest extends CompilerApiTest {
+
+ public ApiTest(Object parameters) {
+ super(parameters);
+ }
+
+ public void runR8(ProgramConsumer programConsumer) throws Exception {
+ R8.run(
+ R8Command.builder()
+ .addClassProgramData(getBytesForClass(getMockClass()), Origin.unknown())
+ .addProguardConfiguration(getKeepMainRules(getMockClass()), Origin.unknown())
+ .addLibraryFiles(getJava8RuntimeJar())
+ .setProgramConsumer(programConsumer)
+ .enableExperimentalPartialShrinking("**", null)
+ .setMinApiLevel(MIN_API_LEVEL)
+ .build());
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runR8(DexIndexedConsumer.emptyConsumer());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/partial/pkg1/A1.java b/src/test/java/com/android/tools/r8/partial/pkg1/A1.java
new file mode 100644
index 0000000..e435721
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/pkg1/A1.java
@@ -0,0 +1,6 @@
+// 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.partial.pkg1;
+
+public class A1 {}
diff --git a/src/test/java/com/android/tools/r8/partial/pkg1/A2.java b/src/test/java/com/android/tools/r8/partial/pkg1/A2.java
new file mode 100644
index 0000000..7cac5c2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/pkg1/A2.java
@@ -0,0 +1,6 @@
+// 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.partial.pkg1;
+
+public class A2 {}
diff --git a/src/test/java/com/android/tools/r8/partial/pkg1/subpkg/B.java b/src/test/java/com/android/tools/r8/partial/pkg1/subpkg/B.java
new file mode 100644
index 0000000..ee4cb46
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/pkg1/subpkg/B.java
@@ -0,0 +1,6 @@
+// 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.partial.pkg1.subpkg;
+
+public class B {}
diff --git a/src/test/java/com/android/tools/r8/partial/pkg2/C1.java b/src/test/java/com/android/tools/r8/partial/pkg2/C1.java
new file mode 100644
index 0000000..a6225c1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/pkg2/C1.java
@@ -0,0 +1,6 @@
+// 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.partial.pkg2;
+
+public class C1 {}
diff --git a/src/test/java/com/android/tools/r8/partial/pkg2/C2.java b/src/test/java/com/android/tools/r8/partial/pkg2/C2.java
new file mode 100644
index 0000000..139666a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/pkg2/C2.java
@@ -0,0 +1,6 @@
+// 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.partial.pkg2;
+
+public class C2 {}
diff --git a/src/test/testbase/java/com/android/tools/r8/R8PartialTestBuilder.java b/src/test/testbase/java/com/android/tools/r8/R8PartialTestBuilder.java
index 9ea93eb..3dacb92 100644
--- a/src/test/testbase/java/com/android/tools/r8/R8PartialTestBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/R8PartialTestBuilder.java
@@ -10,23 +10,17 @@
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.base.Predicates;
-import com.google.common.collect.ImmutableList;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
+import com.android.tools.r8.utils.R8PartialCompilationConfiguration;
import java.util.List;
import java.util.function.Consumer;
-import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
-import java.util.stream.Collectors;
public class R8PartialTestBuilder
extends R8TestBuilder<R8PartialTestCompileResult, R8TestRunResult, R8PartialTestBuilder> {
- private R8PartialConfiguration r8PartialConfiguration =
- R8PartialConfiguration.defaultConfiguration();
+ private R8PartialCompilationConfiguration r8PartialConfiguration =
+ R8PartialCompilationConfiguration.disabledConfiguration();
private R8PartialTestBuilder(TestState state, Builder builder, Backend backend) {
super(state, builder, backend);
@@ -52,106 +46,30 @@
return this;
}
- public static class R8PartialConfiguration implements Predicate<String> {
- private static final R8PartialConfiguration defaultConfiguration =
- new R8PartialConfiguration(ImmutableList.of(), ImmutableList.of());
- private final List<Predicate<String>> includePredicates;
- private final List<Predicate<String>> excludePredicates;
-
- public R8PartialConfiguration(
- List<Predicate<String>> includePredicates, List<Predicate<String>> excludePredicates) {
- this.includePredicates = includePredicates;
- this.excludePredicates = excludePredicates;
- }
-
- private static R8PartialConfiguration defaultConfiguration() {
- return defaultConfiguration;
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- public boolean test(String name) {
- for (Predicate<String> isR8ClassPredicate : includePredicates) {
- if (isR8ClassPredicate.test(name)) {
- for (Predicate<String> isD8ClassPredicate : excludePredicates) {
- if (isD8ClassPredicate.test(name)) {
- return false;
- }
- }
- return true;
- }
- }
- return false;
- }
-
- public static class Builder {
- private final List<Predicate<String>> includePredicates = new ArrayList<>();
- private final List<Predicate<String>> excludePredicates = new ArrayList<>();
-
- public R8PartialConfiguration build() {
- return new R8PartialConfiguration(includePredicates, excludePredicates);
- }
-
- public Builder includeAll() {
- includePredicates.add(Predicates.alwaysTrue());
- return this;
- }
-
- public Builder includeClasses(Class<?>... classes) {
- return includeClasses(Arrays.asList(classes));
- }
-
- public Builder includeClasses(Collection<Class<?>> classes) {
- Collection<String> typeNames =
- classes.stream().map(Class::getTypeName).collect(Collectors.toList());
- includePredicates.add(typeNames::contains);
- return this;
- }
-
- public Builder include(Predicate<String> include) {
- includePredicates.add(include);
- return this;
- }
-
- public Builder excludeClasses(Class<?>... classes) {
- return excludeClasses(Arrays.asList(classes));
- }
-
- public Builder excludeClasses(Collection<Class<?>> classes) {
- Collection<String> typeNames =
- classes.stream().map(Class::getTypeName).collect(Collectors.toList());
- excludePredicates.add(typeNames::contains);
- return this;
- }
-
- public Builder exclude(Predicate<String> exclude) {
- excludePredicates.add(exclude);
- return this;
- }
- }
- }
-
- public R8PartialTestBuilder setR8PartialConfigurationPredicate(Predicate<String> include) {
- assert r8PartialConfiguration == R8PartialConfiguration.defaultConfiguration()
+ public R8PartialTestBuilder setR8PartialConfigurationJavaTypePredicate(
+ Predicate<String> include) {
+ assert r8PartialConfiguration.equals(R8PartialCompilationConfiguration.disabledConfiguration())
: "Overwriting configuration...?";
- r8PartialConfiguration = R8PartialConfiguration.builder().include(include).build();
+ r8PartialConfiguration =
+ R8PartialCompilationConfiguration.builder().includeJavaType(include).build();
return self();
}
- public R8PartialTestBuilder setR8PartialConfiguration(R8PartialConfiguration configuration) {
- assert r8PartialConfiguration == R8PartialConfiguration.defaultConfiguration()
+ public R8PartialTestBuilder setR8PartialConfiguration(
+ R8PartialCompilationConfiguration configuration) {
+ assert r8PartialConfiguration.equals(R8PartialCompilationConfiguration.disabledConfiguration())
: "Overwriting configuration...?";
r8PartialConfiguration = configuration;
return self();
}
public R8PartialTestBuilder setR8PartialConfiguration(
- Function<R8PartialConfiguration.Builder, R8PartialConfiguration> fn) {
- assert r8PartialConfiguration == R8PartialConfiguration.defaultConfiguration()
+ Consumer<R8PartialCompilationConfiguration.Builder> consumer) {
+ assert r8PartialConfiguration.equals(R8PartialCompilationConfiguration.disabledConfiguration())
: "Overwriting configuration...?";
- r8PartialConfiguration = fn.apply(R8PartialConfiguration.builder());
+ R8PartialCompilationConfiguration.Builder builder = R8PartialCompilationConfiguration.builder();
+ consumer.accept(builder);
+ r8PartialConfiguration = builder.build();
return self();
}
@@ -165,18 +83,17 @@
Box<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer,
StringBuilder proguardMapBuilder)
throws CompilationFailedException {
- Box<AndroidApp> r8InputApp = new Box<>();
- Box<AndroidApp> d8InputApp = new Box<>();
- Box<AndroidApp> r8OutputApp = new Box<>();
- Box<AndroidApp> d8OutputApp = new Box<>();
+ Box<AndroidApp> r8InputAppBox = new Box<>();
+ Box<AndroidApp> d8InputAppBox = new Box<>();
+ Box<AndroidApp> r8OutputAppBox = new Box<>();
+ Box<AndroidApp> d8OutputAppBox = new Box<>();
Consumer<InternalOptions> configureR8PartialCompilation =
options -> {
- options.r8PartialCompilationOptions.enabled = true;
- options.r8PartialCompilationOptions.isR8 = r8PartialConfiguration;
- options.r8PartialCompilationOptions.r8InputApp = r8InputApp::set;
- options.r8PartialCompilationOptions.d8InputApp = d8InputApp::set;
- options.r8PartialCompilationOptions.r8OutputApp = r8OutputApp::set;
- options.r8PartialCompilationOptions.d8OutputApp = d8OutputApp::set;
+ options.partialCompilationConfiguration = r8PartialConfiguration;
+ options.partialCompilationConfiguration.r8InputAppConsumer = r8InputAppBox::set;
+ options.partialCompilationConfiguration.d8InputAppConsumer = d8InputAppBox::set;
+ options.partialCompilationConfiguration.r8OutputAppConsumer = r8OutputAppBox::set;
+ options.partialCompilationConfiguration.d8OutputAppConsumer = d8OutputAppBox::set;
};
ToolHelper.runAndBenchmarkR8PartialWithoutResult(
builder, configureR8PartialCompilation.andThen(optionsConsumer), benchmarkResults);
@@ -195,9 +112,9 @@
resourceShrinkerOutput,
resourceShrinkerOutputForFeatures,
buildMetadata != null ? buildMetadata.get() : null,
- r8InputApp.get(),
- d8InputApp.get(),
- r8OutputApp.get(),
- d8OutputApp.get());
+ r8InputAppBox.get(),
+ d8InputAppBox.get(),
+ r8OutputAppBox.get(),
+ d8OutputAppBox.get());
}
}
diff --git a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index d055bdc..d9af1e0 100644
--- a/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/testbase/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -47,6 +47,7 @@
import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.BiMapContainer;
+import com.android.tools.r8.utils.BooleanBox;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
@@ -63,6 +64,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
@@ -424,6 +426,18 @@
return builder.build();
}
+ public boolean hasExactlyProgramClasses(Class<?>... classes) {
+ return hasExactlyProgramClasses(Arrays.asList(classes));
+ }
+
+ public boolean hasExactlyProgramClasses(Collection<Class<?>> classes) {
+ Set<ClassReference> descriptors =
+ classes.stream().map(Reference::classFromClass).collect(Collectors.toSet());
+ BooleanBox allFound = new BooleanBox(true);
+ forAllClasses(clazz -> allFound.and(descriptors.remove(clazz.reference)));
+ return descriptors.isEmpty() && allFound.get();
+ }
+
public Stream<InstructionSubject> streamInstructions() {
return allClasses().stream()
.flatMap(cls -> cls.allMethods(MethodSubject::hasCode).stream())