Merge commit '6a9a086393127c12eeec489d216f434223af9910' into dev-release
Change-Id: Ide3635762e37d6296872028c9dee0c51ad51fb18
diff --git a/doc/compilerdump.md b/doc/compilerdump.md
index 7bffd27..c85a9ff 100644
--- a/doc/compilerdump.md
+++ b/doc/compilerdump.md
@@ -9,13 +9,16 @@
The dump contains almost all of the inputs that are given to the compiler as
part of compilation. In particular, it contains *all* class definitions in the
form of Java classfiles (i.e., bytecode, not the Java source files).
+If resource shrinking is enabled, the dump also contains *all* resources (i.e.,
+the manifest, all res folder entries in proto and raw format, and any other
+file flowing into resource shrinking).
In addition to the classfiles, the dump also includes Java resources, the
compiler type, version, and flags, such as `--debug` or `--release`,
main-dex lists or rules, and more. For R8 the dump also contains the full
concatenation of all keep rules.
The dump is a zip file containing the above. You should unzip it and review
-the content locally. The program, classpath and library content will be in
+the content locally. The program, classpath, library and resource content will be in
nested zip files. The remaining content is in plain text files.
diff --git a/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java b/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java
index a8c4a65..29b468f 100644
--- a/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java
+++ b/src/main/java/com/android/tools/r8/ClassFileResourceProvider.java
@@ -49,4 +49,10 @@
default void finished(DiagnosticsHandler handler) throws IOException {
// Do nothing by default.
}
+
+ /** Experimental addition still incubating. */
+ @Deprecated
+ default DataResourceProvider getDataResourceProvider() {
+ return null;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 55a8199..8fccc8d 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -99,7 +99,7 @@
command.getReporter(),
() -> {
try {
- run(app, options, executor);
+ runInternal(app, options, executor);
} finally {
executor.shutdown();
}
@@ -119,7 +119,7 @@
ExceptionUtils.withD8CompilationHandler(
command.getReporter(),
() -> {
- run(app, options, executor);
+ runInternal(app, options, executor);
});
}
@@ -159,7 +159,7 @@
options.reporter,
() -> {
try {
- run(inputApp, options, executor);
+ runInternal(inputApp, options, executor);
} finally {
executor.shutdown();
}
@@ -190,7 +190,7 @@
return timing.time("Create app-view", () -> AppView.createForD8(appInfo, typeRewriter, timing));
}
- private static void run(AndroidApp inputApp, InternalOptions options, ExecutorService executor)
+ static void runInternal(AndroidApp inputApp, InternalOptions options, ExecutorService executor)
throws IOException {
if (options.printMemory) {
// Run GC twice to remove objects with finalizers.
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 6a8d346..24d8545 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -171,7 +171,7 @@
private final Timing timing;
private final InternalOptions options;
- private R8(InternalOptions options) {
+ R8(InternalOptions options) {
this.options = options;
if (options.printMemory) {
System.gc();
@@ -209,7 +209,7 @@
ExceptionUtils.withR8CompilationHandler(
command.getReporter(),
() -> {
- run(app, options, executor);
+ runInternal(app, options, executor);
});
}
@@ -238,16 +238,24 @@
options.reporter,
() -> {
try {
- run(app, options, executor);
+ runInternal(app, options, executor);
} finally {
executor.shutdown();
}
});
}
- private static void run(AndroidApp app, InternalOptions options, ExecutorService executor)
+ static void runInternal(AndroidApp app, InternalOptions options, ExecutorService executor)
throws IOException {
- new R8(options).run(app, executor);
+ if (options.r8PartialCompilationOptions.enabled) {
+ try {
+ new R8Partial(options).runInternal(app, executor);
+ } catch (ResourceException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ new R8(options).runInternal(app, executor);
+ }
}
private static DirectMappedDexApplication getDirectApp(AppView<?> appView) {
@@ -255,7 +263,7 @@
}
@SuppressWarnings("DefaultCharset")
- private void run(AndroidApp inputApp, ExecutorService executorService) throws IOException {
+ void runInternal(AndroidApp inputApp, ExecutorService executorService) throws IOException {
timing.begin("Run prelude");
assert options.programConsumer != null;
if (options.quiet) {
@@ -1268,8 +1276,8 @@
InternalOptions options = command.getInternalOptions();
ExecutorService executorService = ThreadUtils.getExecutorService(options);
try {
- ExceptionUtils.withR8CompilationHandler(options.reporter, () ->
- run(command.getInputApp(), options, executorService));
+ ExceptionUtils.withR8CompilationHandler(
+ options.reporter, () -> runInternal(command.getInputApp(), options, executorService));
} finally {
executorService.shutdown();
}
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 70220ac..1077122 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -141,6 +141,8 @@
private boolean enableMissingLibraryApiModeling = false;
private boolean enableExperimentalKeepAnnotations =
System.getProperty("com.android.tools.r8.enableKeepAnnotations") != null;
+ private boolean readEmbeddedRulesFromClasspathAndLibrary =
+ System.getProperty("com.android.tools.r8.readEmbeddedRulesFromClasspathAndLibrary") != null;
public boolean enableStartupLayoutOptimization = true;
private SemanticVersion fakeCompilerVersion = null;
private AndroidResourceProvider androidResourceProvider = null;
@@ -545,6 +547,13 @@
return self();
}
+ // Package private only for testing. Use system property
+ // com.android.tools.r8.readEmbeddedRulesFromClasspathAndLibrary to enable.
+ @Deprecated
+ void setReadEmbeddedRulesFromClasspathAndLibrary(boolean enable) {
+ this.readEmbeddedRulesFromClasspathAndLibrary = enable;
+ }
+
@Override
protected InternalProgramOutputPathConsumer createProgramOutputConsumer(
Path path,
@@ -801,6 +810,33 @@
private void amendWithRulesAndProvidersForInjarsAndMetaInf(
Reporter reporter, ProguardConfigurationParser parser) {
+ Supplier<SemanticVersion> semanticVersionSupplier =
+ Suppliers.memoize(
+ () -> {
+ SemanticVersion compilerVersion =
+ fakeCompilerVersion == null
+ ? SemanticVersion.create(
+ Version.getMajorVersion(),
+ Version.getMinorVersion(),
+ Version.getPatchVersion())
+ : fakeCompilerVersion;
+ if (compilerVersion.getMajor() < 0) {
+ compilerVersion =
+ SemanticVersion.create(
+ Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ reporter.warning(
+ "Running R8 version "
+ + Version.getVersionString()
+ + ", which cannot be represented as a semantic version. Using"
+ + " an artificial version newer than any known version for selecting"
+ + " Proguard configurations embedded under META-INF/. This means that"
+ + " all rules with a '-upto-' qualifier will be excluded and all rules"
+ + " with a -from- qualifier will be included.");
+ }
+ return compilerVersion;
+ });
+ // TODO(b/370665838): Remove the fixpoint, as -injars are not supported in embedded keep
+ // rules. The comment below is not true.
// Since -injars can itself reference archives with rules and that in turn have -injars the
// completion of amending rules and providers must run in a fixed point. The fixed point is
// reached once the injars set is stable.
@@ -817,47 +853,41 @@
}
}
if (providers.isEmpty()) {
- return;
+ break;
}
- Supplier<SemanticVersion> semanticVersionSupplier =
- Suppliers.memoize(
- () -> {
- SemanticVersion compilerVersion =
- fakeCompilerVersion == null
- ? SemanticVersion.create(
- Version.getMajorVersion(),
- Version.getMinorVersion(),
- Version.getPatchVersion())
- : fakeCompilerVersion;
- if (compilerVersion.getMajor() < 0) {
- compilerVersion =
- SemanticVersion.create(
- Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
- reporter.warning(
- "Running R8 version "
- + Version.getVersionString()
- + ", which cannot be represented as a semantic version. Using"
- + " an artificial version newer than any known version for selecting"
- + " Proguard configurations embedded under META-INF/. This means that"
- + " all rules with a '-upto-' qualifier will be excluded and all rules"
- + " with a -from- qualifier will be included.");
- }
- return compilerVersion;
- });
-
while (!providers.isEmpty()) {
DataResourceProvider dataResourceProvider = providers.pop().getDataResourceProvider();
- if (dataResourceProvider != null) {
- try {
- ExtractEmbeddedRules embeddedProguardConfigurationVisitor =
- new ExtractEmbeddedRules(reporter, semanticVersionSupplier);
- dataResourceProvider.accept(embeddedProguardConfigurationVisitor);
- embeddedProguardConfigurationVisitor.parseRelevantRules(parser);
- } catch (ResourceException e) {
- reporter.error(new ExceptionDiagnostic(e));
- }
- }
+ parseEmbeddedRules(reporter, parser, semanticVersionSupplier, dataResourceProvider);
+ }
+ // TODO(b/370665838): Remove the fixpoint. For now assert that it is not needed.
+ assert seenInjars.containsAll(parser.getConfigurationBuilder().getInjars());
+ }
+ if (readEmbeddedRulesFromClasspathAndLibrary) {
+ for (ClassFileResourceProvider provider : getAppBuilder().getClasspathResourceProviders()) {
+ parseEmbeddedRules(
+ reporter, parser, semanticVersionSupplier, provider.getDataResourceProvider());
+ }
+ for (ClassFileResourceProvider provider : getAppBuilder().getLibraryResourceProviders()) {
+ parseEmbeddedRules(
+ reporter, parser, semanticVersionSupplier, provider.getDataResourceProvider());
+ }
+ }
+ }
+
+ private static void parseEmbeddedRules(
+ Reporter reporter,
+ ProguardConfigurationParser parser,
+ Supplier<SemanticVersion> semanticVersionSupplier,
+ DataResourceProvider dataResourceProvider) {
+ if (dataResourceProvider != null) {
+ try {
+ ExtractEmbeddedRules embeddedProguardConfigurationVisitor =
+ new ExtractEmbeddedRules(reporter, semanticVersionSupplier);
+ dataResourceProvider.accept(embeddedProguardConfigurationVisitor);
+ embeddedProguardConfigurationVisitor.parseRelevantRules(parser);
+ } catch (ResourceException e) {
+ reporter.error(new ExceptionDiagnostic(e));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/R8CommandParser.java b/src/main/java/com/android/tools/r8/R8CommandParser.java
index c5fc86b..898f4eb 100644
--- a/src/main/java/com/android/tools/r8/R8CommandParser.java
+++ b/src/main/java/com/android/tools/r8/R8CommandParser.java
@@ -312,6 +312,8 @@
builder.setAndroidResourceProvider(new ArchiveProtoAndroidResourceProvider(inputPath));
builder.setAndroidResourceConsumer(
new ArchiveProtoAndroidResourceConsumer(outputPath, inputPath));
+ // In the CLI we default to optimized resource shrinking.
+ builder.setResourceShrinkerConfiguration(b -> b.enableOptimizedShrinkingWithR8().build());
} else if (arg.equals("--feature")) {
featureSplitConfigCollector.addInputOutput(nextArg, nextNextArg);
} else if (arg.equals(ISOLATED_SPLITS_FLAG)) {
diff --git a/src/main/java/com/android/tools/r8/R8Partial.java b/src/main/java/com/android/tools/r8/R8Partial.java
new file mode 100644
index 0000000..e9cb9b8
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/R8Partial.java
@@ -0,0 +1,232 @@
+// 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;
+
+import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.android.tools.r8.StringConsumer.FileConsumer;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dump.CompilerDump;
+import com.android.tools.r8.dump.DumpOptions;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.naming.MapConsumer;
+import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
+import com.android.tools.r8.tracereferences.TraceReferencesBridge;
+import com.android.tools.r8.tracereferences.TraceReferencesCommand;
+import com.android.tools.r8.tracereferences.TraceReferencesKeepRules;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.DumpInputFlags;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.ZipUtils;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+class R8Partial {
+
+ private final InternalOptions options;
+
+ R8Partial(InternalOptions options) {
+ this.options = options;
+ }
+
+ void runInternal(AndroidApp app, ExecutorService executor) throws IOException, ResourceException {
+ Timing timing = Timing.create("R8 partial " + Version.LABEL, options);
+
+ ProgramConsumer originalProgramConsumer = options.programConsumer;
+ MapConsumer originalMapConsumer = options.mapConsumer;
+
+ Path tmp = options.r8PartialCompilationOptions.getTemp();
+ Path dumpFile = options.r8PartialCompilationOptions.getDumpFile();
+
+ // Create a dump of the compiler input.
+ // TODO(b/309743298): Do not use compiler dump to handle splitting the compilation. This should
+ // be all in memory.
+ ApplicationReader applicationReader = new ApplicationReader(app, options, timing);
+ applicationReader.dump(
+ new DumpInputFlags() {
+
+ @Override
+ public Path getDumpPath() {
+ return dumpFile;
+ }
+
+ @Override
+ public boolean shouldDump(DumpOptions options) {
+ return true;
+ }
+
+ @Override
+ public boolean shouldFailCompilation() {
+ return false;
+ }
+
+ @Override
+ public boolean shouldLogDumpInfoMessage() {
+ return false;
+ }
+ });
+ CompilerDump dump = CompilerDump.fromArchive(dumpFile, tmp);
+ if (dump.getBuildProperties().hasMainDexKeepRules()
+ || dump.getBuildProperties().hasArtProfileProviders()
+ || dump.getBuildProperties().hasStartupProfileProviders()) {
+ throw options.reporter.fatalError(
+ "Split compilation does not support legacy multi-dex, baseline or startup profiles");
+ }
+
+ DexApplication dapp = applicationReader.read().toDirect();
+ AppInfoWithClassHierarchy appInfo =
+ AppInfoWithClassHierarchy.createForDesugaring(
+ AppInfo.createInitialAppInfo(dapp, GlobalSyntheticsStrategy.forNonSynthesizing()));
+
+ Predicate<String> isR8 = options.r8PartialCompilationOptions.isR8;
+ Set<String> d8classes = new HashSet<>();
+ appInfo
+ .classes()
+ .forEach(
+ clazz -> {
+ String key = clazz.toSourceString();
+ if (!d8classes.contains(key) && !isR8.test(key)) {
+ d8classes.add(key);
+ // TODO(b/309743298): Improve this to only visit each class once and stop at
+ // library boundary.
+ appInfo.forEachSuperType(
+ clazz,
+ (superType, subclass, ignored) -> {
+ DexProgramClass superClass =
+ asProgramClassOrNull(appInfo.definitionFor(superType));
+ if (superClass != null) {
+ d8classes.add(superClass.toSourceString());
+ }
+ });
+ }
+ });
+
+ // Filter the program input into the D8 and R8 parts.
+ Set<String> d8ZipEntries =
+ d8classes.stream()
+ .map(name -> name.replace('.', '/') + ".class")
+ .collect(Collectors.toSet());
+ ZipBuilder d8ProgramBuilder = ZipBuilder.builder(tmp.resolve("d8-program.jar"));
+ ZipBuilder r8ProgramBuilder = ZipBuilder.builder(tmp.resolve("r8-program.jar"));
+ ZipUtils.iter(
+ dump.getProgramArchive(),
+ (entry, input) -> {
+ if (d8ZipEntries.contains(entry.getName())) {
+ d8ProgramBuilder.addBytes(entry.getName(), ByteStreams.toByteArray(input));
+ } else {
+ r8ProgramBuilder.addBytes(entry.getName(), ByteStreams.toByteArray(input));
+ }
+ });
+ Path d8Program = d8ProgramBuilder.build();
+ Path r8Program = r8ProgramBuilder.build();
+
+ // Compile D8 input with D8.
+ Path d8Output = tmp.resolve("d8-output.zip");
+ D8Command.Builder d8Builder =
+ D8Command.builder()
+ .setMinApiLevel(dump.getBuildProperties().getMinApi())
+ .addLibraryFiles(dump.getLibraryArchive())
+ .addClasspathFiles(dump.getClasspathArchive())
+ .addClasspathFiles(r8Program)
+ .addProgramFiles(d8Program)
+ .setMode(dump.getBuildProperties().getCompilationMode())
+ .setOutput(d8Output, OutputMode.DexIndexed);
+ if (dump.hasDesugaredLibrary()) {
+ d8Builder.addDesugaredLibraryConfiguration(
+ Files.readString(dump.getDesugaredLibraryFile(), UTF_8));
+ }
+ d8Builder.validate();
+ D8Command d8command = d8Builder.makeCommand();
+ AndroidApp d8App = d8command.getInputApp();
+ InternalOptions d8Options = d8command.getInternalOptions();
+ assert d8Options.getMinApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.N)
+ : "Default interface methods not yet supported";
+ D8.runInternal(d8App, d8Options, executor);
+
+ // Run trace references to produce keep rules for the D8 compiled part.
+ // TODO(b/309743298): Do not emit keep rules into a file.
+ Path traceReferencesRules = tmp.resolve("tr.rules");
+ TraceReferencesKeepRules keepRulesConsumer =
+ TraceReferencesKeepRules.builder()
+ .setOutputConsumer(new FileConsumer(traceReferencesRules))
+ .build();
+ TraceReferencesCommand.Builder trBuilder =
+ TraceReferencesCommand.builder()
+ .setConsumer(keepRulesConsumer)
+ .addLibraryFiles(dump.getLibraryArchive())
+ .addTargetFiles(r8Program)
+ .addSourceFiles(d8Program);
+ TraceReferencesCommand tr = TraceReferencesBridge.makeCommand(trBuilder);
+ TraceReferencesBridge.runInternal(tr);
+
+ // Compile R8 input with R8 using the keep rules from trace references.
+ Path r8Output = tmp.resolve("r8-output.zip");
+ R8Command.Builder r8Builder =
+ R8Command.builder()
+ .setMinApiLevel(dump.getBuildProperties().getMinApi())
+ .addLibraryFiles(dump.getLibraryArchive())
+ .addClasspathFiles(dump.getClasspathArchive())
+ .addClasspathFiles(d8Program)
+ .addProgramFiles(r8Program)
+ .addProguardConfigurationFiles(dump.getProguardConfigFile(), traceReferencesRules)
+ .setEnableEmptyMemberRulesToDefaultInitRuleConversion(true)
+ .setMode(dump.getBuildProperties().getCompilationMode())
+ .setOutput(r8Output, OutputMode.DexIndexed);
+ if (dump.hasDesugaredLibrary()) {
+ r8Builder.addDesugaredLibraryConfiguration(
+ Files.readString(dump.getDesugaredLibraryFile(), UTF_8));
+ }
+ R8Command r8Command = r8Builder.makeCommand();
+ AndroidApp r8App = r8Command.getInputApp();
+ InternalOptions r8Options = r8Command.getInternalOptions();
+ r8Options.mapConsumer = originalMapConsumer;
+ r8Options.quiet = true; // Don't write the R8 version.
+ R8.runInternal(r8App, r8Options, executor);
+
+ // Emit resources and merged DEX to the output consumer.
+ // TODO(b/309743298): Consider passing the DataResourceConsumer to the R8 invocation above.
+ DataResourceConsumer dataResourceConsumer = originalProgramConsumer.getDataResourceConsumer();
+ if (dataResourceConsumer != null) {
+ ZipUtils.iterWithZipFile(
+ r8Output,
+ (zip, entry) -> {
+ if (entry.getName().endsWith(FileUtils.DEX_EXTENSION)) {
+ return;
+ }
+ dataResourceConsumer.accept(
+ DataEntryResource.fromZip(zip, entry), new DiagnosticsHandler() {});
+ });
+ }
+ // TODO(b/309743298): Handle jumbo string rewriting with PCs in mapping file.
+ D8Command.Builder mergerBuilder =
+ D8Command.builder()
+ .setMinApiLevel(dump.getBuildProperties().getMinApi())
+ .addLibraryFiles(dump.getLibraryArchive())
+ .addClasspathFiles(dump.getClasspathArchive())
+ .addProgramFiles(d8Output, r8Output)
+ .setMode(dump.getBuildProperties().getCompilationMode())
+ .setProgramConsumer(originalProgramConsumer);
+ mergerBuilder.validate();
+ D8Command mergeCommand = mergerBuilder.makeCommand();
+ AndroidApp mergeApp = mergeCommand.getInputApp();
+ InternalOptions mergeOptions = mergeCommand.getInternalOptions();
+ D8.runInternal(mergeApp, mergeOptions, executor);
+ timing.end();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
index 5e31c47..8890de3 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationReader.java
@@ -157,6 +157,11 @@
return builder.build();
}
+ public final void dump(DumpInputFlags dumpInputFlags) {
+ assert verifyMainDexOptionsCompatible(inputApp, options);
+ dumpApplication(dumpInputFlags);
+ }
+
private void dumpApplication(DumpInputFlags dumpInputFlags) {
DumpOptions dumpOptions = options.dumpOptions;
if (dumpOptions == null || !dumpInputFlags.shouldDump(dumpOptions)) {
diff --git a/src/test/testbase/java/com/android/tools/r8/dump/CompilerDump.java b/src/main/java/com/android/tools/r8/dump/CompilerDump.java
similarity index 100%
rename from src/test/testbase/java/com/android/tools/r8/dump/CompilerDump.java
rename to src/main/java/com/android/tools/r8/dump/CompilerDump.java
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 85215e5..2a21f45 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
@@ -330,6 +330,18 @@
return null;
}
+ // Package-private service types are allowed only when the load() call is made from the same
+ // package. Not worth modelling.
+ DexClass serviceTypeResolved = appView.definitionFor(serviceType);
+ if (serviceTypeResolved == null) {
+ report(code.context(), serviceType, "Service type could not be resolved");
+ return null;
+ }
+ if (!serviceTypeResolved.isPublic()) {
+ report(code.context(), serviceType, "Service type must be public");
+ return null;
+ }
+
// Check that ClassLoader used is the ClassLoader defined for the service configuration
// that we are instantiating or NULL.
Value classLoaderValue = invokeInstr.getLastArgument().getAliasedValue();
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingHelper.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingHelper.java
index a2d0f00..649b64f 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingHelper.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingHelper.java
@@ -161,6 +161,9 @@
// `original.holder` on all API levels, in which case it is not OK to rebind to the resolved
// method.
return resolvedMethod.isLibraryMethod()
+ // Only rebind to protected methods in library when generating DEX. Dalvik/ART does not
+ // implement the full instruction verification.
+ && (options.isGeneratingDex() || !resolvedMethod.getAccessFlags().isProtected())
&& isAccessibleInAllContexts(resolvedMethod, resolutionResult, contexts)
&& !isInvokeSuperToInterfaceMethod(resolvedMethod, invokeType)
&& !isInvokeSuperToAbstractMethod(resolvedMethod, invokeType)
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysisBase.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysisBase.java
index 28d6b5c..6d28cbe 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysisBase.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/VirtualRootMethodsAnalysisBase.java
@@ -197,8 +197,8 @@
info.addOverride(existing);
return existing;
}
+ info.addSibling(existing);
if (existing.getMethod().getHolder().isInterface() && !info.isAbstract()) {
- info.addSibling(existing);
existing.addOverride(info);
return info;
}
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 401cd13..20db4fc 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -5506,6 +5506,18 @@
private void handleServiceInstantiation(
DexType serviceType, ProgramMethod context, KeepReason reason) {
+ DexProgramClass serviceClass = getProgramClassOrNullFromReflectiveAccess(serviceType, context);
+ if (serviceClass != null && !serviceClass.isPublic()) {
+ // Package-private service types are allowed only when the load() call is made from the same
+ // package.
+ applyMinimumKeepInfoWhenLive(
+ serviceClass,
+ KeepClassInfo.newEmptyJoiner()
+ .disallowHorizontalClassMerging()
+ .disallowVerticalClassMerging()
+ .disallowAccessModification());
+ }
+
List<DexType> serviceImplementationTypes =
appView.appServices().serviceImplementationsFor(serviceType);
for (DexType serviceImplementationType : serviceImplementationTypes) {
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepAnnotationCollectionInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepAnnotationCollectionInfo.java
index d1e1d5b..8cea4ff 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepAnnotationCollectionInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepAnnotationCollectionInfo.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.shaking.KeepInfoCollectionExported.KeepAnnotationCollectionInfoExported.createExported;
+
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.DexType;
@@ -310,6 +312,19 @@
}
return true;
}
+
+ @Override
+ public String toString() {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder
+ .append("IntermediateKeepAnnotationCollection{ ")
+ .append(anyTypeInfo.toString())
+ .append("; ");
+ specificTypeInfo.forEach(
+ (t, info) -> stringBuilder.append(t).append(": ").append(info.toString()));
+ stringBuilder.append("}");
+ return stringBuilder.toString();
+ }
}
public static Builder builder() {
@@ -328,6 +343,10 @@
return false;
}
+ public boolean isTopOrBottom() {
+ return isTop() || isBottom();
+ }
+
IntermediateKeepAnnotationCollectionInfo asIntermediate() {
throw new Unreachable();
}
@@ -368,6 +387,7 @@
// Info applicable to only specific types. Null if no type specific info is present.
private Map<DexType, KeepAnnotationInfo> specificTypeInfo = null;
+ private boolean export = false;
private Builder(KeepAnnotationInfo anyTypeInfo) {
assert anyTypeInfo != null;
@@ -375,6 +395,10 @@
this.anyTypeInfo = anyTypeInfo;
}
+ void setExport() {
+ this.export = true;
+ }
+
private static Builder createFrom(KeepAnnotationCollectionInfo original) {
if (original.isTop()) {
return createTop();
@@ -506,7 +530,9 @@
if (isBottom()) {
return BottomKeepAnnotationCollectionInfo.getInstance();
}
- return new IntermediateKeepAnnotationCollectionInfo(anyTypeInfo, specificTypeInfo);
+ IntermediateKeepAnnotationCollectionInfo intermediate =
+ new IntermediateKeepAnnotationCollectionInfo(anyTypeInfo, specificTypeInfo);
+ return export ? createExported(intermediate) : intermediate;
}
}
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
index 985559d..308e29c 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepClassInfo.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.InternalOptions;
+import java.util.List;
import java.util.function.Function;
/** Immutable keep requirements for a class. */
@@ -214,6 +215,20 @@
return hash;
}
+ @Override
+ public List<String> lines() {
+ List<String> lines = super.lines();
+ lines.add("allowClassInlining: " + allowClassInlining);
+ lines.add("allowHorizontalClassMerging: " + allowHorizontalClassMerging);
+ lines.add("allowPermittedSubclassesRemoval: " + allowPermittedSubclassesRemoval);
+ lines.add("allowRepackaging: " + allowRepackaging);
+ lines.add("allowSyntheticSharing: " + allowSyntheticSharing);
+ lines.add("allowUnusedInterfaceRemoval: " + allowUnusedInterfaceRemoval);
+ lines.add("allowVerticalClassMerging: " + allowVerticalClassMerging);
+ lines.add("checkEnumUnboxed: " + checkEnumUnboxed);
+ return lines;
+ }
+
public static class Builder extends KeepInfo.Builder<Builder, KeepClassInfo> {
private boolean allowClassInlining;
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
index 1c6cfa5..b31df54 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepFieldInfo.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import java.util.List;
+
/** Immutable keep requirements for a field. */
public final class KeepFieldInfo extends KeepMemberInfo<KeepFieldInfo.Builder, KeepFieldInfo> {
@@ -88,6 +90,14 @@
return hash;
}
+ @Override
+ public List<String> lines() {
+ List<String> lines = super.lines();
+ lines.add("allowFieldTypeStrengthening: " + allowFieldTypeStrengthening);
+ lines.add("allowRedundantFieldLoadElimination: " + allowRedundantFieldLoadElimination);
+ return lines;
+ }
+
public static class Builder extends KeepMemberInfo.Builder<Builder, KeepFieldInfo> {
private boolean allowFieldTypeStrengthening;
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 a085f21..50c76a6 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfo.java
@@ -13,7 +13,9 @@
import com.android.tools.r8.shaking.KeepReason.ReflectiveUseFrom;
import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.Sets;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
@@ -293,6 +295,8 @@
}
public boolean equalsNoAnnotations(K other) {
+ assert annotationsInfo.isTopOrBottom();
+ assert typeAnnotationsInfo.isTopOrBottom();
return getClass() == other.getClass()
&& (allowAccessModification == other.internalIsAccessModificationAllowed())
&& (allowAccessModificationForTesting
@@ -301,10 +305,14 @@
&& (allowOptimization == other.internalIsOptimizationAllowed())
&& (allowShrinking == other.internalIsShrinkingAllowed())
&& (allowSignatureRemoval == other.internalIsSignatureRemovalAllowed())
- && (checkDiscarded == other.internalIsCheckDiscardedEnabled());
+ && (checkDiscarded == other.internalIsCheckDiscardedEnabled())
+ && (annotationsInfo == other.internalAnnotationsInfo())
+ && (typeAnnotationsInfo == other.internalTypeAnnotationsInfo());
}
public int hashCodeNoAnnotations() {
+ assert annotationsInfo.isTopOrBottom();
+ assert typeAnnotationsInfo.isTopOrBottom();
int hash = 0;
int index = 0;
hash += bit(allowAccessModification, index++);
@@ -313,12 +321,28 @@
hash += bit(allowOptimization, index++);
hash += bit(allowShrinking, index++);
hash += bit(allowSignatureRemoval, index++);
- hash += bit(checkDiscarded, index);
+ hash += bit(checkDiscarded, index++);
+ hash += bit(annotationsInfo.isTop(), index++);
+ hash += bit(typeAnnotationsInfo.isTop(), index);
return hash;
}
+ public List<String> lines() {
+ List<String> lines = new ArrayList<>();
+ lines.add("allowAccessModification: " + allowAccessModification);
+ lines.add("allowAccessModificationForTesting: " + allowAccessModificationForTesting);
+ lines.add("allowMinification: " + allowMinification);
+ lines.add("allowOptimization: " + allowOptimization);
+ lines.add("allowShrinking: " + allowShrinking);
+ lines.add("allowSignatureRemoval: " + allowSignatureRemoval);
+ lines.add("checkDiscarded: " + checkDiscarded);
+ lines.add("annotationsInfo: " + annotationsInfo);
+ lines.add("typeAnnotationsInfo: " + typeAnnotationsInfo);
+ return lines;
+ }
+
protected int numberOfBooleans() {
- return 7;
+ return 9;
}
protected int bit(boolean bool, int index) {
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCanonicalizer.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCanonicalizer.java
index ada990b..356ef8c 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCanonicalizer.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCanonicalizer.java
@@ -43,13 +43,14 @@
private final Map<Equivalence.Wrapper<KeepFieldInfo>, Equivalence.Wrapper<KeepFieldInfo>>
keepFieldInfos = new LRUCache<>(CACHE_SIZE);
- private boolean hasKeepInfoAnnotationInfo(KeepInfo<?, ?> info) {
- return !info.internalAnnotationsInfo().isTop() || !info.internalTypeAnnotationsInfo().isTop();
+ private boolean hasNonTrivialKeepInfoAnnotationInfo(KeepInfo<?, ?> info) {
+ return !info.internalAnnotationsInfo().isTopOrBottom()
+ || !info.internalTypeAnnotationsInfo().isTopOrBottom();
}
@Override
public KeepClassInfo canonicalizeKeepClassInfo(KeepClassInfo classInfo) {
- if (hasKeepInfoAnnotationInfo(classInfo)) {
+ if (hasNonTrivialKeepInfoAnnotationInfo(classInfo)) {
return classInfo;
}
return keepClassInfos.computeIfAbsent(classEquivalence.wrap(classInfo), w -> w).get();
@@ -57,8 +58,8 @@
@Override
public KeepMethodInfo canonicalizeKeepMethodInfo(KeepMethodInfo methodInfo) {
- if (hasKeepInfoAnnotationInfo(methodInfo)
- || !methodInfo.internalParameterAnnotationsInfo().isTop()) {
+ if (hasNonTrivialKeepInfoAnnotationInfo(methodInfo)
+ || !methodInfo.internalParameterAnnotationsInfo().isTopOrBottom()) {
return methodInfo;
}
return keepMethodInfos.computeIfAbsent(methodEquivalence.wrap(methodInfo), w -> w).get();
@@ -66,7 +67,7 @@
@Override
public KeepFieldInfo canonicalizeKeepFieldInfo(KeepFieldInfo fieldInfo) {
- if (hasKeepInfoAnnotationInfo(fieldInfo)) {
+ if (hasNonTrivialKeepInfoAnnotationInfo(fieldInfo)) {
return fieldInfo;
}
return keepFieldInfos.computeIfAbsent(fieldEquivalence.wrap(fieldInfo), w -> w).get();
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 d89da04..3a4b7e4 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -264,6 +264,12 @@
InternalOptions options,
Timing timing);
+ public abstract KeepInfoCollectionExported exportToCollection();
+
+ public void exportToDirectory(Path directory) throws IOException {
+ exportToCollection().exportToDirectory(directory);
+ }
+
public abstract KeepInfoCollection mutate(Consumer<MutableKeepInfoCollection> mutator);
public abstract void writeToDirectory(Path directory) throws IOException;
@@ -398,6 +404,11 @@
return result;
}
+ @Override
+ public KeepInfoCollectionExported exportToCollection() {
+ return new KeepInfoCollectionExported(keepClassInfo, keepMethodInfo, keepFieldInfo);
+ }
+
private Map<DexType, KeepClassInfo> rewriteClassInfo(
NonIdentityGraphLens lens, InternalOptions options, Timing timing) {
timing.begin("Rewrite class info");
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollectionExported.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollectionExported.java
new file mode 100644
index 0000000..92aaad2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollectionExported.java
@@ -0,0 +1,203 @@
+// 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.shaking;
+
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexAnnotation;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.references.FieldReference;
+import com.android.tools.r8.references.MethodReference;
+import com.android.tools.r8.references.TypeReference;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class KeepInfoCollectionExported {
+
+ private final Map<TypeReference, ExportedClassInfo> classInfos;
+
+ static class ExportedClassInfo {
+
+ private final KeepClassInfo keepClassInfo;
+ private final Map<FieldReference, KeepFieldInfo> fieldInfos;
+ private final Map<MethodReference, KeepMethodInfo> methodInfos;
+
+ ExportedClassInfo(
+ KeepClassInfo keepClassInfo,
+ Map<FieldReference, KeepFieldInfo> fieldInfos,
+ Map<MethodReference, KeepMethodInfo> methodInfos) {
+ this.keepClassInfo = keepClassInfo;
+ this.fieldInfos = fieldInfos;
+ this.methodInfos = methodInfos;
+ }
+
+ public List<String> lines() {
+ List<String> lines = new ArrayList<>();
+ if (keepClassInfo != null) {
+ lines.add("class info");
+ lines.addAll(keepClassInfo.lines());
+ }
+ List<FieldReference> fieldRefs = new ArrayList<>(fieldInfos.keySet());
+ fieldRefs.sort(Comparator.comparing(FieldReference::toSourceString));
+ for (FieldReference fieldRef : fieldRefs) {
+ lines.add("field " + fieldRef.toSourceString());
+ lines.addAll(fieldInfos.get(fieldRef).lines());
+ }
+ List<MethodReference> methodRefs = new ArrayList<>(methodInfos.keySet());
+ methodRefs.sort(Comparator.comparing(MethodReference::toSourceString));
+ for (MethodReference methodRef : methodRefs) {
+ lines.add("method " + methodRef.toSourceString());
+ lines.addAll(methodInfos.get(methodRef).lines());
+ }
+ return lines;
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ static class Builder {
+
+ private KeepClassInfo keepClassInfo;
+ private final Map<FieldReference, KeepFieldInfo> fieldInfos = new HashMap<>();
+ private final Map<MethodReference, KeepMethodInfo> methodInfos = new HashMap<>();
+
+ public void setKeepClassInfo(KeepClassInfo keepClassInfo) {
+ this.keepClassInfo = keepClassInfo;
+ }
+
+ public void putFieldInfo(FieldReference fieldReference, KeepFieldInfo keepFieldInfo) {
+ fieldInfos.put(fieldReference, keepFieldInfo);
+ }
+
+ public void putMethodInfo(MethodReference methodReference, KeepMethodInfo keepMethodInfo) {
+ methodInfos.put(methodReference, keepMethodInfo);
+ }
+
+ public ExportedClassInfo build() {
+ return new ExportedClassInfo(
+ keepClassInfo, ImmutableMap.copyOf(fieldInfos), ImmutableMap.copyOf(methodInfos));
+ }
+ }
+ }
+
+ public KeepInfoCollectionExported(
+ Map<DexType, KeepClassInfo> keepClassInfo,
+ Map<DexMethod, KeepMethodInfo> keepMethodInfo,
+ Map<DexField, KeepFieldInfo> keepFieldInfo) {
+ Map<TypeReference, ExportedClassInfo.Builder> classInfosBuilder = new HashMap<>();
+ keepClassInfo.forEach(
+ (type, info) -> {
+ ExportedClassInfo.Builder builder = getBuilder(classInfosBuilder, type.asTypeReference());
+ builder.setKeepClassInfo(export(info));
+ });
+ keepMethodInfo.forEach(
+ (method, info) -> {
+ ExportedClassInfo.Builder builder =
+ getBuilder(classInfosBuilder, method.getHolderType().asTypeReference());
+ builder.putMethodInfo(method.asMethodReference(), export(info));
+ });
+ keepFieldInfo.forEach(
+ (field, info) -> {
+ ExportedClassInfo.Builder builder =
+ getBuilder(classInfosBuilder, field.getHolderType().asTypeReference());
+ builder.putFieldInfo(field.asFieldReference(), export(info));
+ });
+ ImmutableMap.Builder<TypeReference, ExportedClassInfo> mapBuilder = ImmutableMap.builder();
+ classInfosBuilder.forEach((typeRef, builder) -> mapBuilder.put(typeRef, builder.build()));
+ classInfos = mapBuilder.build();
+ }
+
+ private ExportedClassInfo.Builder getBuilder(
+ Map<TypeReference, ExportedClassInfo.Builder> classInfosBuilder,
+ TypeReference typeReference) {
+ return classInfosBuilder.computeIfAbsent(typeReference, t -> ExportedClassInfo.builder());
+ }
+
+ public void exportToDirectory(Path directory) throws IOException {
+ for (Entry<TypeReference, ExportedClassInfo> entry : classInfos.entrySet()) {
+ TypeReference typeReference = entry.getKey();
+ ExportedClassInfo exportedClassInfo = entry.getValue();
+ String binaryName =
+ DescriptorUtils.getClassBinaryNameFromDescriptor(typeReference.getDescriptor());
+ Files.createDirectories(directory.resolve(binaryName).getParent());
+ Files.write(
+ directory.resolve(binaryName), exportedClassInfo.lines(), StandardOpenOption.CREATE);
+ }
+ }
+
+ static boolean noAnnotation(KeepInfo<?, ?> info) {
+ return info.internalAnnotationsInfo().isTopOrBottom()
+ && info.internalTypeAnnotationsInfo().isTopOrBottom();
+ }
+
+ private KeepClassInfo export(KeepClassInfo classInfo) {
+ if (noAnnotation(classInfo)) {
+ return classInfo;
+ }
+ KeepClassInfo.Builder builder = classInfo.builder();
+ builder.getAnnotationsInfo().setExport();
+ builder.getTypeAnnotationsInfo().setExport();
+ return builder.build();
+ }
+
+ private KeepFieldInfo export(KeepFieldInfo fieldInfo) {
+ if (noAnnotation(fieldInfo)) {
+ return fieldInfo;
+ }
+ KeepFieldInfo.Builder builder = fieldInfo.builder();
+ builder.getAnnotationsInfo().setExport();
+ builder.getTypeAnnotationsInfo().setExport();
+ return builder.build();
+ }
+
+ private KeepMethodInfo export(KeepMethodInfo methodInfo) {
+ if (noAnnotation(methodInfo) && methodInfo.internalParameterAnnotationsInfo().isTopOrBottom()) {
+ return methodInfo;
+ }
+ KeepMethodInfo.Builder builder = methodInfo.builder();
+ builder.getAnnotationsInfo().setExport();
+ builder.getTypeAnnotationsInfo().setExport();
+ builder.getParameterAnnotationsInfo().setExport();
+ return builder.build();
+ }
+
+ public static class KeepAnnotationCollectionInfoExported extends KeepAnnotationCollectionInfo {
+
+ static KeepAnnotationCollectionInfo createExported(KeepAnnotationCollectionInfo from) {
+ if (from.isTopOrBottom()) {
+ return from;
+ }
+ return new KeepAnnotationCollectionInfoExported(from.toString());
+ }
+
+ private final String export;
+
+ KeepAnnotationCollectionInfoExported(String export) {
+ this.export = export;
+ }
+
+ @Override
+ public String toString() {
+ return export;
+ }
+
+ @Override
+ public boolean isRemovalAllowed(DexAnnotation annotation) {
+ throw new Unreachable();
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
index 887dff9..5e2f8d4 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMemberInfo.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.ProgramMember;
import com.android.tools.r8.shaking.KeepMemberInfo.Builder;
import com.android.tools.r8.utils.InternalOptions;
+import java.util.List;
/** Immutable keep requirements for a member. */
@SuppressWarnings("BadImport")
@@ -105,6 +106,13 @@
}
@Override
+ public List<String> lines() {
+ List<String> lines = super.lines();
+ lines.add("allowValuePropagation: " + allowValuePropagation);
+ return lines;
+ }
+
+ @Override
protected int numberOfBooleans() {
return super.numberOfBooleans() + 1;
}
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 0711d8a..f121490 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.DexAnnotation;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.shaking.KeepAnnotationCollectionInfo.RetentionInfo;
+import java.util.List;
/** Immutable keep requirements for a method. */
public class KeepMethodInfo extends KeepMemberInfo<KeepMethodInfo.Builder, KeepMethodInfo> {
@@ -282,6 +283,7 @@
@Override
public boolean equalsNoAnnotations(KeepMethodInfo other) {
+ assert parameterAnnotationsInfo.isTopOrBottom();
return super.equalsNoAnnotations(other)
&& allowThrowsRemoval == other.internalIsThrowsRemovalAllowed()
&& allowClassInlining == other.internalIsClassInliningAllowed()
@@ -300,11 +302,13 @@
&& allowUnusedArgumentOptimization == other.internalIsUnusedArgumentOptimizationAllowed()
&& allowUnusedReturnValueOptimization
== other.internalIsUnusedReturnValueOptimizationAllowed()
- && allowParameterNamesRemoval == other.internalIsParameterNamesRemovalAllowed();
+ && allowParameterNamesRemoval == other.internalIsParameterNamesRemovalAllowed()
+ && parameterAnnotationsInfo == other.internalParameterAnnotationsInfo();
}
@Override
public int hashCodeNoAnnotations() {
+ assert parameterAnnotationsInfo.isTopOrBottom();
int hash = super.hashCodeNoAnnotations();
int index = super.numberOfBooleans();
hash += bit(allowThrowsRemoval, index++);
@@ -322,10 +326,34 @@
hash += bit(allowSingleCallerInlining, index++);
hash += bit(allowUnusedArgumentOptimization, index++);
hash += bit(allowUnusedReturnValueOptimization, index++);
- hash += bit(allowParameterNamesRemoval, index);
+ hash += bit(allowParameterNamesRemoval, index++);
+ hash += bit(parameterAnnotationsInfo.isTop(), index);
return hash;
}
+ @Override
+ public List<String> lines() {
+ List<String> lines = super.lines();
+ lines.add("allowThrowsRemoval: " + allowThrowsRemoval);
+ lines.add("allowClassInlining: " + allowClassInlining);
+ lines.add("allowClosedWorldReasoning: " + allowClosedWorldReasoning);
+ lines.add("allowCodeReplacement: " + allowCodeReplacement);
+ lines.add("allowConstantArgumentOptimization: " + allowConstantArgumentOptimization);
+ lines.add("allowInlining: " + allowInlining);
+ lines.add("allowMethodStaticizing: " + allowMethodStaticizing);
+ lines.add("allowParameterRemoval: " + allowParameterRemoval);
+ lines.add("allowParameterReordering: " + allowParameterReordering);
+ lines.add("allowParameterTypeStrengthening: " + allowParameterTypeStrengthening);
+ lines.add("allowReprocessing: " + allowReprocessing);
+ lines.add("allowReturnTypeStrengthening: " + allowReturnTypeStrengthening);
+ lines.add("allowSingleCallerInlining: " + allowSingleCallerInlining);
+ lines.add("allowUnusedArgumentOptimization: " + allowUnusedArgumentOptimization);
+ lines.add("allowUnusedReturnValueOptimization: " + allowUnusedReturnValueOptimization);
+ lines.add("allowParameterNamesRemoval: " + allowParameterNamesRemoval);
+ lines.add("parameterAnnotationsInfo: " + parameterAnnotationsInfo);
+ return lines;
+ }
+
public static class Builder extends KeepMemberInfo.Builder<Builder, KeepMethodInfo> {
private boolean allowThrowsRemoval;
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
index 50a016c..c186bb9 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
@@ -67,7 +67,7 @@
command.getReporter(), () -> runInternal(command, options));
}
- private static void runInternal(TraceReferencesCommand command, InternalOptions options)
+ static void runInternal(TraceReferencesCommand command, InternalOptions options)
throws IOException, ResourceException {
AndroidApp.Builder builder = AndroidApp.builder();
command.getLibrary().forEach(builder::addLibraryResourceProvider);
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesBridge.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesBridge.java
new file mode 100644
index 0000000..5c0b302
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesBridge.java
@@ -0,0 +1,20 @@
+// 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.tracereferences;
+
+import com.android.tools.r8.ResourceException;
+import java.io.IOException;
+
+// Provide access to some package private APIs.
+public class TraceReferencesBridge {
+
+ public static TraceReferencesCommand makeCommand(TraceReferencesCommand.Builder builder) {
+ return builder.makeCommand();
+ }
+
+ public static void runInternal(TraceReferencesCommand command)
+ throws IOException, ResourceException {
+ TraceReferences.runInternal(command, command.getInternalOptions());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
index 28deeb3..429b912 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferencesCommand.java
@@ -337,7 +337,7 @@
return this;
}
- private TraceReferencesCommand makeCommand() {
+ TraceReferencesCommand makeCommand() {
if (isPrintHelp() || isPrintVersion()) {
return new TraceReferencesCommand(isPrintHelp(), isPrintVersion());
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApp.java b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
index 9ce000f..6672f02 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApp.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApp.java
@@ -806,7 +806,13 @@
ZipOutputStream dexArchiveOutputStream,
ProgramResource programResource)
throws ResourceException, IOException {
- byte[] bytes = StreamUtils.streamToByteArrayClose(programResource.getByteStream());
+ byte[] bytes;
+ if (programResource instanceof OneShotByteResource) {
+ OneShotByteResource oneShotByteResource = (OneShotByteResource) programResource;
+ bytes = oneShotByteResource.getBytesForDumpInput();
+ } else {
+ bytes = StreamUtils.streamToByteArrayClose(programResource.getByteStream());
+ }
if (programResource.getKind() == Kind.CF) {
Set<String> classDescriptors = programResource.getClassDescriptors();
String classDescriptor =
@@ -1424,6 +1430,14 @@
}
}
+ public List<ClassFileResourceProvider> getLibraryResourceProviders() {
+ return libraryResourceProviders;
+ }
+
+ public List<ClassFileResourceProvider> getClasspathResourceProviders() {
+ return classpathResourceProviders;
+ }
+
public List<ProgramResourceProvider> getProgramResourceProviders() {
ensureAllResourcesAreInProviders();
return programResourceProviders;
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpBase.java b/src/main/java/com/android/tools/r8/utils/CompileDumpBase.java
index f6c7269..a5aadef 100644
--- a/src/main/java/com/android/tools/r8/utils/CompileDumpBase.java
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpBase.java
@@ -34,6 +34,22 @@
.accept(new Object[] {isolatedSplits});
}
+ static void setupResourceShrinking(
+ Path androidResourcesInput, Path androidResourcesOutput, Object builder) {
+ try {
+ Class<?> androidResourceProvider =
+ Class.forName("com.android.tools.r8.AndroidResourceProvider");
+ Class<?> androidResourceConsumer =
+ Class.forName("com.android.tools.r8.AndroidResourceConsumer");
+ getReflectiveBuilderMethod(builder, "setAndroidResourceProvider", androidResourceProvider)
+ .accept(new Object[] {createAndroidResourceProvider(androidResourcesInput)});
+ getReflectiveBuilderMethod(builder, "setAndroidResourceConsumer", androidResourceConsumer)
+ .accept(new Object[] {createAndroidResourceConsumer(androidResourcesOutput)});
+ } catch (ClassNotFoundException e) {
+ // Ignore
+ }
+ }
+
static void addArtProfilesForRewriting(Object builder, Map<Path, Path> artProfileFiles) {
try {
Class<?> artProfileProviderClass =
@@ -62,38 +78,44 @@
.accept(new Object[] {createStartupProfileProviders(startupProfileFiles)});
}
- static Object createArtProfileProvider(Path artProfile) {
- Object[] artProfileProvider = new Object[1];
+ static Object callReflectiveDumpUtilsMethodWithPath(Path path, String method) {
+ Object[] returnObject = new Object[1];
boolean found =
callReflectiveUtilsMethod(
- "createArtProfileProviderFromDumpFile",
+ method,
new Class<?>[] {Path.class},
- fn -> artProfileProvider[0] = fn.apply(new Object[] {artProfile}));
+ fn -> returnObject[0] = fn.apply(new Object[] {path}));
if (!found) {
System.out.println(
- "Unable to add art profiles as input. "
- + "Method createArtProfileProviderFromDumpFile() was not found.");
+ "Unable to call invoke method on path "
+ + path
+ + ". "
+ + "Method "
+ + method
+ + "() was not found.");
return null;
}
- System.out.println(artProfileProvider[0]);
- return artProfileProvider[0];
+ return returnObject[0];
+ }
+
+ static Object createAndroidResourceProvider(Path resourceInput) {
+ return callReflectiveDumpUtilsMethodWithPath(
+ resourceInput, "createAndroidResourceProviderFromDumpFile");
+ }
+
+ static Object createAndroidResourceConsumer(Path resourceOutput) {
+ return callReflectiveDumpUtilsMethodWithPath(
+ resourceOutput, "createAndroidResourceConsumerFromDumpFile");
+ }
+
+ static Object createArtProfileProvider(Path artProfile) {
+ return callReflectiveDumpUtilsMethodWithPath(
+ artProfile, "createArtProfileProviderFromDumpFile");
}
static Object createResidualArtProfileConsumer(Path residualArtProfile) {
- Object[] residualArtProfileConsumer = new Object[1];
- boolean found =
- callReflectiveUtilsMethod(
- "createResidualArtProfileConsumerFromDumpFile",
- new Class<?>[] {Path.class},
- fn -> residualArtProfileConsumer[0] = fn.apply(new Object[] {residualArtProfile}));
- if (!found) {
- System.out.println(
- "Unable to add art profiles as input. "
- + "Method createResidualArtProfileConsumerFromDumpFile() was not found.");
- return null;
- }
- System.out.println(residualArtProfileConsumer[0]);
- return residualArtProfileConsumer[0];
+ return callReflectiveDumpUtilsMethodWithPath(
+ residualArtProfile, "createResidualArtProfileConsumerFromDumpFile");
}
static Collection<Object> createStartupProfileProviders(List<Path> startupProfileFiles) {
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
index cd01544..9112b77 100644
--- a/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpCompatR8.java
@@ -64,7 +64,7 @@
"--startup-profile");
private static final List<String> VALID_OPTIONS_WITH_TWO_OPERANDS =
- Arrays.asList("--art-profile", "--feature-jar");
+ Arrays.asList("--art-profile", "--feature-jar", "--android-resources");
private static boolean FileUtils_isArchive(Path path) {
String name = path.getFileName().toString().toLowerCase(Locale.ROOT);
@@ -91,6 +91,8 @@
List<Path> mainDexRulesFiles = new ArrayList<>();
Map<Path, Path> artProfileFiles = new LinkedHashMap<>();
List<Path> startupProfileFiles = new ArrayList<>();
+ Path androidResourcesInput = null;
+ Path androidResourcesOutput = null;
int minApi = 1;
int threads = -1;
boolean enableMissingLibraryApiModeling = false;
@@ -202,6 +204,12 @@
artProfileFiles.put(Paths.get(firstOperand), Paths.get(secondOperand));
break;
}
+ case "--android-resources":
+ {
+ androidResourcesInput = Paths.get(firstOperand);
+ androidResourcesOutput = Paths.get(secondOperand);
+ break;
+ }
case "--feature-jar":
{
Path featureIn = Paths.get(firstOperand);
@@ -237,6 +245,9 @@
if (desugaredLibJson != null) {
commandBuilder.addDesugaredLibraryConfiguration(readAllBytesJava7(desugaredLibJson));
}
+ if (androidResourcesInput != null) {
+ setupResourceShrinking(androidResourcesInput, androidResourcesOutput, commandBuilder);
+ }
if (desugaredLibKeepRuleConsumer != null) {
commandBuilder.setDesugaredLibraryKeepRuleConsumer(desugaredLibKeepRuleConsumer);
}
diff --git a/src/main/java/com/android/tools/r8/utils/CompileDumpUtils.java b/src/main/java/com/android/tools/r8/utils/CompileDumpUtils.java
index 4356671..ef013f6 100644
--- a/src/main/java/com/android/tools/r8/utils/CompileDumpUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/CompileDumpUtils.java
@@ -4,6 +4,10 @@
package com.android.tools.r8.utils;
+import com.android.tools.r8.AndroidResourceConsumer;
+import com.android.tools.r8.AndroidResourceProvider;
+import com.android.tools.r8.ArchiveProtoAndroidResourceConsumer;
+import com.android.tools.r8.ArchiveProtoAndroidResourceProvider;
import com.android.tools.r8.KeepMethodForCompileDump;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.origin.PathOrigin;
@@ -34,6 +38,16 @@
}
@KeepMethodForCompileDump
+ static AndroidResourceProvider createAndroidResourceProviderFromDumpFile(Path resourceInput) {
+ return new ArchiveProtoAndroidResourceProvider(resourceInput);
+ }
+
+ @KeepMethodForCompileDump
+ static AndroidResourceConsumer createAndroidResourceConsumerFromDumpFile(Path resourceOutput) {
+ return new ArchiveProtoAndroidResourceConsumer(resourceOutput);
+ }
+
+ @KeepMethodForCompileDump
static StartupProfileProvider createStartupProfileProviderFromDumpFile(Path path) {
return new StartupProfileProvider() {
diff --git a/src/main/java/com/android/tools/r8/utils/InternalArchiveClassFileProvider.java b/src/main/java/com/android/tools/r8/utils/InternalArchiveClassFileProvider.java
index 5c2eec5..7685a95 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalArchiveClassFileProvider.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalArchiveClassFileProvider.java
@@ -7,9 +7,13 @@
import static com.android.tools.r8.utils.FileUtils.isArchive;
import com.android.tools.r8.ClassFileResourceProvider;
+import com.android.tools.r8.DataDirectoryResource;
+import com.android.tools.r8.DataEntryResource;
+import com.android.tools.r8.DataResourceProvider;
import com.android.tools.r8.DiagnosticsHandler;
import com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.ResourceException;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.origin.ArchiveEntryOrigin;
import com.android.tools.r8.origin.Origin;
@@ -37,7 +41,8 @@
* the zip-file descriptor throughout compilation and close at the end of reading. It must also be
* safe to reopen it as currently our own tests reuse AndroidApp structures.
*/
-class InternalArchiveClassFileProvider implements ClassFileResourceProvider, AutoCloseable {
+class InternalArchiveClassFileProvider
+ implements ClassFileResourceProvider, DataResourceProvider, AutoCloseable {
private final Path path;
private final Origin origin;
private final Set<String> descriptors = new HashSet<>();
@@ -130,4 +135,30 @@
return getOpenZipFile()
.getEntry(descriptor.substring(1, descriptor.length() - 1) + CLASS_EXTENSION);
}
+
+ @Override
+ public DataResourceProvider getDataResourceProvider() {
+ return this;
+ }
+
+ @Override
+ public void accept(Visitor resourceBrowser) throws ResourceException {
+ try {
+ ZipUtils.iterWithZipFile(
+ path,
+ (zipFile, entry) -> {
+ if (!ZipUtils.isClassFile(entry.getName())) {
+ if (entry.isDirectory()) {
+ resourceBrowser.visit(DataDirectoryResource.fromZip(zipFile, entry));
+ } else {
+ resourceBrowser.visit(DataEntryResource.fromZip(zipFile, entry));
+ }
+ }
+ });
+ } catch (IOException e) {
+ throw new ResourceException(
+ origin,
+ new CompilationError("I/O exception while reading '" + path + "': " + e.getMessage(), e));
+ }
+ }
}
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 da0da31..7d9439b 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -114,11 +114,14 @@
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;
@@ -1019,6 +1022,9 @@
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 final TestingOptions testing = new TestingOptions();
public List<ProguardConfigurationRule> mainDexKeepRules = ImmutableList.of();
@@ -2276,6 +2282,39 @@
}
}
+ public static class R8PartialCompilationOptions {
+ public boolean enabled;
+ public Path tempDir = null;
+ public Predicate<String> isR8 = null;
+
+ 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/OneShotByteResource.java b/src/main/java/com/android/tools/r8/utils/OneShotByteResource.java
index 494d500..76be7da 100644
--- a/src/main/java/com/android/tools/r8/utils/OneShotByteResource.java
+++ b/src/main/java/com/android/tools/r8/utils/OneShotByteResource.java
@@ -54,6 +54,11 @@
return result;
}
+ public byte[] getBytesForDumpInput() {
+ assert bytes != null;
+ return bytes;
+ }
+
@Override
public Set<String> getClassDescriptors() {
return classDescriptors;
diff --git a/src/main/java/com/android/tools/r8/utils/ZipUtils.java b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
index 2b11a9f..fb33e96 100644
--- a/src/main/java/com/android/tools/r8/utils/ZipUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ZipUtils.java
@@ -89,18 +89,42 @@
iter(Paths.get(zipFileStr), handler);
}
- public static void iter(Path zipFilePath, OnEntryHandler handler) throws IOException {
+ public static void iter(Path zipFile, OnEntryHandler handler) throws IOException {
+ iterWithZipFileAndInputStream(zipFile, (zip, entry, input) -> handler.onEntry(entry, input));
+ }
+
+ public interface OnEntryHandlerWithZipFile {
+ void onEntry(ZipFile zip, ZipEntry entry) throws IOException;
+ }
+
+ public interface OnEntryHandlerWithZipFileAndInputStream {
+ void onEntry(ZipFile zip, ZipEntry entry, InputStream input) throws IOException;
+ }
+
+ public static void iterWithZipFileAndInputStream(
+ Path zipFilePath, OnEntryHandlerWithZipFileAndInputStream handler) throws IOException {
try (ZipFile zipFile = new ZipFile(zipFilePath.toFile(), StandardCharsets.UTF_8)) {
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
try (InputStream entryStream = zipFile.getInputStream(entry)) {
- handler.onEntry(entry, entryStream);
+ handler.onEntry(zipFile, entry, entryStream);
}
}
}
}
+ public static void iterWithZipFile(Path zipFilePath, OnEntryHandlerWithZipFile handler)
+ throws IOException {
+ try (ZipFile zipFile = new ZipFile(zipFilePath.toFile(), StandardCharsets.UTF_8)) {
+ final Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ handler.onEntry(zipFile, entry);
+ }
+ }
+ }
+
public static void iter(Path zipFilePath, Consumer<ZipEntry> entryConsumer) throws IOException {
try (ZipFile zipFile = new ZipFile(zipFilePath.toFile(), StandardCharsets.UTF_8)) {
final Enumeration<? extends ZipEntry> entries = zipFile.entries();
diff --git a/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java b/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java
index 875288c..fccc50d 100644
--- a/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java
+++ b/src/resourceshrinker/java/com/android/build/shrinker/r8integration/R8ResourceShrinkerState.java
@@ -57,7 +57,7 @@
private final List<Supplier<InputStream>> manifestProviders = new ArrayList<>();
private final Map<String, Supplier<InputStream>> resfileProviders = new HashMap<>();
- private final Map<ResourceTable, FeatureSplit> resourceTables = new HashMap<>();
+ private final Map<FeatureSplit, ResourceTable> resourceTables = new HashMap<>();
private ClassReferenceCallback enqueuerCallback;
private Map<Integer, List<String>> resourceIdToXmlFiles;
private Set<String> packageNames;
@@ -138,7 +138,7 @@
// feature.
if (packageNames == null) {
packageNames = new HashSet<>();
- for (ResourceTable resourceTable : resourceTables.keySet()) {
+ for (ResourceTable resourceTable : resourceTables.values()) {
for (Package aPackage : resourceTable.getPackageList()) {
packageNames.add(aPackage.getPackageName());
}
@@ -165,7 +165,7 @@
public void addResourceTable(InputStream inputStream, FeatureSplit featureSplit) {
this.resourceTables.put(
- r8ResourceShrinkerModel.instantiateFromResourceTable(inputStream, true), featureSplit);
+ featureSplit, r8ResourceShrinkerModel.instantiateFromResourceTable(inputStream, true));
}
public R8ResourceShrinkerModel getR8ResourceShrinkerModel() {
@@ -191,7 +191,7 @@
}
public void setupReferences() {
- for (ResourceTable resourceTable : resourceTables.keySet()) {
+ for (ResourceTable resourceTable : resourceTables.values()) {
new ProtoResourcesGraphBuilder(this::getXmlOrResFileBytes, unused -> resourceTable)
.buildGraph(r8ResourceShrinkerModel);
}
@@ -207,12 +207,11 @@
Map<FeatureSplit, ResourceTable> shrunkenTables = new IdentityHashMap<>();
resourceTables.forEach(
- (resourceTable, featureSplit) ->
- shrunkenTables.put(
- featureSplit,
- ResourceTableUtilKt.nullOutEntriesWithIds(
- resourceTable, resourceIdsToRemove, true)));
-
+ (featureSplit, resourceTable) -> {
+ shrunkenTables.put(
+ featureSplit,
+ ResourceTableUtilKt.nullOutEntriesWithIds(resourceTable, resourceIdsToRemove, true));
+ });
return new ShrinkerResult(resEntriesToKeep, shrunkenTables);
}
@@ -311,7 +310,7 @@
public Map<Integer, List<String>> getResourceIdToXmlFiles() {
if (resourceIdToXmlFiles == null) {
resourceIdToXmlFiles = new HashMap<>();
- for (ResourceTable resourceTable : resourceTables.keySet()) {
+ for (ResourceTable resourceTable : resourceTables.values()) {
for (Package packageEntry : resourceTable.getPackageList()) {
for (Resources.Type type : packageEntry.getTypeList()) {
for (Entry entry : type.getEntryList()) {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InstantiatedAndUninstantiatedClassMergingTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InstantiatedAndUninstantiatedClassMergingTest.java
index ac11021..1a2a5db 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InstantiatedAndUninstantiatedClassMergingTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InstantiatedAndUninstantiatedClassMergingTest.java
@@ -30,7 +30,7 @@
test(testForR8Compat(parameters.getBackend()));
}
- private <T extends R8TestBuilder<T>> void test(T testBuilder) throws Exception {
+ private <B extends R8TestBuilder<?, ?, ?>> void test(B testBuilder) throws Exception {
testBuilder
.addInnerClasses(getClass())
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
index b11215a..09c5556 100644
--- a/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
+++ b/src/test/java/com/android/tools/r8/internal/opensourceapps/TiviTest.java
@@ -63,7 +63,7 @@
.compile();
}
- private void configure(R8TestBuilder<?> testBuilder) {
+ private void configure(R8TestBuilder<?, ?, ?> testBuilder) {
testBuilder
.addClasspathFiles(outDirectory.resolve("classpath.jar"))
.addLibraryFiles(outDirectory.resolve("library.jar"))
diff --git a/src/test/java/com/android/tools/r8/internal/proto/ProtoRuntime.java b/src/test/java/com/android/tools/r8/internal/proto/ProtoRuntime.java
index a9dd2b2..3a12b3a 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/ProtoRuntime.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/ProtoRuntime.java
@@ -24,7 +24,7 @@
this.syntheticVersionNumber = syntheticVersionNumber;
}
- public void addRuntime(R8TestBuilder<?> testBuilder) {
+ public void addRuntime(R8TestBuilder<?, ?, ?> testBuilder) {
Path runtimeDir = Paths.get(ToolHelper.PROTO_RUNTIME_DIR, runtimeName);
testBuilder
.addProgramFiles(runtimeDir.resolve("libprotobuf_lite.jar"))
@@ -58,7 +58,7 @@
}
// The class com.google.protobuf.ProtoMessage is not present in newer proto lite runtimes.
- public void workaroundProtoMessageRemoval(R8TestBuilder<?> testBuilder) {
+ public void workaroundProtoMessageRemoval(R8TestBuilder<?, ?, ?> testBuilder) {
if (isNewerThanOrEqualTo(ProtoRuntime.EDITION2023)) {
testBuilder.addDontWarn("com.google.protobuf.ProtoMessage");
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/B369670481.java b/src/test/java/com/android/tools/r8/ir/optimize/B369670481.java
new file mode 100644
index 0000000..9eb062c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/B369670481.java
@@ -0,0 +1,82 @@
+// 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.ir.optimize;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import java.util.ArrayList;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+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 B369670481 extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), Main.class)
+ .assertFailureWithErrorThatThrows(ConcurrentModificationException.class);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters)
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .applyIf(
+ parameters.isCfRuntime(),
+ rr -> rr.assertFailureWithErrorThatThrows(ConcurrentModificationException.class),
+ rr -> rr.assertSuccessWithOutputLines("2"));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .setMinApi(parameters)
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .applyIf(
+ parameters.isCfRuntime(),
+ rr -> rr.assertFailureWithErrorThatThrows(ConcurrentModificationException.class),
+ rr -> rr.assertSuccessWithOutputLines("2"));
+ }
+
+ static class Main {
+
+ public static void main(String[] strArr) {
+ ArrayList<Integer> numbers = new ArrayList<>();
+ numbers.add(10);
+ numbers.add(20);
+ numbers.add(40);
+ Iterator<Integer> iterator = numbers.iterator();
+ while (iterator.hasNext()) {
+ if (iterator.next() == 40) {
+ numbers.remove(2);
+ }
+ }
+ System.out.println(numbers.size());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/B370217724Test.java b/src/test/java/com/android/tools/r8/ir/optimize/B370217724Test.java
new file mode 100644
index 0000000..2e862cb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/B370217724Test.java
@@ -0,0 +1,85 @@
+// 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.ir.optimize;
+
+import static org.hamcrest.CoreMatchers.anyOf;
+import static org.hamcrest.CoreMatchers.is;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.StringUtils;
+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 B370217724Test extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private static final String OUTPUT_JVM8 = StringUtils.lines("8989.358669383371");
+ private static final String OUTPUT_FROM_JVM9 = StringUtils.lines("3695.708516962155");
+ // Depending on the ART host run environment these results are seen.
+ private static final String OUTPUT_ART_1 = StringUtils.lines("5753.491198916323");
+ private static final String OUTPUT_ART_2 = StringUtils.lines("10192.673136265881");
+
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK9),
+ r -> r.assertSuccessWithOutput(OUTPUT_FROM_JVM9),
+ r -> r.assertSuccessWithOutput(OUTPUT_JVM8));
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutputThatMatches(anyOf(is(OUTPUT_ART_1), is(OUTPUT_ART_2)));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ parameters.isCfRuntime(),
+ r ->
+ r.assertSuccessWithOutput(
+ parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK9)
+ ? OUTPUT_FROM_JVM9
+ : OUTPUT_JVM8),
+ r -> r.assertSuccessWithOutputThatMatches(anyOf(is(OUTPUT_ART_1), is(OUTPUT_ART_2))));
+ }
+
+ static class TestClass {
+ public static void main(String[] strArr) {
+ double d = 8.65068;
+ for (int i = 1; i < 240; ++i) {
+ d = 6372.8 * Math.acos(Math.cos(d));
+ }
+ System.out.println(d);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/B370303498Test.java b/src/test/java/com/android/tools/r8/ir/optimize/B370303498Test.java
new file mode 100644
index 0000000..9578b71
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/B370303498Test.java
@@ -0,0 +1,67 @@
+// 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.ir.optimize;
+
+import com.android.tools.r8.SingleTestRunResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class B370303498Test extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertFailureWithErrorThatThrows(ArrayIndexOutOfBoundsException.class);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ // TODO(b/370303498): Should throw ArrayIndexOutOfBoundsException.
+ .assertSuccess();
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ parameters.isCfRuntime(),
+ r -> r.assertFailureWithErrorThatThrows(ArrayIndexOutOfBoundsException.class),
+ // TODO(b/370303498): Should throw ArrayIndexOutOfBoundsException.
+ SingleTestRunResult::assertSuccess);
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ java.lang.reflect.Array.set(new int[2], 2, new Object());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMappingOnSameLineTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMappingOnSameLineTest.java
index d26643f..f4a55f5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMappingOnSameLineTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/InlineMappingOnSameLineTest.java
@@ -47,7 +47,7 @@
}
@Override
- public void configure(R8TestBuilder<?> builder) {
+ public void configure(R8TestBuilder<?, ?, ?> builder) {
builder.enableInliningAnnotations();
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/B369739224Test.java b/src/test/java/com/android/tools/r8/ir/optimize/string/B369739224Test.java
new file mode 100644
index 0000000..615ba60
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/B369739224Test.java
@@ -0,0 +1,65 @@
+// 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.ir.optimize.string;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class B369739224Test extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertFailureWithErrorThatThrows(IndexOutOfBoundsException.class);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertFailureWithErrorThatThrows(IndexOutOfBoundsException.class);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ // TODO(b/369739224): Should throw IndexOutOfBoundsException.
+ .assertSuccessWithOutputLines("46");
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ String f = "";
+ int c = '.';
+ new StringBuilder().append(f, 0, c);
+ System.out.println(c);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/B370217723Test.java b/src/test/java/com/android/tools/r8/ir/optimize/string/B370217723Test.java
new file mode 100644
index 0000000..fb42612
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/B370217723Test.java
@@ -0,0 +1,63 @@
+// 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.ir.optimize.string;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+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 B370217723Test extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertFailureWithErrorThatThrows(NullPointerException.class);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ // TODO(b/370217723): Should throw NullPointerException.
+ .assertSuccess();
+ }
+
+ static class TestClass {
+ public static void main(String[] strArr) {
+ StringBuilder b = null;
+ b.append("0900");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/java_language/pattern_matching_for_instenceof/PattternMatchingForInstanceOfTest.java b/src/test/java/com/android/tools/r8/java_language/pattern_matching_for_instenceof/PattternMatchingForInstanceOfTest.java
index feb4819..135fc1f 100644
--- a/src/test/java/com/android/tools/r8/java_language/pattern_matching_for_instenceof/PattternMatchingForInstanceOfTest.java
+++ b/src/test/java/com/android/tools/r8/java_language/pattern_matching_for_instenceof/PattternMatchingForInstanceOfTest.java
@@ -60,7 +60,7 @@
@Test
public void testR8() throws Exception {
parameters.assumeR8TestParameters();
- R8TestBuilder<?> builder =
+ R8TestBuilder<?, ?, ?> builder =
testForR8(parameters.getBackend())
.addProgramFiles(JAR)
.setMinApi(parameters)
diff --git a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java
index cf7edc4..5a548bc 100644
--- a/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java
+++ b/src/test/java/com/android/tools/r8/keepanno/KeepAnnoTestBuilder.java
@@ -108,7 +108,8 @@
return this;
}
- public KeepAnnoTestBuilder applyIfR8Current(ThrowableConsumer<R8TestBuilder<?>> builderConsumer) {
+ public KeepAnnoTestBuilder applyIfR8Current(
+ ThrowableConsumer<R8TestBuilder<?, ?, ?>> builderConsumer) {
return this;
}
@@ -229,7 +230,7 @@
@Override
public KeepAnnoTestBuilder applyIfR8Current(
- ThrowableConsumer<R8TestBuilder<?>> builderConsumer) {
+ ThrowableConsumer<R8TestBuilder<?, ?, ?>> builderConsumer) {
builderConsumer.acceptWithRuntimeException(builder);
return this;
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
index 4014e78..41c2ec4 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinClassInlinerTest.java
@@ -9,7 +9,7 @@
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_6_0;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_1_9_21;
import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_2_0_20;
-import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLIN_DEV;
+import static com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion.KOTLINC_2_1_0_BETA1;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsentIf;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
@@ -223,7 +223,7 @@
kotlinParameters
.getCompiler()
.getCompilerVersion()
- .isEqualTo(KOTLIN_DEV),
+ .isGreaterThanOrEqualTo(KOTLINC_2_1_0_BETA1),
i -> {
ClassReference sequencesKt =
Reference.classFromTypeName(
diff --git a/src/test/java/com/android/tools/r8/kotlin/access/B369418242Test.java b/src/test/java/com/android/tools/r8/kotlin/access/B369418242Test.java
new file mode 100644
index 0000000..0d39931
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/access/B369418242Test.java
@@ -0,0 +1,126 @@
+// 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.kotlin.access;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.KotlinTestBase;
+import com.android.tools.r8.KotlinTestParameters;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.kotlin.access.b369418242.pkg.B;
+import com.android.tools.r8.kotlin.access.b369418242.pkg.Helper;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class B369418242Test extends KotlinTestBase {
+
+ private static final String PKG = B369418242Test.class.getPackage().getName() + ".b369418242";
+ private static final String KOTLIN_FILE = "main";
+ private static final String MAIN_CLASS = PKG + ".MainKt";
+
+ private static KotlinCompileMemoizer compiledJars;
+
+ private final TestParameters parameters;
+
+ @BeforeClass
+ public static void setup() throws Exception {
+ Path classpath = getStaticTemp().newFolder().toPath().resolve("out.jar");
+ ZipBuilder.builder(classpath)
+ .addFilesRelative(
+ ToolHelper.getClassPathForTests(),
+ ToolHelper.getClassFileForTestClass(Helper.getClassA()),
+ ToolHelper.getClassFileForTestClass(B.class))
+ .build();
+ compiledJars =
+ getCompileMemoizer(
+ Paths.get(
+ ToolHelper.TESTS_DIR,
+ "java",
+ DescriptorUtils.getBinaryNameFromJavaType(PKG),
+ KOTLIN_FILE + FileUtils.KT_EXTENSION))
+ .configure(x -> x.addClasspathFiles(classpath));
+ }
+
+ @Parameters(name = "{0}, {1}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(),
+ getKotlinTestParameters()
+ .withAllCompilers()
+ .withAllTargetVersions()
+ .withLambdaGenerationInvokeDynamic()
+ .build());
+ }
+
+ public B369418242Test(TestParameters parameters, KotlinTestParameters kotlinParameters) {
+ super(kotlinParameters);
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testKotlinExampleOnJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .addProgramFiles(compiledJars.getForConfiguration(kotlinParameters))
+ .addProgramFiles(kotlinc.getKotlinStdlibJar())
+ .addProgramClasses(Helper.getClassA(), B.class)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertFailureWithErrorThatThrows(IllegalAccessError.class);
+ }
+
+ @Test
+ public void testKotlinExampleWithD8() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8()
+ .addProgramFiles(compiledJars.getForConfiguration(kotlinParameters))
+ .addProgramFiles(kotlinc.getKotlinStdlibJar())
+ .addProgramClasses(Helper.getClassA(), B.class)
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertFailureWithErrorThatThrows(IllegalAccessError.class);
+ }
+
+ @Test
+ public void testKotlinExampleWithR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramFiles(compiledJars.getForConfiguration(kotlinParameters))
+ .addProgramFiles(kotlinc.getKotlinStdlibJar())
+ .addProgramClasses(Helper.getClassA(), B.class)
+ .addKeepMainRule(MAIN_CLASS)
+ .addDontWarn("org.jetbrains.annotations.NotNull")
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ // TODO(b/369418242): R8 should in principle preserve the IAE.
+ .assertSuccessWithEmptyOutput();
+ }
+
+ @Test
+ public void testJavaExampleOnJVM() throws Exception {
+ parameters.assumeJvmTestParameters();
+ assumeTrue(kotlinParameters.isFirst());
+ testForJvm(parameters)
+ .addProgramClasses(Main.class, Helper.getClassA(), B.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithEmptyOutput();
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ B child = new B();
+ child.doSomething(new B());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/access/b369418242/main.kt b/src/test/java/com/android/tools/r8/kotlin/access/b369418242/main.kt
new file mode 100644
index 0000000..cc3ea76
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/access/b369418242/main.kt
@@ -0,0 +1,14 @@
+/*
+ * 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.kotlin.access.b369418242
+
+import com.android.tools.r8.kotlin.access.b369418242.pkg.B
+
+fun main(args: Array<String>) {
+ // Kotlinc generates bytecode for `child.doSomething((A) B())`
+ val child = B()
+ child.doSomething(B())
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/access/b369418242/pkg/A.java b/src/test/java/com/android/tools/r8/kotlin/access/b369418242/pkg/A.java
new file mode 100644
index 0000000..d9a39ea
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/access/b369418242/pkg/A.java
@@ -0,0 +1,9 @@
+// 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.kotlin.access.b369418242.pkg;
+
+class A {
+
+ public void doSomething(A other) {}
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/access/b369418242/pkg/B.java b/src/test/java/com/android/tools/r8/kotlin/access/b369418242/pkg/B.java
new file mode 100644
index 0000000..ffa5079
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/access/b369418242/pkg/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.kotlin.access.b369418242.pkg;
+
+public class B extends A {}
diff --git a/src/test/java/com/android/tools/r8/kotlin/access/b369418242/pkg/Helper.java b/src/test/java/com/android/tools/r8/kotlin/access/b369418242/pkg/Helper.java
new file mode 100644
index 0000000..727b410
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/kotlin/access/b369418242/pkg/Helper.java
@@ -0,0 +1,11 @@
+// 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.kotlin.access.b369418242.pkg;
+
+public class Helper {
+
+ public static Class<?> getClassA() {
+ return A.class;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/libcore/B369739225Test.java b/src/test/java/com/android/tools/r8/libcore/B369739225Test.java
new file mode 100644
index 0000000..ee5fa8a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/libcore/B369739225Test.java
@@ -0,0 +1,92 @@
+// 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.libcore;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.utils.StringUtils;
+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 B369739225Test extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private static final String EXPECTED_OUTPUT_DALVIK_ART_JDK_UNTIL_11 =
+ StringUtils.lines("5", "5", "5", "5", "5");
+
+ private static final String EXPECTED_OUTPUT_JDK_FROM_17 =
+ StringUtils.lines("5", "10", "10", "10", "10");
+
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .addInnerClasses(getClass())
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ parameters.isCfRuntime() && parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK17),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT_JDK_FROM_17),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT_DALVIK_ART_JDK_UNTIL_11));
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT_DALVIK_ART_JDK_UNTIL_11);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ parameters.isCfRuntime() && parameters.asCfRuntime().isNewerThanOrEqual(CfVm.JDK17),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT_JDK_FROM_17),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT_DALVIK_ART_JDK_UNTIL_11));
+ }
+
+ static class TestClass {
+ long a;
+ long b;
+
+ void m() {
+ int k = 5;
+ k <<= a;
+ try {
+ String j = "sss";
+ java.io.LineNumberReader d =
+ new java.io.LineNumberReader(new java.io.CharArrayReader(j.toCharArray()));
+ d.skip(Long.MAX_VALUE);
+ a = d.getLineNumber();
+ } catch (Exception e) {
+ }
+ System.out.println(k);
+ }
+
+ public static void main(String[] n) {
+ TestClass test = new TestClass();
+ for (int i = 0; i < 5; i++) test.m();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/MemberRebindingProtectedInLibraryHierarchyTest.java b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/MemberRebindingProtectedInLibraryHierarchyTest.java
new file mode 100644
index 0000000..de3d6bb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/MemberRebindingProtectedInLibraryHierarchyTest.java
@@ -0,0 +1,74 @@
+// 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.memberrebinding.protectedaccess;
+
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.InvokeInstructionSubject;
+import java.util.LinkedHashMap;
+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;
+
+// Test for B/367915233.
+@RunWith(Parameterized.class)
+public class MemberRebindingProtectedInLibraryHierarchyTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!");
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT)
+ .inspect(
+ inspector ->
+ assertTrue(
+ inspector
+ .clazz(TestClass.class)
+ .mainMethod()
+ .streamInstructions()
+ .filter(InstructionSubject::isInvokeVirtual)
+ .map(
+ instruction -> ((InvokeInstructionSubject) instruction).invokedMethod())
+ .anyMatch(
+ method ->
+ (parameters.isCfRuntime()
+ ? method
+ .getHolderType()
+ .toSourceString()
+ .equals("java.util.HashMap")
+ : method
+ .getHolderType()
+ .toSourceString()
+ .equals("java.lang.Object"))
+ && method.getName().toString().equals("clone"))));
+ }
+
+ static class TestClass {
+
+ public static void main(String[] args) {
+ new LinkedHashMap<>().clone();
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/MemberRebindingProtectedInProgramHierachyInvalidInputTest.java b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/MemberRebindingProtectedInProgramHierachyInvalidInputTest.java
new file mode 100644
index 0000000..886da04
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/MemberRebindingProtectedInProgramHierachyInvalidInputTest.java
@@ -0,0 +1,88 @@
+// 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.memberrebinding.protectedaccess;
+
+import static org.hamcrest.CoreMatchers.containsString;
+
+import com.android.tools.r8.SingleTestRunResult;
+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.memberrebinding.protectedaccess.p1.ClassWithProtectedField;
+import com.android.tools.r8.memberrebinding.protectedaccess.p2.SubClassOfClassWithProtectedField;
+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 MemberRebindingProtectedInProgramHierachyInvalidInputTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void testJvm() throws Exception {
+ parameters.assumeJvmTestParameters();
+ testForJvm(parameters)
+ .addProgramClasses(SubClassOfClassWithProtectedField.class, TestClass.class)
+ .addProgramClassFileData(getTransformedClass())
+ .run(parameters.getRuntime(), TestClass.class)
+ .applyIf(
+ parameters.getCfRuntime().isNewerThanOrEqual(CfVm.JDK21),
+ r -> r.assertFailureWithErrorThatThrows(VerifyError.class),
+ parameters.getCfRuntime().getVm() == CfVm.JDK11,
+ SingleTestRunResult::assertFailure, // JDK-11 either crash or report VerifyError.
+ r -> r.assertFailureWithOutputThatMatches(containsString("SIGSEGV")));
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ parameters.assumeDexRuntime();
+ testForD8(parameters.getBackend())
+ .addProgramClasses(SubClassOfClassWithProtectedField.class, TestClass.class)
+ .addProgramClassFileData(getTransformedClass())
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ // The code runs fine on Dalvik/ART.
+ .assertSuccessWithOutputLines("1");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(SubClassOfClassWithProtectedField.class, TestClass.class)
+ .addProgramClassFileData(getTransformedClass())
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters)
+ .addDontObfuscate()
+ .run(parameters.getRuntime(), TestClass.class)
+ // The code runs fine after R8 processing on both JDK and Dalvik/ART.
+ .assertSuccessWithOutputLines("1");
+ }
+
+ private byte[] getTransformedClass() throws Exception {
+ return transformer(ClassWithProtectedField.class)
+ .setAccessFlags(
+ ClassWithProtectedField.class.getDeclaredField("f"),
+ accessFlags -> {
+ accessFlags.unsetPublic();
+ accessFlags.setProtected();
+ })
+ .transform();
+ }
+
+ static class TestClass {
+ public static void main(String[] args) {
+ new SubClassOfClassWithProtectedField(2).m(new ClassWithProtectedField(1));
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/MemberRebindingProtectedInProgramHierachyTest.java b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/MemberRebindingProtectedInProgramHierachyTest.java
new file mode 100644
index 0000000..962517f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/MemberRebindingProtectedInProgramHierachyTest.java
@@ -0,0 +1,58 @@
+// 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.memberrebinding.protectedaccess;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithHolderAndName;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.memberrebinding.protectedaccess.util.A;
+import com.android.tools.r8.memberrebinding.protectedaccess.util.B;
+import com.android.tools.r8.memberrebinding.protectedaccess.util.Base;
+import com.android.tools.r8.utils.StringUtils;
+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 MemberRebindingProtectedInProgramHierachyTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!");
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Base.class, A.class, B.class, TestClass.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassAndMembersRules(Base.class, A.class, B.class)
+ .setMinApi(parameters)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT)
+ .inspect(
+ inspector ->
+ assertThat(
+ inspector.clazz(TestClass.class).mainMethod(),
+ invokesMethodWithHolderAndName(A.class.getTypeName(), "m")));
+ }
+
+ static class TestClass extends Base {
+ public static void main(String[] args) {
+ // This will be rebound to A.m. If member rebinding is changed to rebind to higher in the
+ // hierarchy then this still cannot be rebound to Base.m (at least not for class file output).
+ new B().m();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/p1/ClassWithProtectedField.java b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/p1/ClassWithProtectedField.java
new file mode 100644
index 0000000..bc143c5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/p1/ClassWithProtectedField.java
@@ -0,0 +1,12 @@
+// 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.memberrebinding.protectedaccess.p1;
+
+public class ClassWithProtectedField {
+ public int f; // protected with transformer.
+
+ public ClassWithProtectedField(int f) {
+ this.f = f;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/p2/SubClassOfClassWithProtectedField.java b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/p2/SubClassOfClassWithProtectedField.java
new file mode 100644
index 0000000..da144a0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/p2/SubClassOfClassWithProtectedField.java
@@ -0,0 +1,16 @@
+// 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.memberrebinding.protectedaccess.p2;
+
+import com.android.tools.r8.memberrebinding.protectedaccess.p1.ClassWithProtectedField;
+
+public class SubClassOfClassWithProtectedField extends ClassWithProtectedField {
+ public SubClassOfClassWithProtectedField(int v) {
+ super(v);
+ }
+
+ public void m(ClassWithProtectedField instance) {
+ System.out.println(instance.f);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/util/A.java b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/util/A.java
new file mode 100644
index 0000000..7af2b36
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/util/A.java
@@ -0,0 +1,10 @@
+// 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.memberrebinding.protectedaccess.util;
+
+public class A extends Base {
+ public void m() {
+ super.m();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/util/B.java b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/util/B.java
new file mode 100644
index 0000000..9468783
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/util/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.memberrebinding.protectedaccess.util;
+
+public class B extends A {}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/util/Base.java b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/util/Base.java
new file mode 100644
index 0000000..9774c7e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/memberrebinding/protectedaccess/util/Base.java
@@ -0,0 +1,10 @@
+// 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.memberrebinding.protectedaccess.util;
+
+public class Base {
+ protected void m() {
+ System.out.println("Hello, world!");
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/naming/RenameSourceFileRetraceTest.java b/src/test/java/com/android/tools/r8/naming/RenameSourceFileRetraceTest.java
index b307545..6142fb7 100644
--- a/src/test/java/com/android/tools/r8/naming/RenameSourceFileRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/RenameSourceFileRetraceTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.R8TestBuilder;
+import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.graph.DexClass;
@@ -62,7 +63,7 @@
@Test
public void testR8()
throws ExecutionException, CompilationFailedException, IOException, NoSuchMethodException {
- R8TestBuilder<? extends R8TestBuilder<?>> r8TestBuilder =
+ R8TestBuilder<?, R8TestRunResult, ?> r8TestBuilder =
isCompat ? testForR8Compat(parameters.getBackend()) : testForR8(parameters.getBackend());
if (keepSourceFile) {
r8TestBuilder.addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE);
@@ -85,7 +86,7 @@
@Test
public void testRenameSourceFileR8()
throws ExecutionException, CompilationFailedException, IOException, NoSuchMethodException {
- R8TestBuilder<? extends R8TestBuilder<?>> r8TestBuilder =
+ R8TestBuilder<?, R8TestRunResult, ?> r8TestBuilder =
isCompat ? testForR8Compat(parameters.getBackend()) : testForR8(parameters.getBackend());
if (keepSourceFile) {
r8TestBuilder.addKeepAttributes(ProguardKeepAttributes.SOURCE_FILE);
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodDirectRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodDirectRetraceTest.java
index 2439b3b..758fd3e 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodDirectRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/DesugarStaticInterfaceMethodDirectRetraceTest.java
@@ -46,7 +46,7 @@
}
@Override
- public void configure(R8TestBuilder<?> builder) {
+ public void configure(R8TestBuilder<?, ?, ?> builder) {
builder.enableInliningAnnotations();
}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/RetraceTestBase.java b/src/test/java/com/android/tools/r8/naming/retrace/RetraceTestBase.java
index 21ea804..b82ab15 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/RetraceTestBase.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/RetraceTestBase.java
@@ -37,7 +37,7 @@
.assertFailure()
.map(StackTrace::extractFromJvm));
- public void configure(R8TestBuilder<?> builder) {}
+ public void configure(R8TestBuilder<?, ?, ?> builder) {}
public void inspect(CodeInspector inspector) {}
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java b/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
index ebf25d9..8bd3581 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/VerticalClassMergingRetraceTest.java
@@ -48,7 +48,7 @@
}
@Override
- public void configure(R8TestBuilder<?> builder) {
+ public void configure(R8TestBuilder<?, ?, ?> builder) {
builder
.addOptionsModification(
options ->
diff --git a/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java b/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
index f08c178..6a32a4c 100644
--- a/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
+++ b/src/test/java/com/android/tools/r8/naming/signature/GenericSignatureRenamingTest.java
@@ -81,7 +81,7 @@
internalOptions.testing.assertConsistentRenamingOfSignature = true));
}
- private void test(R8TestBuilder<?> builder) throws Exception {
+ private void test(R8TestBuilder<?, ?, ?> builder) throws Exception {
builder
.addKeepRules("-dontoptimize")
.addKeepAttributes(ProguardKeepAttributes.SIGNATURE)
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagationMultipleInterfacesSameMethodTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagationMultipleInterfacesSameMethodTest.java
index b38b518..20c556c 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagationMultipleInterfacesSameMethodTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagationMultipleInterfacesSameMethodTest.java
@@ -40,8 +40,7 @@
inspector
.assertHasPolymorphicMethodState(
Reference.methodFromMethod(I.class.getDeclaredMethod("m")))
- // TODO(b/363492038): J.m is not monomorphic.
- .assertHasMonomorphicMethodState(
+ .assertHasPolymorphicMethodState(
Reference.methodFromMethod(J.class.getDeclaredMethod("m")))
.apply(ignore -> inspected.set()))
.enableNoHorizontalClassMergingAnnotations()
diff --git a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderDesugaredLibraryTest.java b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderDesugaredLibraryTest.java
index a3bc5b4..22389b4 100644
--- a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderDesugaredLibraryTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderDesugaredLibraryTest.java
@@ -19,7 +19,6 @@
import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
import com.android.tools.r8.origin.Origin;
-import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
import com.google.common.collect.ImmutableList;
@@ -79,7 +78,7 @@
private static final String servicesFile =
StringUtils.lines(SimpleChronology.class.getTypeName());
- private void configureR8(R8TestBuilder<?> builder) {
+ private void configureR8(R8TestBuilder<?, ?, ?> builder) {
// When testing R8 add the META-INF/services to the input to apply rewriting.
builder
.addDataEntryResources(
diff --git a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderRewritingTest.java b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderRewritingTest.java
index 18f4a79..75d51ed 100644
--- a/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/serviceloader/ServiceLoaderRewritingTest.java
@@ -7,7 +7,6 @@
import static com.android.tools.r8.ToolHelper.DexVm.Version.V7_0_0;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assume.assumeTrue;
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverInline;
@@ -31,12 +30,16 @@
private final String EXPECTED_OUTPUT =
StringUtils.lines("Hello World!", "Hello World!", "Hello World!");
+ interface NonPublicService {
+ void print();
+ }
+
public interface Service {
void print();
}
- public static class ServiceImpl implements Service {
+ public static class ServiceImpl implements Service, NonPublicService {
@Override
public void print() {
@@ -150,6 +153,13 @@
}
}
+ public static class MainWithNonPublicService {
+
+ public static void main(String[] args) {
+ ServiceLoader.load(NonPublicService.class, null).iterator().next().print();
+ }
+ }
+
@Parameterized.Parameters(name = "{0}, enableRewriting: {1}")
public static List<Object[]> data() {
return buildParameters(
@@ -169,8 +179,11 @@
}
}
- private boolean isDexV7() {
- // Runtime uses boot classloader rather than system classloader on this version.
+ private boolean isAndroid7() {
+ // Runtime uses boot classloader rather than system classloader on this version. See b/130164528
+ // for more details.
+ // The CL that changed behaviour after Nougat is:
+ // https://android-review.googlesource.com/c/platform/libcore/+/273135
return parameters.isDexRuntime() && parameters.getDexRuntimeVersion() == V7_0_0;
}
@@ -192,7 +205,7 @@
.compile()
.run(parameters.getRuntime(), MainRunner.class)
.applyIf(
- isDexV7() && !enableRewriting,
+ isAndroid7() && !enableRewriting,
runResult ->
runResult.assertFailureWithErrorThatThrows(ServiceConfigurationError.class),
runResult ->
@@ -212,7 +225,7 @@
.compile()
.run(parameters.getRuntime(), MainRunner.class)
.applyIf(
- isDexV7() && !enableRewriting,
+ isAndroid7() && !enableRewriting,
runResult ->
runResult.assertFailureWithErrorThatThrows(ServiceConfigurationError.class),
runResult ->
@@ -288,7 +301,7 @@
.compileWithExpectedDiagnostics(expectedDiagnostics)
.run(parameters.getRuntime(), MainRunner.class)
.applyIf(
- !isDexV7(),
+ !isAndroid7(),
runResult -> runResult.assertSuccessWithOutput(EXPECTED_OUTPUT),
runResult ->
runResult.assertFailureWithErrorThatThrows(ServiceConfigurationError.class));
@@ -332,22 +345,39 @@
@Test
public void testKeepAsOriginal()
throws IOException, CompilationFailedException, ExecutionException {
- // The CL that changed behaviour after Nougat is:
- // https://android-review.googlesource.com/c/platform/libcore/+/273135
- assumeTrue(
- parameters.getRuntime().isCf()
- || !parameters.getRuntime().asDex().getVm().getVersion().equals(V7_0_0));
serviceLoaderTest(Service.class, ServiceImpl.class)
.addKeepMainRule(MainRunner.class)
.addKeepClassRules(Service.class)
.allowDiagnosticInfoMessages(enableRewriting)
.compileWithExpectedDiagnostics(expectedDiagnostics)
- .run(parameters.getRuntime(), MainRunner.class)
- .assertSuccessWithOutput(EXPECTED_OUTPUT)
.inspect(
inspector -> {
assertEquals(3, getServiceLoaderLoads(inspector));
verifyServiceMetaInf(inspector, Service.class, ServiceImpl.class);
- });
+ })
+ .run(parameters.getRuntime(), MainRunner.class)
+ .applyIf(
+ isAndroid7(),
+ r -> r.assertFailureWithErrorThatThrows(ServiceConfigurationError.class),
+ r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
+ }
+
+ @Test
+ public void testNonPublicService()
+ throws IOException, CompilationFailedException, ExecutionException {
+ serviceLoaderTest(NonPublicService.class, ServiceImpl.class)
+ .addKeepMainRule(MainWithNonPublicService.class)
+ .allowDiagnosticInfoMessages(enableRewriting)
+ .compileWithExpectedDiagnostics(expectedDiagnostics)
+ .inspect(
+ inspector -> {
+ assertEquals(1, getServiceLoaderLoads(inspector));
+ verifyServiceMetaInf(inspector, NonPublicService.class, ServiceImpl.class);
+ })
+ .run(parameters.getRuntime(), MainWithNonPublicService.class)
+ .applyIf(
+ isAndroid7(),
+ r -> r.assertFailureWithErrorThatThrows(ServiceConfigurationError.class),
+ r -> r.assertSuccessWithOutputLines("Hello World!"));
}
}
diff --git a/src/test/java/com/android/tools/r8/partial/ClassHierarchyInterleavedD8AndR8Test.java b/src/test/java/com/android/tools/r8/partial/ClassHierarchyInterleavedD8AndR8Test.java
new file mode 100644
index 0000000..db45859
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/ClassHierarchyInterleavedD8AndR8Test.java
@@ -0,0 +1,102 @@
+// 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.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndNotRenamed;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import 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.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import java.util.function.Predicate;
+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 ClassHierarchyInterleavedD8AndR8Test extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ // Test with min API level 24 where default interface methods are supported instead of using
+ // dump.getBuildProperties().getMinApi(). Tivi has min API 23 and there are currently trace
+ // references issues with CC classes.
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntime(DexVm.Version.V7_0_0)
+ .withApiLevel(AndroidApiLevel.N)
+ .build();
+ }
+
+ private void runTest(
+ Predicate<String> isR8, ThrowingConsumer<CodeInspector, RuntimeException> inspector)
+ throws Exception {
+ // Path tempDir = temp.newFolder().toPath();
+ testForR8Partial(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClasses(A.class, B.class, C.class, Main.class)
+ .addKeepMainRule(Main.class)
+ .setR8PartialConfigurationPredicate(isR8)
+ .compile()
+ .inspect(inspector)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithEmptyOutput();
+ }
+
+ @Test
+ public void testD8Top() throws Exception {
+ runTest(
+ name -> !name.equals(A.class.getTypeName()),
+ inspector -> {
+ assertThat(inspector.clazz(A.class), isPresentAndNotRenamed());
+ assertThat(inspector.clazz(B.class), isAbsent()); // Merged into C.
+ assertThat(inspector.clazz(C.class), isPresentAndRenamed());
+ });
+ }
+
+ @Test
+ public void testD8Middle() throws Exception {
+ runTest(
+ name -> !name.equals(B.class.getTypeName()),
+ inspector -> {
+ assertThat(inspector.clazz(A.class), isPresentAndNotRenamed());
+ assertThat(inspector.clazz(B.class), isPresentAndNotRenamed());
+ assertThat(inspector.clazz(C.class), isPresentAndRenamed());
+ });
+ }
+
+ @Test
+ public void testD8Bottom() throws Exception {
+ runTest(
+ name -> !name.equals(C.class.getTypeName()),
+ inspector -> {
+ assertThat(inspector.clazz(A.class), isPresentAndNotRenamed());
+ assertThat(inspector.clazz(B.class), isPresentAndNotRenamed());
+ assertThat(inspector.clazz(C.class), isPresentAndNotRenamed());
+ });
+ }
+
+ public static class A {}
+
+ public static class B extends A {}
+
+ public static class C extends B {}
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ new C();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicTest.java
new file mode 100644
index 0000000..662aa21
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationBasicTest.java
@@ -0,0 +1,93 @@
+// 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.utils.codeinspector.Matchers.isAbsent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.utils.AndroidApiLevel;
+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 PartialCompilationBasicTest extends TestBase {
+
+ @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();
+ }
+
+ @Test
+ public void runTestClassAIsCompiledWithD8() throws Exception {
+ testForR8Partial(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClasses(A.class, B.class, Main.class)
+ .addKeepMainRule(Main.class)
+ .setR8PartialConfiguration(builder -> builder.includeAll().excludeClasses(A.class).build())
+ .compile()
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz(A.class), isPresent());
+ assertThat(inspector.clazz(B.class), isAbsent());
+ })
+ .run(parameters.getRuntime(), Main.class, getClass().getTypeName())
+ .assertSuccessWithOutputLines("Instantiated", "Not instantiated");
+ }
+
+ @Test
+ public void runTestClassBIsCompiledWithD8() throws Exception {
+ testForR8Partial(parameters.getBackend())
+ .setMinApi(parameters)
+ .addProgramClasses(A.class, B.class, Main.class)
+ .addKeepMainRule(Main.class)
+ .setR8PartialConfiguration(builder -> builder.includeAll().excludeClasses(B.class).build())
+ .compile()
+ .inspect(
+ inspector -> {
+ assertThat(inspector.clazz(A.class), isAbsent());
+ assertThat(inspector.clazz(B.class), isPresent());
+ })
+ .run(parameters.getRuntime(), Main.class, getClass().getTypeName())
+ .assertSuccessWithOutputLines("Not instantiated", "Instantiated");
+ }
+
+ public static class A {}
+
+ public static class B {}
+
+ public static class Main {
+
+ public static void main(String[] args) throws Exception {
+ // Instantiate class A.
+ try {
+ Class.forName(new String(args[0] + "$" + new String(new byte[] {65})));
+ System.out.println("Instantiated");
+ } catch (ClassNotFoundException e) {
+ System.out.println("Not instantiated");
+ }
+ // Instantiate class B.
+ try {
+ Class.forName(new String(args[0] + "$" + new String(new byte[] {66})));
+ System.out.println("Instantiated");
+ } catch (ClassNotFoundException e) {
+ System.out.println("Not instantiated");
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/partial/PartialCompilationDemoTest.java b/src/test/java/com/android/tools/r8/partial/PartialCompilationDemoTest.java
new file mode 100644
index 0000000..0ccd8ce
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/partial/PartialCompilationDemoTest.java
@@ -0,0 +1,309 @@
+// 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.ToolHelper.DESUGARED_JDK_11_LIB_JAR;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.L8;
+import com.android.tools.r8.L8Command;
+import com.android.tools.r8.LibraryDesugaringTestConfiguration;
+import com.android.tools.r8.OutputMode;
+import com.android.tools.r8.StringConsumer.FileConsumer;
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.desugar.desugaredlibrary.jdk11.DesugaredLibraryJDK11Undesugarer;
+import com.android.tools.r8.dump.CompilerDump;
+import com.android.tools.r8.tracereferences.TraceReferences;
+import com.android.tools.r8.tracereferences.TraceReferencesCommand;
+import com.android.tools.r8.tracereferences.TraceReferencesKeepRules;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.ZipUtils;
+import com.google.common.collect.ImmutableMap;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.Predicate;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+// This test is currently for demonstrating R8 partial compilation, and for now all tests should
+// be @Ignore(d) and only enabled for local experiments.
+@RunWith(Parameterized.class)
+public class PartialCompilationDemoTest extends TestBase {
+
+ private static final Path TIVI_DUMP_PATH =
+ Paths.get(ToolHelper.THIRD_PARTY_DIR, "opensource-apps", "tivi", "dump_app.zip");
+ private static final Path NOWINANDROID_DUMP_PATH =
+ Paths.get(
+ ToolHelper.THIRD_PARTY_DIR, "opensource-apps", "android", "nowinandroid", "dump_app.zip");
+ // When using with the desugar_jdk_libs.jar in third_party (DESUGARED_JDK_11_LIB_JAR) for L8
+ // compilation then the configuration from the dump cannot be used for L8, as the configuration
+ // in the dump is the "machine specification" which only works with the specific version it was
+ // built for.
+ private static final boolean useDesugaredLibraryConfigurationFromDump = false;
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ // Test with min API level 24 where default interface methods are supported instead fo using
+ // dump.getBuildProperties().getMinApi(). Tivi has min API 23 and there are currently trace
+ // references issues with CC classes for default interface methods.
+ public static TestParametersCollection data() {
+ return getTestParameters()
+ .withDexRuntime(DexVm.Version.V7_0_0)
+ .withApiLevel(AndroidApiLevel.N)
+ .build();
+ }
+
+ private void configureDesugaredLibrary(
+ TestCompilerBuilder<?, ?, ?, ?, ?> builder, CompilerDump dump) {
+ if (useDesugaredLibraryConfigurationFromDump) {
+ builder.enableCoreLibraryDesugaring(
+ LibraryDesugaringTestConfiguration.forSpecification(dump.getDesugaredLibraryFile()));
+ } else {
+ builder.enableCoreLibraryDesugaring(
+ LibraryDesugaringTestConfiguration.forSpecification(
+ Paths.get(ToolHelper.LIBRARY_DESUGAR_SOURCE_DIR, "jdk11", "desugar_jdk_libs.json")));
+ }
+ }
+
+ private void configureDesugaredLibrary(L8Command.Builder builder, CompilerDump dump) {
+ if (useDesugaredLibraryConfigurationFromDump) {
+ builder.addDesugaredLibraryConfiguration(
+ StringResource.fromFile(dump.getDesugaredLibraryFile()));
+ } else {
+ builder.addDesugaredLibraryConfiguration(
+ StringResource.fromFile(
+ Paths.get(ToolHelper.LIBRARY_DESUGAR_SOURCE_DIR, "jdk11", "desugar_jdk_libs.json")));
+ }
+ }
+
+ @Test
+ @Ignore("Will be removed, only present to easily compare with partial compilation")
+ public void testD8() throws Exception {
+ Path tempDir = temp.newFolder().toPath();
+
+ CompilerDump dump = CompilerDump.fromArchive(TIVI_DUMP_PATH, temp.newFolder().toPath());
+ Path output = tempDir.resolve("tivid8.zip");
+ testForD8(parameters.getBackend())
+ .setMinApi(parameters)
+ .addLibraryFiles(dump.getLibraryArchive())
+ .addClasspathFiles(dump.getClasspathArchive())
+ .addProgramFiles(dump.getProgramArchive())
+ .setMode(CompilationMode.RELEASE)
+ .apply(b -> configureDesugaredLibrary(b, dump))
+ .compile()
+ .writeToZip(output);
+
+ Path l8Output = tempDir.resolve("tivid8l8.zip");
+ runL8(tempDir, dump, output, l8Output);
+ }
+
+ @Test
+ @Ignore("Will be removed, only present to easily compare with partial compilation")
+ public void testR8() throws Exception {
+ Path tempDir = temp.newFolder().toPath();
+
+ CompilerDump dump = CompilerDump.fromArchive(TIVI_DUMP_PATH, temp.newFolder().toPath());
+ Path output = tempDir.resolve("tivir8.zip");
+ testForR8(parameters.getBackend())
+ .setMinApi(parameters)
+ .addLibraryFiles(dump.getLibraryArchive())
+ .addClasspathFiles(dump.getClasspathArchive())
+ .addProgramFiles(dump.getProgramArchive())
+ .addKeepRuleFiles(dump.getProguardConfigFile())
+ .apply(b -> configureDesugaredLibrary(b, dump))
+ .setMode(CompilationMode.RELEASE)
+ .addOptionsModification(
+ options -> options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces())
+ .allowDiagnosticMessages()
+ .allowUnnecessaryDontWarnWildcards()
+ .allowUnusedDontWarnPatterns()
+ .allowUnusedProguardConfigurationRules()
+ .compile()
+ .writeToZip(output);
+
+ Path l8Output = tempDir.resolve("tivir8l8.zip");
+ runL8(tempDir, dump, output, l8Output);
+ }
+
+ private void testDump(CompilerDump dump, String appNamespace) throws Exception {
+ assert !appNamespace.endsWith(".");
+ Path tempDir = temp.newFolder().toPath();
+
+ // Different sets of namespaces to shrink.
+ ImmutableMap<String, Predicate<String>> splits =
+ ImmutableMap.of(
+ "androidx", name -> name.startsWith("androidx."),
+ "androidx_kotlin_and_kotlinx",
+ name ->
+ name.startsWith("androidx.")
+ || name.startsWith("kotlin.")
+ || name.startsWith("kotlinx."),
+ "more_libraries",
+ name ->
+ name.startsWith("androidx.")
+ || name.startsWith("kotlin.")
+ || name.startsWith("kotlinx.")
+ || name.startsWith("android.support.")
+ || name.startsWith("io.ktor.")
+ || name.startsWith("com.google.android.gms.")
+ || name.startsWith("com.google.firebase."),
+ "all_but_app namespace", name -> !name.startsWith(appNamespace + "."));
+
+ // Compile with each set of namespaces to shrink and collect DEX size.
+ Map<String, Pair<Long, Long>> dexSize = new LinkedHashMap<>();
+ for (Entry<String, Predicate<String>> entry : splits.entrySet()) {
+ long size = runR8PartialAndL8(tempDir, dump, entry.getKey(), entry.getValue());
+ dexSize.put(entry.getKey(), new Pair<>(size, 0L));
+ }
+ dexSize.forEach(
+ (name, size) ->
+ System.out.println(name + ": " + size.getFirst() + ", " + size.getSecond()));
+
+ // Check that sizes for increased namespaces to shrink does not increase size.
+ Pair<Long, Long> previousSize = null;
+ for (Entry<String, Pair<Long, Long>> entry : dexSize.entrySet()) {
+ if (previousSize != null) {
+ assertTrue(entry.getKey(), entry.getValue().getFirst() <= previousSize.getFirst());
+ assertTrue(entry.getKey(), entry.getValue().getSecond() <= previousSize.getSecond());
+ }
+ previousSize = entry.getValue();
+ }
+ }
+
+ @Test
+ @Ignore("Still experimental, do not run this test by default")
+ public void testTivi() throws Exception {
+ Path tempDir = temp.newFolder().toPath();
+ Path dumpDir = tempDir.resolve("dump");
+ testDump(CompilerDump.fromArchive(TIVI_DUMP_PATH, dumpDir), "app.tivi");
+ }
+
+ @Test
+ @Ignore("Still experimental, do not run this test by default")
+ public void testNowinandroid() throws Exception {
+ Path tempDir = temp.newFolder().toPath();
+ Path dumpDir = tempDir.resolve("dump");
+ testDump(
+ CompilerDump.fromArchive(NOWINANDROID_DUMP_PATH, dumpDir),
+ "com.google.samples.apps.nowinandroid");
+ }
+
+ private long runR8PartialAndL8(
+ Path tempDir, CompilerDump dump, String name, Predicate<String> isR8) throws Exception {
+ Path tmp = tempDir.resolve(name);
+ Files.createDirectory(tmp);
+ Path output = tmp.resolve("tivix8.zip");
+ runR8Partial(tempDir, dump, output, isR8);
+ Path l8Output = tmp.resolve("tivix8l8.zip");
+ runL8(tmp, dump, output, l8Output);
+ Box<Long> size = new Box<>(0L);
+ for (Path zipWithDex : new Path[] {output, l8Output}) {
+ ZipUtils.iter(
+ zipWithDex,
+ (zipEntry, input) ->
+ size.set(
+ size.get()
+ + (zipEntry.getName().endsWith(FileUtils.DEX_EXTENSION)
+ ? zipEntry.getSize()
+ : 0L)));
+ }
+ return size.get();
+ }
+
+ private void runR8Partial(Path tempDir, CompilerDump dump, Path output, Predicate<String> isR8)
+ throws IOException, CompilationFailedException {
+ testForR8Partial(parameters.getBackend())
+ .setR8PartialConfigurationPredicate(isR8)
+ .addOptionsModification(
+ options -> {
+ options.r8PartialCompilationOptions.tempDir = tempDir;
+
+ // For compiling nowonandroid.
+ options.testing.allowUnnecessaryDontWarnWildcards = true;
+ options.testing.allowUnusedDontWarnRules = true;
+ options.getOpenClosedInterfacesOptions().suppressAllOpenInterfaces();
+ })
+ .setMinApi(parameters)
+ .addLibraryFiles(dump.getLibraryArchive())
+ .addClasspathFiles(dump.getClasspathArchive())
+ .addProgramFiles(dump.getProgramArchive())
+ .addKeepRuleFiles(dump.getProguardConfigFile())
+ // Add this keep rules as trace references does not trace annotations. The consumer rules
+ // explicitly leaves out the annotation class Navigator.Name:
+ //
+ // # A -keep rule for the Navigator.Name annotation class is not required
+ // # since the annotation is referenced from the code.
+ //
+ // As this code reference is not in the D8 part of the code Navigator.Name is removed.
+ .addKeepRules("-keep class androidx.navigation.Navigator$Name { *; }")
+ .apply(b -> configureDesugaredLibrary(b, dump))
+ .setMode(CompilationMode.RELEASE)
+ .allowStdoutMessages()
+ .allowStderrMessages()
+ .allowUnusedProguardConfigurationRules() // nowonandroid
+ .enableEmptyMemberRulesToDefaultInitRuleConversion(true) // avoid warnings
+ .allowDiagnosticInfoMessages()
+ .compile()
+ .writeToZip(output);
+ }
+
+ private void runL8(Path tempDir, CompilerDump dump, Path appDexCode, Path output)
+ throws Exception {
+ Path desugaredLibraryRules = tempDir.resolve("desugared_library.rules");
+ TraceReferencesKeepRules keepRulesConsumer =
+ TraceReferencesKeepRules.builder()
+ .setOutputConsumer(new FileConsumer(desugaredLibraryRules))
+ .build();
+
+ AndroidApiLevel apiLevel = AndroidApiLevel.N;
+ Path path = tempDir.resolve("desugared_library.jar");
+ Path dd =
+ DesugaredLibraryJDK11Undesugarer.undesugaredJarJDK11(tempDir, DESUGARED_JDK_11_LIB_JAR);
+ L8Command.Builder commandBuilder =
+ L8Command.builder()
+ .setMinApiLevel(apiLevel.getLevel())
+ .addLibraryFiles(dump.getLibraryArchive())
+ .addProgramFiles(dd)
+ .setOutput(path, OutputMode.ClassFile);
+ configureDesugaredLibrary(commandBuilder, dump);
+ L8.run(commandBuilder.build());
+
+ TraceReferencesCommand.Builder traceReferencesCommandBuilder =
+ TraceReferencesCommand.builder()
+ .addLibraryFiles(dump.getLibraryArchive())
+ .addSourceFiles(appDexCode)
+ .addTargetFiles(path)
+ .setConsumer(keepRulesConsumer);
+ TraceReferences.run(traceReferencesCommandBuilder.build());
+
+ testForR8(Backend.DEX)
+ .addLibraryFiles(dump.getLibraryArchive())
+ .addProgramFiles(path)
+ .addKeepRuleFiles(desugaredLibraryRules)
+ .compile()
+ .writeToZip(output);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageDontObfuscateTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageDontObfuscateTest.java
index 677fe21..030966f 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageDontObfuscateTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageDontObfuscateTest.java
@@ -99,7 +99,8 @@
.assertSuccessWithOutputLines(EXPECTED);
}
- private R8TestCompileResult setup(R8TestBuilder<?> r8TestBuilder) throws Exception {
+ private R8TestCompileResult setup(R8TestBuilder<R8TestCompileResult, ?, ?> r8TestBuilder)
+ throws Exception {
return r8TestBuilder
.addInnerClasses(getClass())
.setMinApi(parameters)
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageInnerAndOuterClassTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageInnerAndOuterClassTest.java
index a026793..003b2e5 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageInnerAndOuterClassTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageInnerAndOuterClassTest.java
@@ -39,7 +39,8 @@
test(testForR8Compat(parameters.getBackend()).addProgramClasses(TestClass.class), false);
}
- private void test(R8TestBuilder<?> testBuilder, boolean eligibleForRepackaging) throws Exception {
+ private void test(R8TestBuilder<?, ?, ?> testBuilder, boolean eligibleForRepackaging)
+ throws Exception {
testBuilder
.addProgramClasses(Outer.class, Inner.class)
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateInnerClassTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateInnerClassTest.java
index 70bd738..6234266 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateInnerClassTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithPackagePrivateInnerClassTest.java
@@ -39,7 +39,7 @@
test(testForR8(parameters.getBackend()).addKeepClassRules(NonPublicKeptClass.class), true);
}
- private void test(R8TestBuilder<?> builder, boolean expectRepackaged) throws Exception {
+ private void test(R8TestBuilder<?, ?, ?> builder, boolean expectRepackaged) throws Exception {
builder
.addInnerClasses(getClass())
.addKeepMainRule(TestClass.class)
diff --git a/src/test/java/com/android/tools/r8/resolution/B367915233Test.java b/src/test/java/com/android/tools/r8/resolution/B367915233Test.java
deleted file mode 100644
index c01e188..0000000
--- a/src/test/java/com/android/tools/r8/resolution/B367915233Test.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// 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.resolution;
-
-import com.android.tools.r8.TestBase;
-import com.android.tools.r8.TestParameters;
-import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.StringUtils;
-import java.util.LinkedHashMap;
-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 B367915233Test extends TestBase {
-
- @Parameter(0)
- public TestParameters parameters;
-
- @Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimesAndApiLevels().build();
- }
-
- private static final String EXPECTED_OUTPUT = StringUtils.lines("Hello, world!");
-
- @Test
- public void testR8() throws Exception {
- testForR8(parameters.getBackend())
- .addInnerClasses(getClass())
- .addKeepMainRule(TestClass.class)
- .setMinApi(parameters)
- .run(parameters.getRuntime(), TestClass.class)
- .applyIf(
- parameters.isCfRuntime(),
- r -> r.assertFailureWithErrorThatThrows(VerifyError.class),
- r -> r.assertSuccessWithOutput(EXPECTED_OUTPUT));
- }
-
- static class TestClass {
-
- public static void main(String[] args) {
- new LinkedHashMap<>().clone();
- System.out.println("Hello, world!");
- }
- }
-}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerAssertionInClinitOnlyTest.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerAssertionInClinitOnlyTest.java
index f2de8f4..160d344 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerAssertionInClinitOnlyTest.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerAssertionInClinitOnlyTest.java
@@ -38,7 +38,7 @@
}
@Override
- protected void configure(R8TestBuilder<?> builder) {
+ protected void configure(R8TestBuilder<?, ?, ?> builder) {
builder.allowUnusedProguardConfigurationRules();
}
}
diff --git a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerTestBase.java b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerTestBase.java
index c3a4eeb..004e756 100644
--- a/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerTestBase.java
+++ b/src/test/java/com/android/tools/r8/rewrite/assertions/AssertionConfigurationAssertionHandlerTestBase.java
@@ -40,7 +40,7 @@
return ImmutableList.of(AssertionHandlers.class);
}
- protected void configure(R8TestBuilder<?> builder) {}
+ protected void configure(R8TestBuilder<?, ?, ?> builder) {}
protected void inspect(CodeInspector inspector) {}
diff --git a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
index b7c0d9a..e523554 100644
--- a/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/KeepAnnotatedMemberTest.java
@@ -343,7 +343,8 @@
}
}
- private void suppressZipFileAssignmentsToJavaLangAutoCloseable(R8TestBuilder<?> testBuilder) {
+ private void suppressZipFileAssignmentsToJavaLangAutoCloseable(
+ R8TestBuilder<?, ?, ?> testBuilder) {
testBuilder.addOptionsModification(
options ->
options
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryClassExtendingProgramClassSuperTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryClassExtendingProgramClassSuperTest.java
index e856027..f3db1be 100644
--- a/src/test/java/com/android/tools/r8/shaking/LibraryClassExtendingProgramClassSuperTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryClassExtendingProgramClassSuperTest.java
@@ -59,7 +59,7 @@
@Test
public void testR8() throws Exception {
- R8TestBuilder<? extends R8TestBuilder<?>> r8TestBuilder =
+ R8TestBuilder<?, ?, ?> r8TestBuilder =
(proguardCompatibility
? testForR8Compat(parameters.getBackend(), true)
: testForR8(parameters.getBackend()))
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesFromClasspathOrLibraryTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesFromClasspathOrLibraryTest.java
new file mode 100644
index 0000000..d9631f1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesFromClasspathOrLibraryTest.java
@@ -0,0 +1,266 @@
+// 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.shaking;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticOrigin;
+import static com.android.tools.r8.OriginMatcher.hasPart;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.ClassFileResourceProvider;
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DataResourceProvider;
+import com.android.tools.r8.ProgramResource;
+import com.android.tools.r8.ProgramResource.Kind;
+import com.android.tools.r8.ResourceException;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.origin.ArchiveEntryOrigin;
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.DescriptorUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.google.common.collect.ImmutableList;
+import com.google.common.io.ByteStreams;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+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 LibraryProvidedProguardRulesFromClasspathOrLibraryTest
+ extends LibraryProvidedProguardRulesTestBase {
+
+ @interface Keep {}
+
+ public interface Interface {}
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameter(1)
+ public LibraryType libraryType;
+
+ @Parameter(2)
+ public boolean isClasspath;
+
+ @Parameters(name = "{0}, libraryType: {1}, isClasspath {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build(),
+ LibraryType.values(),
+ BooleanUtils.values());
+ }
+
+ private Path buildLibrary(List<String> rules) throws Exception {
+ return buildLibrary(libraryType, ImmutableList.of(Interface.class, Keep.class), rules);
+ }
+
+ private CodeInspector runTest(List<String> rules) throws Exception {
+ Path library = buildLibrary(rules);
+ return testForR8(parameters.getBackend())
+ .addProgramClasses(A.class, B.class)
+ .applyIf(
+ isClasspath,
+ b -> b.addClasspathFiles(library),
+ b ->
+ b.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
+ .addLibraryFiles(library))
+ .setMinApi(parameters)
+ .apply(b -> ToolHelper.setReadEmbeddedRulesFromClasspathAndLibrary(b.getBuilder(), true))
+ .compile()
+ .inspector();
+ }
+
+ private CodeInspector runTest(String rules) throws Exception {
+ return runTest(ImmutableList.of(rules));
+ }
+
+ @Test
+ public void providedKeepRuleImplements() throws Exception {
+ CodeInspector inspector =
+ runTest("-keep class * implements " + Interface.class.getTypeName() + " { *; }");
+ // TODO(b/228319861): Read Proguard rules from AAR's.
+ assertThat(inspector.clazz(A.class), notIf(isPresent(), libraryType.isAar()));
+ assertThat(inspector.clazz(B.class), not(isPresent()));
+ }
+
+ @Test
+ public void providedKeepRuleAnnotated() throws Exception {
+ CodeInspector inspector = runTest("-keep @" + Keep.class.getTypeName() + " class * { *; }");
+ assertThat(inspector.clazz(A.class), not(isPresent()));
+ // TODO(b/228319861): Read Proguard rules from AAR's.
+ assertThat(inspector.clazz(B.class), notIf(isPresent(), libraryType.isAar()));
+ }
+
+ @Test
+ public void providedKeepRuleImplementsOrAnnotated() throws Exception {
+ CodeInspector inspector =
+ runTest(
+ ImmutableList.of(
+ "-keep class * implements " + Interface.class.getTypeName() + " { *; }",
+ "-keep @" + Keep.class.getTypeName() + " class * { *; }"));
+ // TODO(b/228319861): Read Proguard rules from AAR's.
+ assertThat(inspector.clazz(A.class), notIf(isPresent(), libraryType.isAar()));
+ assertThat(inspector.clazz(B.class), notIf(isPresent(), libraryType.isAar()));
+ }
+
+ @Test
+ public void providedKeepRuleSyntaxError() {
+ // TODO(b/228319861): Read Proguard rules from AAR's.
+ assumeTrue(!libraryType.isAar());
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForR8(parameters.getBackend())
+ .addProgramFiles(buildLibrary(ImmutableList.of("error")))
+ .setMinApi(parameters)
+ .apply(
+ b ->
+ ToolHelper.setReadEmbeddedRulesFromClasspathAndLibrary(
+ b.getBuilder(), true))
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertErrorThatMatches(
+ allOf(
+ diagnosticMessage(containsString("Expected char '-'")),
+ diagnosticOrigin(hasPart("META-INF/proguard/jar.rules")),
+ diagnosticOrigin(instanceOf(ArchiveEntryOrigin.class))))));
+ }
+
+ @Test
+ public void providedKeepRuleInjarsError() {
+ // TODO(b/228319861): Read Proguard rules from AAR's.
+ assumeTrue(!libraryType.isAar());
+ assertThrows(
+ CompilationFailedException.class,
+ () -> {
+ Path library = buildLibrary(ImmutableList.of("-injars some.jar"));
+ testForR8(parameters.getBackend())
+ .applyIf(
+ isClasspath,
+ b -> b.addClasspathFiles(library),
+ b ->
+ b.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
+ .addLibraryFiles(library))
+ .setMinApi(parameters)
+ .apply(
+ b -> ToolHelper.setReadEmbeddedRulesFromClasspathAndLibrary(b.getBuilder(), true))
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertErrorThatMatches(
+ diagnosticMessage(
+ containsString("Options with file names are not supported"))));
+ });
+ }
+
+ @Test
+ public void providedKeepRuleIncludeError() {
+ // TODO(b/228319861): Read Proguard rules from AAR's.
+ assumeTrue(!libraryType.isAar());
+ assertThrows(
+ CompilationFailedException.class,
+ () -> {
+ Path library = buildLibrary(ImmutableList.of("-include other.rules"));
+ testForR8(parameters.getBackend())
+ .applyIf(
+ isClasspath,
+ b -> b.addClasspathFiles(library),
+ b ->
+ b.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
+ .addLibraryFiles(library))
+ .setMinApi(parameters)
+ .apply(
+ b -> ToolHelper.setReadEmbeddedRulesFromClasspathAndLibrary(b.getBuilder(), true))
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertErrorThatMatches(
+ diagnosticMessage(
+ containsString("Options with file names are not supported"))));
+ });
+ }
+
+ static class TestProvider implements ClassFileResourceProvider, DataResourceProvider {
+
+ @Override
+ public ProgramResource getProgramResource(String descriptor) {
+ byte[] bytes;
+ try {
+ bytes = ByteStreams.toByteArray(Keep.class.getResourceAsStream("K.class"));
+ } catch (IOException e) {
+ return null;
+ }
+ return ProgramResource.fromBytes(
+ Origin.unknown(),
+ Kind.CF,
+ bytes,
+ Collections.singleton(DescriptorUtils.javaTypeToDescriptor(Keep.class.getTypeName())));
+ }
+
+ @Override
+ public Set<String> getClassDescriptors() {
+ return null;
+ }
+
+ @Override
+ public DataResourceProvider getDataResourceProvider() {
+ return this;
+ }
+
+ @Override
+ public void accept(Visitor visitor) throws ResourceException {
+ throw new ResourceException(Origin.unknown(), "Cannot provide data resources after all");
+ }
+ }
+
+ @Test
+ public void throwingDataResourceProvider() {
+ // TODO(b/228319861): Read Proguard rules from AAR's.
+ assumeTrue(!libraryType.isAar());
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForR8(parameters.getBackend())
+ .applyIf(
+ isClasspath,
+ b -> b.addClasspathResourceProviders(new TestProvider()),
+ b ->
+ b.addLibraryFiles(ToolHelper.getAndroidJar(AndroidApiLevel.LATEST))
+ .addLibraryResourceProviders(new TestProvider()))
+ .setMinApi(parameters)
+ .apply(
+ b ->
+ ToolHelper.setReadEmbeddedRulesFromClasspathAndLibrary(
+ b.getBuilder(), true))
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertErrorThatMatches(
+ allOf(
+ diagnosticMessage(
+ containsString("Cannot provide data resources after all")),
+ diagnosticOrigin(is(Origin.unknown()))))));
+ }
+
+ static class A implements Interface {}
+
+ @Keep
+ static class B {}
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesR8SpecificTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesR8SpecificTest.java
index ccb56de..ecc00c4 100644
--- a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesR8SpecificTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesR8SpecificTest.java
@@ -37,7 +37,7 @@
@Parameter(2)
public ProviderType providerType;
- @Parameters(name = "{0}, AAR: {1}, {2}")
+ @Parameters(name = "{0}, libraryType: {1}, providerType: {2}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withNoneRuntime().build(),
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
index 72b1cb7..dfd61c5 100644
--- a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
@@ -29,8 +29,6 @@
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteStreams;
@@ -74,7 +72,7 @@
@Parameter(2)
public ProviderType providerType;
- @Parameters(name = "{0}, AAR: {1}, {2}")
+ @Parameters(name = "{0}, libraryType: {1}, providerType: {2}")
public static List<Object[]> data() {
return buildParameters(
getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build(),
@@ -83,27 +81,7 @@
}
private Path buildLibrary(List<String> rules) throws Exception {
- ZipBuilder jarBuilder =
- ZipBuilder.builder(temp.newFile(libraryType.isAar() ? "classes.jar" : "test.jar").toPath());
- addTestClassesToZip(jarBuilder.getOutputStream(), ImmutableList.of(A.class, B.class));
- if (libraryType.hasRulesInJar()) {
- for (int i = 0; i < rules.size(); i++) {
- String name = "META-INF/proguard/jar" + (i == 0 ? "" : i) + ".rules";
- jarBuilder.addText(name, rules.get(i));
- }
- }
- if (libraryType.isAar()) {
- Path jar = jarBuilder.build();
- String allRules = StringUtils.lines(rules);
- ZipBuilder aarBuilder = ZipBuilder.builder(temp.newFile("test.aar").toPath());
- aarBuilder.addFilesRelative(jar.getParent(), jar);
- if (libraryType.hasRulesInAar()) {
- aarBuilder.addText("proguard.txt", allRules);
- }
- return aarBuilder.build();
- } else {
- return jarBuilder.build();
- }
+ return buildLibrary(libraryType, ImmutableList.of(A.class, B.class), rules);
}
private CodeInspector runTest(List<String> rules) throws Exception {
@@ -153,7 +131,7 @@
}
@Test
- public void syntaxError() {
+ public void providedKeepRuleSyntaxError() {
// TODO(b/228319861): Read Proguard rules from AAR's.
assumeTrue(!libraryType.isAar());
assertThrows(
@@ -172,7 +150,24 @@
}
@Test
- public void includeError() {
+ public void providedKeepRuleInjarsError() {
+ // TODO(b/228319861): Read Proguard rules from AAR's.
+ assumeTrue(!libraryType.isAar());
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ testForR8(parameters.getBackend())
+ .addProgramFiles(buildLibrary(ImmutableList.of("-injars some.jar")))
+ .setMinApi(parameters)
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ diagnostics.assertErrorThatMatches(
+ diagnosticMessage(
+ containsString("Options with file names are not supported")))));
+ }
+
+ @Test
+ public void providedKeepRuleIncludeError() {
// TODO(b/228319861): Read Proguard rules from AAR's.
assumeTrue(!libraryType.isAar());
assertThrows(
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTestBase.java b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTestBase.java
index 4bca489..3bdedc2 100644
--- a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTestBase.java
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTestBase.java
@@ -5,6 +5,10 @@
package com.android.tools.r8.shaking;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import java.nio.file.Path;
+import java.util.List;
public class LibraryProvidedProguardRulesTestBase extends TestBase {
@@ -31,4 +35,29 @@
API,
INJARS
}
+
+ protected Path buildLibrary(LibraryType libraryType, List<Class<?>> classes, List<String> rules)
+ throws Exception {
+ ZipBuilder jarBuilder =
+ ZipBuilder.builder(temp.newFile(libraryType.isAar() ? "classes.jar" : "test.jar").toPath());
+ addTestClassesToZip(jarBuilder.getOutputStream(), classes);
+ if (libraryType.hasRulesInJar()) {
+ for (int i = 0; i < rules.size(); i++) {
+ String name = "META-INF/proguard/jar" + (i == 0 ? "" : i) + ".rules";
+ jarBuilder.addText(name, rules.get(i));
+ }
+ }
+ if (libraryType.isAar()) {
+ Path jar = jarBuilder.build();
+ String allRules = StringUtils.lines(rules);
+ ZipBuilder aarBuilder = ZipBuilder.builder(temp.newFile("test.aar").toPath());
+ aarBuilder.addFilesRelative(jar.getParent(), jar);
+ if (libraryType.hasRulesInAar()) {
+ aarBuilder.addText("proguard.txt", allRules);
+ }
+ return aarBuilder.build();
+ } else {
+ return jarBuilder.build();
+ }
+ }
}
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/KeepEnclosingMethodForKeptMethodTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/KeepEnclosingMethodForKeptMethodTest.java
index 9a66258..f6d7ea4 100644
--- a/src/test/java/com/android/tools/r8/shaking/attributes/KeepEnclosingMethodForKeptMethodTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/attributes/KeepEnclosingMethodForKeptMethodTest.java
@@ -61,7 +61,8 @@
runTest(testForR8Compat(parameters.getBackend())).assertSuccessWithOutputLines(EXPECTED);
}
- private R8TestRunResult runTest(R8TestBuilder<?> testBuilder) throws Exception {
+ private R8TestRunResult runTest(R8TestBuilder<?, R8TestRunResult, ?> testBuilder)
+ throws Exception {
return testBuilder
.addInnerClasses(KeptClass.class)
.addProgramClassFileData(
diff --git a/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java b/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java
index 56e7b82..57a904f 100644
--- a/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/attributes/KeepSignatureTest.java
@@ -66,7 +66,8 @@
runTest(testForR8Compat(parameters.getBackend()), true);
}
- private void runTest(R8TestBuilder<?> testBuilder, boolean keptForNotKept) throws Exception {
+ private void runTest(R8TestBuilder<?, ?, ?> testBuilder, boolean keptForNotKept)
+ throws Exception {
testBuilder
.addProgramClassFileData(transformer(KeptClass.class).removeInnerClasses().transform())
.addProgramClassFileData(transformer(NotKeptClass.class).removeInnerClasses().transform())
diff --git a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
index 41c6730..2670bee 100644
--- a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
+++ b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
@@ -194,7 +194,7 @@
}
}
- public static <B extends R8TestBuilder<B>> ThrowableConsumer<B> addStartupProfile(
+ public static <B extends R8TestBuilder<?, ?, ?>> ThrowableConsumer<B> addStartupProfile(
Collection<ExternalStartupItem> startupItems) {
return testBuilder -> addStartupProfile(testBuilder, startupItems);
}
diff --git a/src/test/testbase/java/com/android/tools/r8/KotlinCompilerTool.java b/src/test/testbase/java/com/android/tools/r8/KotlinCompilerTool.java
index eb7eb2d..1c0c6a8 100644
--- a/src/test/testbase/java/com/android/tools/r8/KotlinCompilerTool.java
+++ b/src/test/testbase/java/com/android/tools/r8/KotlinCompilerTool.java
@@ -78,6 +78,7 @@
KOTLINC_1_8_0("kotlin-compiler-1.8.0", KotlinLambdaGeneration.CLASS),
KOTLINC_1_9_21("kotlin-compiler-1.9.21", KotlinLambdaGeneration.CLASS),
KOTLINC_2_0_20("kotlin-compiler-2.0.20", KotlinLambdaGeneration.INVOKE_DYNAMIC),
+ KOTLINC_2_1_0_BETA1("kotlin-compiler-2.1.0-Beta1", KotlinLambdaGeneration.INVOKE_DYNAMIC),
KOTLIN_DEV("kotlin-compiler-dev", KotlinLambdaGeneration.INVOKE_DYNAMIC);
public static final KotlinCompilerVersion MIN_SUPPORTED_VERSION = KOTLINC_2_0_20;
diff --git a/src/test/testbase/java/com/android/tools/r8/R8CompatTestBuilder.java b/src/test/testbase/java/com/android/tools/r8/R8CompatTestBuilder.java
index 8074250..bc904c7 100644
--- a/src/test/testbase/java/com/android/tools/r8/R8CompatTestBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/R8CompatTestBuilder.java
@@ -5,8 +5,17 @@
import com.android.tools.r8.R8Command.Builder;
import com.android.tools.r8.TestBase.Backend;
+import com.android.tools.r8.benchmarks.BenchmarkResults;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
-public class R8CompatTestBuilder extends R8TestBuilder<R8CompatTestBuilder> {
+public class R8CompatTestBuilder
+ extends R8TestBuilder<R8TestCompileResult, R8TestRunResult, R8CompatTestBuilder> {
private R8CompatTestBuilder(TestState state, Builder builder, Backend backend) {
super(state, builder, backend);
@@ -28,4 +37,31 @@
R8CompatTestBuilder self() {
return this;
}
+
+ R8TestCompileResult internalCompileR8(
+ Builder builder,
+ Consumer<InternalOptions> optionsConsumer,
+ Supplier<AndroidApp> app,
+ BenchmarkResults benchmarkResults,
+ StringBuilder pgConfOutput,
+ Box<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer,
+ StringBuilder proguardMapBuilder)
+ throws CompilationFailedException {
+ ToolHelper.runAndBenchmarkR8WithoutResult(builder, optionsConsumer, benchmarkResults);
+ return new R8TestCompileResult(
+ getState(),
+ getOutputMode(),
+ libraryDesugaringTestConfiguration,
+ app.get(),
+ pgConfOutput.toString(),
+ syntheticProguardRulesConsumer.get(),
+ proguardMapBuilder.toString(),
+ graphConsumer,
+ getMinApiLevel(),
+ features,
+ residualArtProfiles,
+ resourceShrinkerOutput,
+ resourceShrinkerOutputForFeatures,
+ buildMetadata != null ? buildMetadata.get() : null);
+ }
}
diff --git a/src/test/testbase/java/com/android/tools/r8/R8FullTestBuilder.java b/src/test/testbase/java/com/android/tools/r8/R8FullTestBuilder.java
index a8f6d60..3caab28 100644
--- a/src/test/testbase/java/com/android/tools/r8/R8FullTestBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/R8FullTestBuilder.java
@@ -5,9 +5,17 @@
import com.android.tools.r8.R8Command.Builder;
import com.android.tools.r8.TestBase.Backend;
+import com.android.tools.r8.benchmarks.BenchmarkResults;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.InternalOptions;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
-public class R8FullTestBuilder extends R8TestBuilder<R8FullTestBuilder> {
+public class R8FullTestBuilder
+ extends R8TestBuilder<R8TestCompileResult, R8TestRunResult, R8FullTestBuilder> {
private R8FullTestBuilder(TestState state, Builder builder, Backend backend) {
super(state, builder, backend);
@@ -32,4 +40,31 @@
R8FullTestBuilder self() {
return this;
}
+
+ R8TestCompileResult internalCompileR8(
+ Builder builder,
+ Consumer<InternalOptions> optionsConsumer,
+ Supplier<AndroidApp> app,
+ BenchmarkResults benchmarkResults,
+ StringBuilder pgConfOutput,
+ Box<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer,
+ StringBuilder proguardMapBuilder)
+ throws CompilationFailedException {
+ ToolHelper.runAndBenchmarkR8WithoutResult(builder, optionsConsumer, benchmarkResults);
+ return new R8TestCompileResult(
+ getState(),
+ getOutputMode(),
+ libraryDesugaringTestConfiguration,
+ app.get(),
+ pgConfOutput.toString(),
+ syntheticProguardRulesConsumer.get(),
+ proguardMapBuilder.toString(),
+ graphConsumer,
+ getMinApiLevel(),
+ features,
+ residualArtProfiles,
+ resourceShrinkerOutput,
+ resourceShrinkerOutputForFeatures,
+ buildMetadata != null ? buildMetadata.get() : null);
+ }
}
diff --git a/src/test/testbase/java/com/android/tools/r8/R8PartialTestBuilder.java b/src/test/testbase/java/com/android/tools/r8/R8PartialTestBuilder.java
new file mode 100644
index 0000000..319b633
--- /dev/null
+++ b/src/test/testbase/java/com/android/tools/r8/R8PartialTestBuilder.java
@@ -0,0 +1,191 @@
+// 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;
+
+import com.android.tools.r8.R8Command.Builder;
+import com.android.tools.r8.TestBase.Backend;
+import com.android.tools.r8.benchmarks.BenchmarkResults;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
+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 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 R8PartialTestBuilder(TestState state, Builder builder, Backend backend) {
+ super(state, builder, backend);
+ }
+
+ public static R8PartialTestBuilder create(TestState state, Backend backend) {
+ Builder builder = R8Command.builder(state.getDiagnosticsHandler());
+ return new R8PartialTestBuilder(state, builder, backend);
+ }
+
+ public static R8PartialTestBuilder create(
+ TestState state, AndroidApp.Builder appBuilder, Backend backend) {
+ return new R8PartialTestBuilder(state, R8Command.builder(appBuilder.build()), backend);
+ }
+
+ @Override
+ public boolean isR8TestBuilder() {
+ return true;
+ }
+
+ @Override
+ R8PartialTestBuilder self() {
+ 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()
+ : "Overwriting configuration...?";
+ r8PartialConfiguration = R8PartialConfiguration.builder().include(include).build();
+ return self();
+ }
+
+ public R8PartialTestBuilder setR8PartialConfiguration(R8PartialConfiguration configuration) {
+ assert r8PartialConfiguration == R8PartialConfiguration.defaultConfiguration()
+ : "Overwriting configuration...?";
+ r8PartialConfiguration = configuration;
+ return self();
+ }
+
+ public R8PartialTestBuilder setR8PartialConfiguration(
+ Function<R8PartialConfiguration.Builder, R8PartialConfiguration> fn) {
+ assert r8PartialConfiguration == R8PartialConfiguration.defaultConfiguration()
+ : "Overwriting configuration...?";
+ r8PartialConfiguration = fn.apply(R8PartialConfiguration.builder());
+ return self();
+ }
+
+ @Override
+ R8PartialTestCompileResult internalCompileR8(
+ Builder builder,
+ Consumer<InternalOptions> optionsConsumer,
+ Supplier<AndroidApp> app,
+ BenchmarkResults benchmarkResults,
+ StringBuilder pgConfOutput,
+ Box<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer,
+ StringBuilder proguardMapBuilder)
+ throws CompilationFailedException {
+ Consumer<InternalOptions> configureR8PartialCompilation =
+ options -> {
+ options.r8PartialCompilationOptions.enabled = true;
+ options.r8PartialCompilationOptions.isR8 = r8PartialConfiguration;
+ };
+ ToolHelper.runAndBenchmarkR8WithoutResult(
+ builder, configureR8PartialCompilation.andThen(optionsConsumer), benchmarkResults);
+ return new R8PartialTestCompileResult(
+ getState(),
+ getOutputMode(),
+ libraryDesugaringTestConfiguration,
+ app.get(),
+ pgConfOutput.toString(),
+ syntheticProguardRulesConsumer.get(),
+ proguardMapBuilder.toString(),
+ graphConsumer,
+ getMinApiLevel(),
+ features,
+ residualArtProfiles,
+ resourceShrinkerOutput,
+ resourceShrinkerOutputForFeatures,
+ buildMetadata != null ? buildMetadata.get() : null);
+ }
+}
diff --git a/src/test/testbase/java/com/android/tools/r8/R8PartialTestCompileResult.java b/src/test/testbase/java/com/android/tools/r8/R8PartialTestCompileResult.java
new file mode 100644
index 0000000..18f75fb
--- /dev/null
+++ b/src/test/testbase/java/com/android/tools/r8/R8PartialTestCompileResult.java
@@ -0,0 +1,54 @@
+// 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;
+
+import com.android.tools.r8.metadata.R8BuildMetadata;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.shaking.CollectingGraphConsumer;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.utils.AndroidApp;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.List;
+
+public class R8PartialTestCompileResult
+ extends R8TestCompileResultBase<R8PartialTestCompileResult> {
+
+ R8PartialTestCompileResult(
+ TestState state,
+ OutputMode outputMode,
+ LibraryDesugaringTestConfiguration libraryDesugaringTestConfiguration,
+ AndroidApp app,
+ String proguardConfiguration,
+ List<ProguardConfigurationRule> syntheticProguardRules,
+ String proguardMap,
+ CollectingGraphConsumer graphConsumer,
+ int minApiLevel,
+ List<Path> features,
+ List<ExternalArtProfile> residualArtProfiles,
+ Path resourceShrinkerOutput,
+ HashMap<String, Path> resourceShrinkerOutputForFeatures,
+ R8BuildMetadata buildMetadata) {
+ super(
+ state,
+ outputMode,
+ libraryDesugaringTestConfiguration,
+ app,
+ proguardConfiguration,
+ syntheticProguardRules,
+ proguardMap,
+ graphConsumer,
+ minApiLevel,
+ features,
+ residualArtProfiles,
+ resourceShrinkerOutput,
+ resourceShrinkerOutputForFeatures,
+ buildMetadata);
+ }
+
+ @Override
+ public R8PartialTestCompileResult self() {
+ return this;
+ }
+}
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 066e1ff..72fd0ae 100644
--- a/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/R8TestBuilder.java
@@ -65,8 +65,11 @@
import java.util.stream.Collectors;
import org.hamcrest.core.IsAnything;
-public abstract class R8TestBuilder<T extends R8TestBuilder<T>>
- extends TestShrinkerBuilder<R8Command, Builder, R8TestCompileResult, R8TestRunResult, T> {
+public abstract class R8TestBuilder<
+ CR extends TestCompileResult<CR, RR>,
+ RR extends TestRunResult<RR>,
+ T extends R8TestBuilder<CR, RR, T>>
+ extends TestShrinkerBuilder<R8Command, Builder, CR, RR, T> {
enum AllowedDiagnosticMessages {
ALL,
@@ -86,15 +89,15 @@
private boolean enableIsolatedSplits = false;
private boolean enableMissingLibraryApiModeling = true;
private boolean enableStartupLayoutOptimization = true;
- private CollectingGraphConsumer graphConsumer = null;
- private final List<ExternalArtProfile> residualArtProfiles = new ArrayList<>();
+ CollectingGraphConsumer graphConsumer = null;
+ final List<ExternalArtProfile> residualArtProfiles = new ArrayList<>();
private final List<String> keepRules = new ArrayList<>();
private final List<Path> mainDexRulesFiles = new ArrayList<>();
private final List<String> applyMappingMaps = new ArrayList<>();
- private final List<Path> features = new ArrayList<>();
- private Path resourceShrinkerOutput = null;
- private HashMap<String, Path> resourceShrinkerOutputForFeatures = new HashMap<>();
- private Box<R8BuildMetadata> buildMetadata;
+ final List<Path> features = new ArrayList<>();
+ Path resourceShrinkerOutput = null;
+ HashMap<String, Path> resourceShrinkerOutputForFeatures = new HashMap<>();
+ Box<R8BuildMetadata> buildMetadata;
private boolean androidPlatformBuild = false;
@Override
@@ -103,12 +106,22 @@
}
@Override
- public R8TestBuilder<?> asR8TestBuilder() {
+ public R8TestBuilder<?, ?, ?> asR8TestBuilder() {
return this;
}
+ abstract CR internalCompileR8(
+ Builder builder,
+ Consumer<InternalOptions> optionsConsumer,
+ Supplier<AndroidApp> app,
+ BenchmarkResults benchmarkResults,
+ StringBuilder pgConfOutput,
+ Box<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer,
+ StringBuilder proguardMapBuilder)
+ throws CompilationFailedException;
+
@Override
- R8TestCompileResult internalCompile(
+ CR internalCompile(
Builder builder,
Consumer<InternalOptions> optionsConsumer,
Supplier<AndroidApp> app,
@@ -134,13 +147,9 @@
}
}
- class Box {
-
- private List<ProguardConfigurationRule> syntheticProguardRules;
- }
- Box box = new Box();
+ Box<List<ProguardConfigurationRule>> syntheticProguardRulesConsumer = new Box<>();
ToolHelper.addSyntheticProguardRulesConsumerForTesting(
- builder, rules -> box.syntheticProguardRules = rules);
+ builder, syntheticProguardRulesConsumer::set);
libraryDesugaringTestConfiguration.configure(builder);
builder.setAndroidPlatformBuild(androidPlatformBuild);
if (!enableEmptyMemberRulesToDefaultInitRuleConversion.isUnknown()) {
@@ -154,23 +163,15 @@
builder.setBuildMetadataConsumer(buildMetadata::set);
}
StringBuilder pgConfOutput = wrapProguardConfigConsumer(builder);
- ToolHelper.runAndBenchmarkR8WithoutResult(builder, optionsConsumer, benchmarkResults);
- R8TestCompileResult compileResult =
- new R8TestCompileResult(
- getState(),
- getOutputMode(),
- libraryDesugaringTestConfiguration,
- app.get(),
- pgConfOutput.toString(),
- box.syntheticProguardRules,
- proguardMapBuilder.toString(),
- graphConsumer,
- getMinApiLevel(),
- features,
- residualArtProfiles,
- resourceShrinkerOutput,
- resourceShrinkerOutputForFeatures,
- buildMetadata != null ? buildMetadata.get() : null);
+ CR compileResult =
+ internalCompileR8(
+ builder,
+ optionsConsumer,
+ app,
+ benchmarkResults,
+ pgConfOutput,
+ syntheticProguardRulesConsumer,
+ proguardMapBuilder);
switch (allowedDiagnosticMessages) {
case ALL:
compileResult.getDiagnosticMessages().assertAllDiagnosticsMatch(new IsAnything<>());
@@ -238,21 +239,42 @@
return pgConfOutput;
}
+ private static StringBuilder wrapProguardMapConsumer(Builder builder, StringBuilder pgMapOutput) {
+ StringConsumer pgMapConsumer = builder.getProguardMapConsumer();
+ builder.setProguardMapConsumer(
+ new StringConsumer.ForwardingConsumer(pgMapConsumer) {
+ @Override
+ public void accept(String string, DiagnosticsHandler handler) {
+ super.accept(string, handler);
+ pgMapOutput.append(string);
+ }
+
+ @Override
+ public void finished(DiagnosticsHandler handler) {
+ super.finished(handler);
+ }
+ });
+ return pgMapOutput;
+ }
+
+ private static StringBuilder wrapProguardConfigConsumer(
+ Builder builder, StringBuilder pgConfOutput) {
+ StringConsumer pgConfConsumer = builder.getProguardConfigurationConsumer();
+ builder.setProguardConfigurationConsumer(
+ new StringConsumer.ForwardingConsumer(pgConfConsumer) {
+ @Override
+ public void accept(String string, DiagnosticsHandler handler) {
+ super.accept(string, handler);
+ pgConfOutput.append(string);
+ }
+ });
+ return pgConfOutput;
+ }
+
public Builder getBuilder() {
return builder;
}
- public T addProgramResourceProviders(Collection<ProgramResourceProvider> providers) {
- for (ProgramResourceProvider provider : providers) {
- builder.addProgramResourceProvider(provider);
- }
- return self();
- }
-
- public T addProgramResourceProviders(ProgramResourceProvider... providers) {
- return addProgramResourceProviders(Arrays.asList(providers));
- }
-
@Override
public T addClasspathClasses(Collection<Class<?>> classes) {
builder.addClasspathResourceProvider(ClassFileResourceProviderFromClasses(classes));
diff --git a/src/test/testbase/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/testbase/java/com/android/tools/r8/R8TestCompileResult.java
index e9956ad..9354c62 100644
--- a/src/test/testbase/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/testbase/java/com/android/tools/r8/R8TestCompileResult.java
@@ -3,61 +3,16 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
-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.assertNotNull;
-
-import com.android.tools.r8.DexSegments.SegmentInfo;
-import com.android.tools.r8.TestRuntime.DexRuntime;
-import com.android.tools.r8.ToolHelper.DexVm;
-import com.android.tools.r8.ToolHelper.ProcessResult;
-import com.android.tools.r8.androidresources.AndroidResourceTestingUtils;
-import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.ResourceTableInspector;
-import com.android.tools.r8.benchmarks.BenchmarkResults;
-import com.android.tools.r8.benchmarks.InstructionCodeSizeResult;
-import com.android.tools.r8.dexsplitter.SplitterTestBase.SplitRunner;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.metadata.R8BuildMetadata;
import com.android.tools.r8.profile.art.model.ExternalArtProfile;
-import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
import com.android.tools.r8.shaking.CollectingGraphConsumer;
import com.android.tools.r8.shaking.ProguardConfigurationRule;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.ThrowingBiConsumer;
-import com.android.tools.r8.utils.ThrowingConsumer;
-import com.android.tools.r8.utils.ZipUtils;
-import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
-import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
-import com.android.tools.r8.utils.graphinspector.GraphInspector;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceLinkedOpenHashMap;
-import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Consumer;
-public class R8TestCompileResult extends TestCompileResult<R8TestCompileResult, R8TestRunResult> {
-
- private final String proguardConfiguration;
- private final List<ProguardConfigurationRule> syntheticProguardRules;
- private final String proguardMap;
- private final CollectingGraphConsumer graphConsumer;
- private final List<Path> features;
- private final List<ExternalArtProfile> residualArtProfiles;
- private final Path resourceShrinkerOutput;
- private final Map<String, Path> resourceShrinkerOutputForFeatures;
- private final R8BuildMetadata buildMetadata;
+public class R8TestCompileResult extends R8TestCompileResultBase<R8TestCompileResult> {
R8TestCompileResult(
TestState state,
@@ -74,318 +29,25 @@
Path resourceShrinkerOutput,
HashMap<String, Path> resourceShrinkerOutputForFeatures,
R8BuildMetadata buildMetadata) {
- super(state, app, minApiLevel, outputMode, libraryDesugaringTestConfiguration);
- this.proguardConfiguration = proguardConfiguration;
- this.syntheticProguardRules = syntheticProguardRules;
- this.proguardMap = proguardMap;
- this.graphConsumer = graphConsumer;
- this.features = features;
- this.residualArtProfiles = residualArtProfiles;
- this.resourceShrinkerOutput = resourceShrinkerOutput;
- this.resourceShrinkerOutputForFeatures = resourceShrinkerOutputForFeatures;
- this.buildMetadata = buildMetadata;
- }
-
- public R8TestCompileResult benchmarkResourceSize(BenchmarkResults results) throws IOException {
- results.addResourceSizeResult(Files.size(resourceShrinkerOutput));
- return self();
+ super(
+ state,
+ outputMode,
+ libraryDesugaringTestConfiguration,
+ app,
+ proguardConfiguration,
+ syntheticProguardRules,
+ proguardMap,
+ graphConsumer,
+ minApiLevel,
+ features,
+ residualArtProfiles,
+ resourceShrinkerOutput,
+ resourceShrinkerOutputForFeatures,
+ buildMetadata);
}
@Override
public R8TestCompileResult self() {
return this;
}
-
- public R8BuildMetadata getBuildMetadata() {
- assert buildMetadata != null;
- return buildMetadata;
- }
-
- @Override
- public TestDiagnosticMessages getDiagnosticMessages() {
- return state.getDiagnosticsMessages();
- }
-
- @Override
- public R8TestCompileResult inspectDiagnosticMessages(Consumer<TestDiagnosticMessages> consumer) {
- consumer.accept(state.getDiagnosticsMessages());
- return self();
- }
-
- public Path getFeature(int index) {
- return features.get(index);
- }
-
- public List<Path> getFeatures() {
- return features;
- }
-
- @Override
- public Set<String> getMainDexClasses() {
- return state.getMainDexClasses();
- }
-
- @Override
- public String getStdout() {
- return state.getStdout();
- }
-
- @Override
- public String getStderr() {
- return state.getStderr();
- }
-
- @Override
- public CodeInspector inspector() throws IOException {
- return inspector(null);
- }
-
- public CodeInspector inspector(Consumer<InternalOptions> debugOptionsConsumer)
- throws IOException {
- return new CodeInspector(app, proguardMap, debugOptionsConsumer);
- }
-
- private CodeInspector featureInspector(Path feature) throws IOException {
- return new CodeInspector(
- AndroidApp.builder().addProgramFile(feature).setProguardMapOutputData(proguardMap).build());
- }
-
- public CodeInspector featureInspector() throws IOException {
- assert features.size() == 1;
- return featureInspector(features.get(0));
- }
-
- @SafeVarargs
- public final <E extends Throwable> R8TestCompileResult inspect(
- ThrowingConsumer<CodeInspector, E>... consumers) throws IOException, E {
- assertEquals(1 + features.size(), consumers.length);
- consumers[0].accept(inspector());
- for (int i = 0; i < features.size(); i++) {
- consumers[i + 1].accept(featureInspector(features.get(i)));
- }
- return self();
- }
-
- @SafeVarargs
- @Override
- public final <E extends Throwable> R8TestCompileResult inspectMultiDex(
- ThrowingConsumer<CodeInspector, E>... consumers) throws E {
- try {
- return inspectMultiDex(writeProguardMap(), consumers);
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- public final <E extends Throwable> R8TestCompileResult inspectGraph(
- ThrowingConsumer<GraphInspector, E> consumer) throws IOException, E {
- consumer.accept(graphInspector());
- return self();
- }
-
- public <E extends Throwable> R8TestCompileResult inspectResidualArtProfile(
- ThrowingConsumer<ArtProfileInspector, E> consumer) throws E, IOException {
- return inspectResidualArtProfile(
- (rewrittenArtProfile, inspector) -> consumer.accept(rewrittenArtProfile));
- }
-
- public <E extends Throwable> R8TestCompileResult inspectResidualArtProfile(
- ThrowingBiConsumer<ArtProfileInspector, CodeInspector, E> consumer) throws E, IOException {
- assertEquals(1, residualArtProfiles.size());
- consumer.accept(new ArtProfileInspector(residualArtProfiles.iterator().next()), inspector());
- return self();
- }
-
- public <E extends Throwable> R8TestCompileResult inspectShrunkenResources(
- Consumer<ResourceTableInspector> consumer) throws IOException {
- assertNotNull(resourceShrinkerOutput);
- consumer.accept(
- new ResourceTableInspector(
- ZipUtils.readSingleEntry(resourceShrinkerOutput, "resources.pb")));
- return self();
- }
-
- public <E extends Throwable> R8TestCompileResult assertResourceFile(String name, boolean present)
- throws IOException {
- assertNotNull(resourceShrinkerOutput);
- assertEquals(ZipUtils.containsEntry(resourceShrinkerOutput, name), present);
- return self();
- }
-
- public <E extends Throwable> R8TestCompileResult assertFeatureResourceFile(
- String name, boolean present, String featureName) throws IOException {
- Path path = resourceShrinkerOutputForFeatures.get(featureName);
- assertEquals(ZipUtils.containsEntry(path, name), present);
- return self();
- }
-
- public String dumpResources() throws IOException {
- ProcessResult processResult = AndroidResourceTestingUtils.dumpWithAapt2(resourceShrinkerOutput);
- assert processResult.exitCode == 0;
- return processResult.stdout;
- }
-
- public <E extends Throwable> R8TestCompileResult inspectShrunkenResourcesForFeature(
- Consumer<ResourceTableInspector> consumer, String featureName) throws IOException {
- Path path = resourceShrinkerOutputForFeatures.get(featureName);
- assertNotNull(path);
- consumer.accept(new ResourceTableInspector(ZipUtils.readSingleEntry(path, "resources.pb")));
- return self();
- }
-
- public GraphInspector graphInspector() throws IOException {
- assert graphConsumer != null;
- return new GraphInspector(graphConsumer, inspector());
- }
-
- public String getProguardConfiguration() {
- return proguardConfiguration;
- }
-
- public R8TestCompileResult inspectProguardConfiguration(Consumer<String> consumer) {
- consumer.accept(getProguardConfiguration());
- return self();
- }
-
- public List<ProguardConfigurationRule> getSyntheticProguardRules() {
- return syntheticProguardRules;
- }
-
- public R8TestCompileResult inspectSyntheticProguardRules(
- Consumer<List<ProguardConfigurationRule>> consumer) {
- consumer.accept(getSyntheticProguardRules());
- return self();
- }
-
- @Override
- public R8TestRunResult createRunResult(TestRuntime runtime, ProcessResult result) {
- return new R8TestRunResult(app, runtime, result, proguardMap, this::graphInspector, state);
- }
-
- public R8TestCompileResult addFeatureSplitsToRunClasspathFiles() {
- return addRunClasspathFiles(features);
- }
-
- public R8TestRunResult runFeature(TestRuntime runtime, Class<?> mainFeatureClass)
- throws IOException {
- return runFeature(runtime, mainFeatureClass, features.get(0));
- }
-
- public R8TestRunResult runFeature(
- TestRuntime runtime, Class<?> mainFeatureClass, Path feature, Path... featureDependencies)
- throws IOException {
- assert getBackend() == runtime.getBackend();
- ClassSubject mainClassSubject = inspector().clazz(SplitRunner.class);
- assertThat("Did you forget a keep rule for the main method?", mainClassSubject, isPresent());
- assertThat(
- "Did you forget a keep rule for the main method?",
- mainClassSubject.mainMethod(),
- isPresent());
- ClassSubject mainFeatureClassSubject = featureInspector(feature).clazz(mainFeatureClass);
- assertThat(
- "Did you forget a keep rule for the run method?", mainFeatureClassSubject, isPresent());
- assertThat(
- "Did you forget a keep rule for the run method?",
- mainFeatureClassSubject.uniqueMethodWithOriginalName("run"),
- isPresent());
- String[] args = new String[2 + featureDependencies.length];
- args[0] = mainFeatureClassSubject.getFinalName();
- args[1] = feature.toString();
- for (int i = 2; i < args.length; i++) {
- args[i] = featureDependencies[i - 2].toString();
- }
- return runArt(runtime, mainClassSubject.getFinalName(), args);
- }
-
- public String getProguardMap() {
- return proguardMap;
- }
-
- public R8TestCompileResult inspectProguardMap(ThrowableConsumer<String> consumer)
- throws Throwable {
- consumer.accept(getProguardMap());
- return this;
- }
-
- public Path writeProguardMap() throws IOException {
- Path file = state.getNewTempFolder().resolve("out.zip");
- writeProguardMap(file);
- return file;
- }
-
- public R8TestCompileResult writeProguardMap(Path path) throws IOException {
- FileUtils.writeTextFile(path, getProguardMap());
- return self();
- }
-
- @Override
- public R8TestCompileResult benchmarkCodeSize(BenchmarkResults results)
- throws IOException, ResourceException {
- if (results.isBenchmarkingCodeSize()) {
- int applicationSizeWithFeatures =
- AndroidApp.builder(app).addProgramFiles(features).build().applicationSize();
- results.addCodeSizeResult(applicationSizeWithFeatures);
- }
- return self();
- }
-
- @Override
- public R8TestCompileResult benchmarkInstructionCodeSize(BenchmarkResults results)
- throws IOException {
- if (results.isBenchmarkingCodeSize()) {
- InstructionCodeSizeResult result = getComposableCodeSize(inspector());
- for (Path feature : features) {
- result.add(getComposableCodeSize(featureInspector(feature)));
- }
- results.addInstructionCodeSizeResult(result.instructionCodeSize);
- results.addComposableInstructionCodeSizeResult(result.composableInstructionCodeSize);
- }
- return self();
- }
-
- private InstructionCodeSizeResult getComposableCodeSize(CodeInspector inspector) {
- DexType composableType =
- inspector.getFactory().createType("Landroidx/compose/runtime/Composable;");
- InstructionCodeSizeResult result = new InstructionCodeSizeResult();
- for (FoundClassSubject classSubject : inspector.allClasses()) {
- DexProgramClass clazz = classSubject.getDexProgramClass();
- for (DexEncodedMethod method : clazz.methods(DexEncodedMethod::hasCode)) {
- int instructionCodeSize = method.getCode().asDexCode().codeSizeInBytes();
- result.instructionCodeSize += instructionCodeSize;
- if (method.annotations().hasAnnotation(composableType)) {
- result.composableInstructionCodeSize += instructionCodeSize;
- }
- }
- }
- return result;
- }
-
- @Override
- public R8TestCompileResult benchmarkDexSegmentsCodeSize(BenchmarkResults results)
- throws IOException, ResourceException {
- if (results.isBenchmarkingCodeSize()) {
- AndroidApp appWithFeatures =
- features.isEmpty() ? app : AndroidApp.builder(app).addProgramFiles(features).build();
- results.addDexSegmentsSizeResult(runDexSegments(appWithFeatures));
- }
- return self();
- }
-
- private Int2ReferenceMap<SegmentInfo> runDexSegments(AndroidApp app)
- throws IOException, ResourceException {
- Map<Integer, SegmentInfo> result = DexSegments.runForTesting(app);
- Int2ReferenceMap<SegmentInfo> rewrittenResult = new Int2ReferenceLinkedOpenHashMap<>();
- rewrittenResult.putAll(result);
- return rewrittenResult;
- }
-
- @Override
- public R8TestCompileResult benchmarkDex2OatCodeSize(BenchmarkResults results) throws IOException {
- if (results.isBenchmarkingCodeSize()) {
- Dex2OatTestRunResult dex2OatTestRunResult =
- runDex2Oat(new DexRuntime(DexVm.Version.LATEST_DEX2OAT));
- results.addDex2OatSizeResult(dex2OatTestRunResult.getOatSizeOrDefault(0));
- }
- return self();
- }
}
diff --git a/src/test/testbase/java/com/android/tools/r8/R8TestCompileResultBase.java b/src/test/testbase/java/com/android/tools/r8/R8TestCompileResultBase.java
new file mode 100644
index 0000000..b38c63c
--- /dev/null
+++ b/src/test/testbase/java/com/android/tools/r8/R8TestCompileResultBase.java
@@ -0,0 +1,403 @@
+// 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;
+
+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.assertNotNull;
+
+import com.android.tools.r8.DexSegments.SegmentInfo;
+import com.android.tools.r8.TestRuntime.DexRuntime;
+import com.android.tools.r8.ToolHelper.DexVm;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils;
+import com.android.tools.r8.androidresources.AndroidResourceTestingUtils.ResourceTableInspector;
+import com.android.tools.r8.benchmarks.BenchmarkResults;
+import com.android.tools.r8.benchmarks.InstructionCodeSizeResult;
+import com.android.tools.r8.dexsplitter.SplitterTestBase.SplitRunner;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.metadata.R8BuildMetadata;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
+import com.android.tools.r8.shaking.CollectingGraphConsumer;
+import com.android.tools.r8.shaking.ProguardConfigurationRule;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.FileUtils;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThrowingBiConsumer;
+import com.android.tools.r8.utils.ThrowingConsumer;
+import com.android.tools.r8.utils.ZipUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.android.tools.r8.utils.graphinspector.GraphInspector;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceLinkedOpenHashMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Consumer;
+
+public abstract class R8TestCompileResultBase<CR extends R8TestCompileResultBase<CR>>
+ extends TestCompileResult<CR, R8TestRunResult> {
+
+ private final String proguardConfiguration;
+ private final List<ProguardConfigurationRule> syntheticProguardRules;
+ private final String proguardMap;
+ private final CollectingGraphConsumer graphConsumer;
+ private final List<Path> features;
+ private final List<ExternalArtProfile> residualArtProfiles;
+ private final Path resourceShrinkerOutput;
+ private final Map<String, Path> resourceShrinkerOutputForFeatures;
+ private final R8BuildMetadata buildMetadata;
+
+ R8TestCompileResultBase(
+ TestState state,
+ OutputMode outputMode,
+ LibraryDesugaringTestConfiguration libraryDesugaringTestConfiguration,
+ AndroidApp app,
+ String proguardConfiguration,
+ List<ProguardConfigurationRule> syntheticProguardRules,
+ String proguardMap,
+ CollectingGraphConsumer graphConsumer,
+ int minApiLevel,
+ List<Path> features,
+ List<ExternalArtProfile> residualArtProfiles,
+ Path resourceShrinkerOutput,
+ HashMap<String, Path> resourceShrinkerOutputForFeatures,
+ R8BuildMetadata buildMetadata) {
+ super(state, app, minApiLevel, outputMode, libraryDesugaringTestConfiguration);
+ this.proguardConfiguration = proguardConfiguration;
+ this.syntheticProguardRules = syntheticProguardRules;
+ this.proguardMap = proguardMap;
+ this.graphConsumer = graphConsumer;
+ this.features = features;
+ this.residualArtProfiles = residualArtProfiles;
+ this.resourceShrinkerOutput = resourceShrinkerOutput;
+ this.resourceShrinkerOutputForFeatures = resourceShrinkerOutputForFeatures;
+ this.buildMetadata = buildMetadata;
+ }
+
+ public CR benchmarkResourceSize(BenchmarkResults results) throws IOException {
+ results.addResourceSizeResult(Files.size(resourceShrinkerOutput));
+ return self();
+ }
+
+ public R8BuildMetadata getBuildMetadata() {
+ assert buildMetadata != null;
+ return buildMetadata;
+ }
+
+ @Override
+ public TestDiagnosticMessages getDiagnosticMessages() {
+ return state.getDiagnosticsMessages();
+ }
+
+ @Override
+ public CR inspectDiagnosticMessages(Consumer<TestDiagnosticMessages> consumer) {
+ consumer.accept(state.getDiagnosticsMessages());
+ return self();
+ }
+
+ public Path getFeature(int index) {
+ return features.get(index);
+ }
+
+ public List<Path> getFeatures() {
+ return features;
+ }
+
+ @Override
+ public Set<String> getMainDexClasses() {
+ return state.getMainDexClasses();
+ }
+
+ @Override
+ public String getStdout() {
+ return state.getStdout();
+ }
+
+ @Override
+ public String getStderr() {
+ return state.getStderr();
+ }
+
+ @Override
+ public CodeInspector inspector() throws IOException {
+ return inspector(null);
+ }
+
+ public CodeInspector inspector(Consumer<InternalOptions> debugOptionsConsumer)
+ throws IOException {
+ return new CodeInspector(app, proguardMap, debugOptionsConsumer);
+ }
+
+ private CodeInspector featureInspector(Path feature) throws IOException {
+ return new CodeInspector(
+ AndroidApp.builder().addProgramFile(feature).setProguardMapOutputData(proguardMap).build());
+ }
+
+ public CodeInspector featureInspector() throws IOException {
+ assert features.size() == 1;
+ return featureInspector(features.get(0));
+ }
+
+ @SafeVarargs
+ public final <E extends Throwable> CR inspect(ThrowingConsumer<CodeInspector, E>... consumers)
+ throws IOException, E {
+ assertEquals(1 + features.size(), consumers.length);
+ consumers[0].accept(inspector());
+ for (int i = 0; i < features.size(); i++) {
+ consumers[i + 1].accept(featureInspector(features.get(i)));
+ }
+ return self();
+ }
+
+ @SafeVarargs
+ @Override
+ public final <E extends Throwable> CR inspectMultiDex(
+ ThrowingConsumer<CodeInspector, E>... consumers) throws E {
+ try {
+ return inspectMultiDex(writeProguardMap(), consumers);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+
+ public final <E extends Throwable> CR inspectGraph(ThrowingConsumer<GraphInspector, E> consumer)
+ throws IOException, E {
+ consumer.accept(graphInspector());
+ return self();
+ }
+
+ public <E extends Throwable> CR inspectResidualArtProfile(
+ ThrowingConsumer<ArtProfileInspector, E> consumer) throws E, IOException {
+ return inspectResidualArtProfile(
+ (rewrittenArtProfile, inspector) -> consumer.accept(rewrittenArtProfile));
+ }
+
+ public <E extends Throwable> CR inspectResidualArtProfile(
+ ThrowingBiConsumer<ArtProfileInspector, CodeInspector, E> consumer) throws E, IOException {
+ assertEquals(1, residualArtProfiles.size());
+ consumer.accept(new ArtProfileInspector(residualArtProfiles.iterator().next()), inspector());
+ return self();
+ }
+
+ public <E extends Throwable> CR inspectShrunkenResources(
+ Consumer<ResourceTableInspector> consumer) throws IOException {
+ assertNotNull(resourceShrinkerOutput);
+ consumer.accept(
+ new ResourceTableInspector(
+ ZipUtils.readSingleEntry(resourceShrinkerOutput, "resources.pb")));
+ return self();
+ }
+
+ public <E extends Throwable> CR assertResourceFile(String name, boolean present)
+ throws IOException {
+ assertNotNull(resourceShrinkerOutput);
+ assertEquals(ZipUtils.containsEntry(resourceShrinkerOutput, name), present);
+ return self();
+ }
+
+ public <E extends Throwable> CR assertFeatureResourceFile(
+ String name, boolean present, String featureName) throws IOException {
+ Path path = resourceShrinkerOutputForFeatures.get(featureName);
+ assertEquals(ZipUtils.containsEntry(path, name), present);
+ return self();
+ }
+
+ public String dumpResources() throws IOException {
+ ProcessResult processResult = AndroidResourceTestingUtils.dumpWithAapt2(resourceShrinkerOutput);
+ assert processResult.exitCode == 0;
+ return processResult.stdout;
+ }
+
+ public <E extends Throwable> CR inspectShrunkenResourcesForFeature(
+ Consumer<ResourceTableInspector> consumer, String featureName) throws IOException {
+ Path path = resourceShrinkerOutputForFeatures.get(featureName);
+ assertNotNull(path);
+ consumer.accept(new ResourceTableInspector(ZipUtils.readSingleEntry(path, "resources.pb")));
+ return self();
+ }
+
+ public GraphInspector graphInspector() throws IOException {
+ assert graphConsumer != null;
+ return new GraphInspector(graphConsumer, inspector());
+ }
+
+ public String getProguardConfiguration() {
+ return proguardConfiguration;
+ }
+
+ public CR inspectProguardConfiguration(Consumer<String> consumer) {
+ consumer.accept(getProguardConfiguration());
+ return self();
+ }
+
+ public List<ProguardConfigurationRule> getSyntheticProguardRules() {
+ return syntheticProguardRules;
+ }
+
+ public CR inspectSyntheticProguardRules(Consumer<List<ProguardConfigurationRule>> consumer) {
+ consumer.accept(getSyntheticProguardRules());
+ return self();
+ }
+
+ @Override
+ public R8TestRunResult createRunResult(TestRuntime runtime, ProcessResult result) {
+ return new R8TestRunResult(app, runtime, result, proguardMap, this::graphInspector, state);
+ }
+
+ public CR addFeatureSplitsToRunClasspathFiles() {
+ return addRunClasspathFiles(features);
+ }
+
+ public R8TestRunResult runFeature(TestRuntime runtime, Class<?> mainFeatureClass)
+ throws IOException {
+ return runFeature(runtime, mainFeatureClass, features.get(0));
+ }
+
+ public R8TestRunResult runFeature(
+ TestRuntime runtime, Class<?> mainFeatureClass, Path feature, Path... featureDependencies)
+ throws IOException {
+ assert getBackend() == runtime.getBackend();
+ ClassSubject mainClassSubject = inspector().clazz(SplitRunner.class);
+ assertThat("Did you forget a keep rule for the main method?", mainClassSubject, isPresent());
+ assertThat(
+ "Did you forget a keep rule for the main method?",
+ mainClassSubject.mainMethod(),
+ isPresent());
+ ClassSubject mainFeatureClassSubject = featureInspector(feature).clazz(mainFeatureClass);
+ assertThat(
+ "Did you forget a keep rule for the run method?", mainFeatureClassSubject, isPresent());
+ assertThat(
+ "Did you forget a keep rule for the run method?",
+ mainFeatureClassSubject.uniqueMethodWithOriginalName("run"),
+ isPresent());
+ String[] args = new String[2 + featureDependencies.length];
+ args[0] = mainFeatureClassSubject.getFinalName();
+ args[1] = feature.toString();
+ for (int i = 2; i < args.length; i++) {
+ args[i] = featureDependencies[i - 2].toString();
+ }
+ return runArt(runtime, mainClassSubject.getFinalName(), args);
+ }
+
+ public CodeInspector inspectorForBase() throws IOException {
+ return new CodeInspector(writeToZip());
+ }
+
+ public CodeInspector inspectorForFeature(int index) throws IOException {
+ return new CodeInspector(getFeature(index));
+ }
+
+ public <E extends Throwable> CR inspectBase(ThrowingConsumer<CodeInspector, E> consumer)
+ throws IOException, E {
+ consumer.accept(inspectorForBase());
+ return self();
+ }
+
+ public <E extends Throwable> CR inspectFeature(
+ int index, ThrowingConsumer<CodeInspector, E> consumer) throws IOException, E {
+ consumer.accept(inspectorForFeature(index));
+ return self();
+ }
+
+ public String getProguardMap() {
+ return proguardMap;
+ }
+
+ public CR inspectProguardMap(ThrowableConsumer<String> consumer) throws Throwable {
+ consumer.accept(getProguardMap());
+ return self();
+ }
+
+ public Path writeProguardMap() throws IOException {
+ Path file = state.getNewTempFolder().resolve("out.zip");
+ writeProguardMap(file);
+ return file;
+ }
+
+ public CR writeProguardMap(Path path) throws IOException {
+ FileUtils.writeTextFile(path, getProguardMap());
+ return self();
+ }
+
+ @Override
+ public CR benchmarkCodeSize(BenchmarkResults results) throws IOException, ResourceException {
+ if (results.isBenchmarkingCodeSize()) {
+ int applicationSizeWithFeatures =
+ AndroidApp.builder(app).addProgramFiles(features).build().applicationSize();
+ results.addCodeSizeResult(applicationSizeWithFeatures);
+ }
+ return self();
+ }
+
+ @Override
+ public CR benchmarkInstructionCodeSize(BenchmarkResults results) throws IOException {
+ if (results.isBenchmarkingCodeSize()) {
+ InstructionCodeSizeResult result = getComposableCodeSize(inspector());
+ for (Path feature : features) {
+ result.add(getComposableCodeSize(featureInspector(feature)));
+ }
+ results.addInstructionCodeSizeResult(result.instructionCodeSize);
+ results.addComposableInstructionCodeSizeResult(result.composableInstructionCodeSize);
+ }
+ return self();
+ }
+
+ private InstructionCodeSizeResult getComposableCodeSize(CodeInspector inspector) {
+ DexType composableType =
+ inspector.getFactory().createType("Landroidx/compose/runtime/Composable;");
+ InstructionCodeSizeResult result = new InstructionCodeSizeResult();
+ for (FoundClassSubject classSubject : inspector.allClasses()) {
+ DexProgramClass clazz = classSubject.getDexProgramClass();
+ for (DexEncodedMethod method : clazz.methods(DexEncodedMethod::hasCode)) {
+ int instructionCodeSize = method.getCode().asDexCode().codeSizeInBytes();
+ result.instructionCodeSize += instructionCodeSize;
+ if (method.annotations().hasAnnotation(composableType)) {
+ result.composableInstructionCodeSize += instructionCodeSize;
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public CR benchmarkDexSegmentsCodeSize(BenchmarkResults results)
+ throws IOException, ResourceException {
+ if (results.isBenchmarkingCodeSize()) {
+ AndroidApp appWithFeatures =
+ features.isEmpty() ? app : AndroidApp.builder(app).addProgramFiles(features).build();
+ results.addDexSegmentsSizeResult(runDexSegments(appWithFeatures));
+ }
+ return self();
+ }
+
+ private Int2ReferenceMap<SegmentInfo> runDexSegments(AndroidApp app)
+ throws IOException, ResourceException {
+ Map<Integer, SegmentInfo> result = DexSegments.runForTesting(app);
+ Int2ReferenceMap<SegmentInfo> rewrittenResult = new Int2ReferenceLinkedOpenHashMap<>();
+ rewrittenResult.putAll(result);
+ return rewrittenResult;
+ }
+
+ @Override
+ public CR benchmarkDex2OatCodeSize(BenchmarkResults results) throws IOException {
+ if (results.isBenchmarkingCodeSize()) {
+ Dex2OatTestRunResult dex2OatTestRunResult =
+ runDex2Oat(new DexRuntime(DexVm.Version.LATEST_DEX2OAT));
+ results.addDex2OatSizeResult(dex2OatTestRunResult.getOatSizeOrDefault(0));
+ }
+ return self();
+ }
+}
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 e3cc4c8..8cdcbc0 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestBase.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestBase.java
@@ -167,6 +167,10 @@
return R8FullTestBuilder.create(new TestState(temp), backend);
}
+ public static R8PartialTestBuilder testForR8Partial(TemporaryFolder temp, Backend backend) {
+ return R8PartialTestBuilder.create(new TestState(temp), backend);
+ }
+
public static R8CompatTestBuilder testForR8Compat(
TemporaryFolder temp, Backend backend, boolean forceProguardCompatibility) {
return R8CompatTestBuilder.create(new TestState(temp), backend, forceProguardCompatibility);
@@ -206,6 +210,10 @@
return testForR8(temp, backend);
}
+ public R8PartialTestBuilder testForR8Partial(Backend backend) {
+ return testForR8Partial(temp, backend);
+ }
+
public R8CompatTestBuilder testForR8Compat(Backend backend) {
return testForR8Compat(backend, true);
}
diff --git a/src/test/testbase/java/com/android/tools/r8/TestBaseBuilder.java b/src/test/testbase/java/com/android/tools/r8/TestBaseBuilder.java
index 98cb209..55d667b 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestBaseBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestBaseBuilder.java
@@ -74,11 +74,33 @@
return self();
}
+ public T addProgramResourceProviders(Collection<ProgramResourceProvider> providers) {
+ for (ProgramResourceProvider provider : providers) {
+ builder.addProgramResourceProvider(provider);
+ }
+ return self();
+ }
+
+ public T addProgramResourceProviders(ProgramResourceProvider... providers) {
+ return addProgramResourceProviders(Arrays.asList(providers));
+ }
+
public T addLibraryProvider(ClassFileResourceProvider provider) {
builder.addLibraryResourceProvider(provider);
return self();
}
+ public T addClasspathResourceProviders(Collection<ClassFileResourceProvider> providers) {
+ for (ClassFileResourceProvider provider : providers) {
+ builder.addClasspathResourceProvider(provider);
+ }
+ return self();
+ }
+
+ public T addClasspathResourceProviders(ClassFileResourceProvider... providers) {
+ return addClasspathResourceProviders(Arrays.asList(providers));
+ }
+
@Override
public T addLibraryFiles(Collection<Path> files) {
builder.addLibraryFiles(files);
@@ -91,6 +113,17 @@
return self();
}
+ public T addLibraryResourceProviders(Collection<ClassFileResourceProvider> providers) {
+ for (ClassFileResourceProvider provider : providers) {
+ builder.addLibraryResourceProvider(provider);
+ }
+ return self();
+ }
+
+ public T addLibraryResourceProviders(ClassFileResourceProvider... providers) {
+ return addLibraryResourceProviders(Arrays.asList(providers));
+ }
+
public T addMainDexListClassReferences(ClassReference... classes) {
return addMainDexListClassReferences(Arrays.asList(classes));
}
diff --git a/src/test/testbase/java/com/android/tools/r8/TestBuilder.java b/src/test/testbase/java/com/android/tools/r8/TestBuilder.java
index 4b18fcb..ea40b20 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestBuilder.java
@@ -88,10 +88,10 @@
return self;
}
- public T applyIfR8(ThrowableConsumer<? super R8TestBuilder<?>> consumer) {
+ public T applyIfR8(ThrowableConsumer<? super R8TestBuilder<?, ?, ?>> consumer) {
T self = self();
- if (this instanceof R8TestBuilder<?>) {
- consumer.acceptWithRuntimeException((R8TestBuilder<?>) self);
+ if (this instanceof R8TestBuilder<?, ?, ?>) {
+ consumer.acceptWithRuntimeException((R8TestBuilder<?, ?, ?>) self);
}
return self;
}
diff --git a/src/test/testbase/java/com/android/tools/r8/TestCompilerBuilder.java b/src/test/testbase/java/com/android/tools/r8/TestCompilerBuilder.java
index 0e28732..c9cc10d 100644
--- a/src/test/testbase/java/com/android/tools/r8/TestCompilerBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/TestCompilerBuilder.java
@@ -118,7 +118,7 @@
return false;
}
- public R8TestBuilder<?> asR8TestBuilder() {
+ public R8TestBuilder<?, ?, ?> asR8TestBuilder() {
return null;
}
diff --git a/src/test/testbase/java/com/android/tools/r8/ToolHelper.java b/src/test/testbase/java/com/android/tools/r8/ToolHelper.java
index 7b76fc0..1a8a049 100644
--- a/src/test/testbase/java/com/android/tools/r8/ToolHelper.java
+++ b/src/test/testbase/java/com/android/tools/r8/ToolHelper.java
@@ -2792,4 +2792,9 @@
assert !strings.isEmpty();
return strings.get(0);
}
+
+ public static void setReadEmbeddedRulesFromClasspathAndLibrary(
+ R8Command.Builder builder, boolean enabled) {
+ builder.setReadEmbeddedRulesFromClasspathAndLibrary(enabled);
+ }
}
diff --git a/src/test/testbase/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java b/src/test/testbase/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
index d9a4981..7aab984 100644
--- a/src/test/testbase/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
+++ b/src/test/testbase/java/com/android/tools/r8/desugar/desugaredlibrary/test/DesugaredLibraryTestBuilder.java
@@ -230,11 +230,11 @@
consumer.accept((D8TestBuilder) builder);
}
- private void withR8TestBuilder(Consumer<R8TestBuilder<?>> consumer) {
+ private void withR8TestBuilder(Consumer<R8TestBuilder<?, ?, ?>> consumer) {
if (!builder.isTestShrinkerBuilder()) {
return;
}
- consumer.accept((R8TestBuilder<?>) builder);
+ consumer.accept((R8TestBuilder<?, ?, ?>) builder);
}
public DesugaredLibraryTestBuilder<T> allowUnusedDontWarnPatterns() {
@@ -267,7 +267,8 @@
return this;
}
- public DesugaredLibraryTestBuilder<T> applyIfR8TestBuilder(Consumer<R8TestBuilder<?>> consumer) {
+ public DesugaredLibraryTestBuilder<T> applyIfR8TestBuilder(
+ Consumer<R8TestBuilder<?, ?, ?>> consumer) {
withR8TestBuilder(consumer);
return this;
}
diff --git a/third_party/kotlin/kotlin-compiler-2.1.0-Beta1.tar.gz.sha1 b/third_party/kotlin/kotlin-compiler-2.1.0-Beta1.tar.gz.sha1
new file mode 100644
index 0000000..3f0f592
--- /dev/null
+++ b/third_party/kotlin/kotlin-compiler-2.1.0-Beta1.tar.gz.sha1
@@ -0,0 +1 @@
+491ee600cbb5b7febfd1ca4bda0d97e5d14aa370
\ No newline at end of file
diff --git a/tools/compiledump.py b/tools/compiledump.py
index 83f775d..c819865 100755
--- a/tools/compiledump.py
+++ b/tools/compiledump.py
@@ -223,6 +223,9 @@
def main_dex_rules_resource(self):
return self.if_exists('main-dex-rules.txt')
+ def resource_ap_file(self):
+ return self.if_exists('app-res.ap_')
+
def art_profile_resources(self):
art_profile_resources = []
while True:
@@ -633,6 +636,10 @@
cmd.append('--isolated-splits')
if dump.library_jar():
cmd.extend(['--lib', dump.library_jar()])
+ if dump.resource_ap_file():
+ res_output = os.path.join(temp, 'ap-res-out.ap_')
+ cmd.extend(['--android-resources', dump.resource_ap_file(),
+ res_output])
if dump.classpath_jar() and not is_l8_compiler(compiler):
cmd.extend([
'--target' if compiler == 'tracereferences' else '--classpath',
@@ -782,6 +789,7 @@
def run(args, otherargs):
+ gradle.EnsureJdk()
if args.summary:
summarize_dump_files(otherargs)
elif args.loop:
diff --git a/tools/tag_versions.py b/tools/tag_versions.py
index 7a6dfc3..cdedf53 100755
--- a/tools/tag_versions.py
+++ b/tools/tag_versions.py
@@ -5,6 +5,7 @@
import argparse
import jdk
+import json
import os.path
import re
import subprocess
@@ -26,7 +27,7 @@
help='The R8 branch to tag versions on, eg, origin/3.0')
parser.add_argument('--agp',
help='The AGP to compute the tag for, eg, 4.2.0-beta03')
- parser.add_argument('--no-push',
+ parser.add_argument('--use-push',
default=False,
action='store_true',
help='To create the tag locally only.')
@@ -40,8 +41,6 @@
def run(options, cmd):
print(' '.join(cmd))
- if 'push' in cmd and options.no_push:
- return
if options.dry_run:
return
try:
@@ -81,6 +80,28 @@
subprocess.check_output(cmd)
return temp
+# Testing info: To delete a tag use
+#
+# gob-curl --request DELETE https://r8-review.git.corp.google.com/a/projects/r8/tags/<tag>
+#
+def gerrit_tag(args, tag, hash, description):
+ data = json.dumps({
+ "message": description if description else tag,
+ "revision": hash
+ })
+ cmd = ' '.join([
+ 'gob-curl',
+ '--header',
+ '"Content-Type: application/json; charset=UTF-8"',
+ '--request',
+ 'PUT',
+ '--data',
+ "'{data}'".format(data=data),
+ 'https://r8-review.git.corp.google.com/a/projects/r8/tags/{tag}'.format(tag=tag)
+ ])
+ result = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
+ print(result)
+
def get_tag_info_on_origin(tag):
output = subprocess.check_output(
@@ -110,8 +131,8 @@
tag_agp_version(version, args)
-def tag_agp_version(agp, args):
- tag = 'agp-%s' % agp
+def tag_agp_version(agp_version, args):
+ tag = 'agp-%s' % agp_version
result = get_tag_info_on_origin(tag)
if result:
print('Tag %s is already present' % tag)
@@ -119,12 +140,12 @@
subprocess.call(['git', 'show', '--oneline', '-s', tag])
return 0
with utils.TempDir() as temp:
- url = "%s/%s/builder-%s.jar" % (AGP_MAVEN, agp, agp)
+ url = "%s/%s/builder-%s.jar" % (AGP_MAVEN, agp_version, agp_version)
jar = os.path.join(temp, "agp.jar")
try:
urllib.request.urlretrieve(url, jar)
except urllib.error.HTTPError as e:
- print('Could not find jar for agp %s' % agp)
+ print('Could not find jar for agp %s' % agp_version)
print(e)
return 1
print_version_helper = prepare_print_version(utils.R8_JAR, temp)
@@ -133,11 +154,17 @@
':'.join([jar, print_version_helper]),
'com.android.tools.r8.utils.PrintR8Version'
]).decode('utf-8')
- version = output.split(' ')[0]
- run(args, ['git', 'tag', '-f', tag, '-m', tag, '%s^{}' % version])
- run(args, [
- 'git', 'push', '-o', 'push-justification=b/313360935', 'origin', tag
- ])
+ r8_version = output.split(' ')[0]
+ cmd = ' '.join(['git', 'rev-list', '-n', '1', r8_version])
+ hash = subprocess.check_output(cmd, shell=True).decode('utf-8').strip()
+ message = 'AGP version {version}'.format(version=agp_version)
+ run(args, ['git', 'tag', '-f', tag, '-m', message, hash])
+ if args.use_push:
+ run(args, [
+ 'git', 'push', '-o', 'push-justification=b/313360935', 'origin', tag
+ ])
+ else:
+ gerrit_tag(args, tag, hash, message)
def tag_r8_branch(branch, args):
@@ -156,11 +183,15 @@
version = m.group(1)
result = get_tag_info_on_origin(version)
if not result:
- run(args, ['git', 'tag', '-a', version, '-m', version, hash])
- run(args, [
- 'git', 'push', '-o', 'push-justification=b/313360935', 'origin',
- version
- ])
+ message = 'Version {version}'.format(version=version)
+ run(args, ['git', 'tag', '-a', version, '-m', message, hash])
+ if args.use_push:
+ run(args, [
+ 'git', 'push', '-o', 'push-justification=b/313360935', 'origin',
+ version
+ ])
+ else:
+ gerrit_tag(args, version, hash, message)
if args.dry_run:
print('Dry run complete. None of the above have been executed.')