Merge commit '6ae4611db18f799a890c9de3a6e15cd6702ad90c' into dev-release
diff --git a/build.gradle b/build.gradle
index 870b615..e7aaa5d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -360,6 +360,7 @@
"android_jar/lib-v31",
"android_jar/lib-v32",
"android_jar/lib-v33",
+ "android_jar/lib-v34",
"android_jar/lib-master",
"api_database/api_database",
"api-outlining/simple-app-dump",
@@ -2298,6 +2299,12 @@
systemProperty 'runtimes', project.property('runtimes')
}
+ if (project.hasProperty('art_profile_rewriting_completeness_check')) {
+ String key = 'com.android.tools.r8.artprofilerewritingcompletenesscheck'
+ String value = project.property('art_profile_rewriting_completeness_check')
+ systemProperty key, value
+ }
+
if (project.hasProperty('slow_tests')) {
systemProperty 'slow_tests', project.property('slow_tests')
}
diff --git a/src/main/java/com/android/tools/r8/D8.java b/src/main/java/com/android/tools/r8/D8.java
index 8cf01de..7508aa1 100644
--- a/src/main/java/com/android/tools/r8/D8.java
+++ b/src/main/java/com/android/tools/r8/D8.java
@@ -23,7 +23,7 @@
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
-import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.PrimaryD8L8IRConverter;
import com.android.tools.r8.ir.desugar.TypeRewriter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAmender;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
@@ -50,10 +50,7 @@
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -246,19 +243,8 @@
appView.setAppServices(AppServices.builder(appView).build());
}
timing.end();
- new IRConverter(appView, timing, printer).convert(appView, executor);
+ new PrimaryD8L8IRConverter(appView, timing).convert(appView, executor);
timing.begin("Post conversion");
- if (options.printCfg) {
- if (options.printCfgFile == null || options.printCfgFile.isEmpty()) {
- System.out.print(printer.toString());
- } else {
- try (OutputStreamWriter writer =
- new OutputStreamWriter(
- new FileOutputStream(options.printCfgFile), StandardCharsets.UTF_8)) {
- writer.write(printer.toString());
- }
- }
- }
// Close any internal archive providers now the application is fully processed.
inputApp.closeInternalArchiveProviders();
@@ -450,26 +436,6 @@
return finalDexApp.build();
}
- static void optimize(
- AppView<AppInfo> appView, InternalOptions options, Timing timing, ExecutorService executor)
- throws IOException, ExecutionException {
- final CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
-
- new IRConverter(appView, timing, printer).convert(appView, executor);
-
- if (options.printCfg) {
- if (options.printCfgFile == null || options.printCfgFile.isEmpty()) {
- System.out.print(printer.toString());
- } else {
- try (OutputStreamWriter writer =
- new OutputStreamWriter(
- new FileOutputStream(options.printCfgFile), StandardCharsets.UTF_8)) {
- writer.write(printer.toString());
- }
- }
- }
- }
-
static class ConvertedCfFiles implements DexIndexedConsumer, ProgramResourceProvider {
private final List<ProgramResource> resources = new ArrayList<>();
@@ -484,7 +450,7 @@
}
@Override
- public Collection<ProgramResource> getProgramResources() throws ResourceException {
+ public Collection<ProgramResource> getProgramResources() {
return resources;
}
diff --git a/src/main/java/com/android/tools/r8/L8.java b/src/main/java/com/android/tools/r8/L8.java
index 69714e7..7950354 100644
--- a/src/main/java/com/android/tools/r8/L8.java
+++ b/src/main/java/com/android/tools/r8/L8.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.LazyLoadedDexApplication;
-import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.PrimaryD8L8IRConverter;
import com.android.tools.r8.ir.desugar.TypeRewriter;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAmender;
import com.android.tools.r8.jar.CfApplicationWriter;
@@ -140,7 +140,7 @@
AnnotationRemover.clearAnnotations(appView);
}
- new IRConverter(appView, timing).convert(appView, executor);
+ new PrimaryD8L8IRConverter(appView, timing).convert(appView, executor);
SyntheticFinalization.finalize(appView, timing, executor);
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index d9e374d..97467f1 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8;
+import static com.android.tools.r8.profile.art.ArtProfileCompletenessChecker.CompletenessExceptions.ALLOW_MISSING_ENUM_UNBOXING_UTILITY_METHODS;
import static com.android.tools.r8.utils.AssertionUtils.forTesting;
import static com.android.tools.r8.utils.ExceptionUtils.unwrapExecutionException;
@@ -18,7 +19,6 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.AppliedGraphLens;
import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
@@ -39,6 +39,7 @@
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger;
import com.android.tools.r8.inspector.internal.InspectorImpl;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.PrimaryR8IRConverter;
import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringCollection;
import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
@@ -71,6 +72,8 @@
import com.android.tools.r8.optimize.interfaces.analysis.CfOpenClosedInterfacesAnalysis;
import com.android.tools.r8.optimize.proto.ProtoNormalizer;
import com.android.tools.r8.origin.CommandLineOrigin;
+import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
import com.android.tools.r8.repackaging.Repackaging;
import com.android.tools.r8.repackaging.RepackagingLens;
import com.android.tools.r8.shaking.AbstractMethodRemover;
@@ -100,7 +103,6 @@
import com.android.tools.r8.synthesis.SyntheticFinalization;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.AndroidApp;
-import com.android.tools.r8.utils.CfgPrinter;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.SelfRetraceTest;
@@ -111,11 +113,8 @@
import com.google.common.collect.Iterables;
import com.google.common.io.ByteStreams;
import java.io.ByteArrayOutputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.OutputStreamWriter;
import java.io.PrintStream;
-import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -278,6 +277,8 @@
SyntheticItems.collectSyntheticInputs(appView);
}
+ assert ArtProfileCompletenessChecker.verify(appView);
+
// Check for potentially having pass-through of Cf-code for kotlin libraries.
options.enableCfByteCodePassThrough =
options.isGeneratingClassFiles() && KotlinMetadataUtils.mayProcessKotlinMetadata(appView);
@@ -302,10 +303,13 @@
appView.dexItemFactory());
// Upfront desugaring generation: Generates new program classes to be added in the app.
+ ArtProfileCollectionAdditions artProfileCollectionAdditions =
+ ArtProfileCollectionAdditions.create(appView);
CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer =
- new CfClassSynthesizerDesugaringEventConsumer();
+ CfClassSynthesizerDesugaringEventConsumer.create(artProfileCollectionAdditions);
CfClassSynthesizerDesugaringCollection.create(appView)
.synthesizeClasses(executorService, classSynthesizerEventConsumer);
+ artProfileCollectionAdditions.commit(appView);
if (appView.getSyntheticItems().hasPendingSyntheticClasses()) {
appView.setAppInfo(
appView
@@ -370,6 +374,7 @@
assert appView.rootSet().verifyKeptMethodsAreTargetedAndLive(appViewWithLiveness);
assert appView.rootSet().verifyKeptTypesAreLive(appViewWithLiveness);
assert appView.rootSet().verifyKeptItemsAreKept(appView);
+ assert ArtProfileCompletenessChecker.verify(appView);
appView.rootSet().checkAllRulesAreUsed(options);
if (options.apiModelingOptions().reportUnknownApiReferences) {
@@ -478,6 +483,8 @@
classMergingEnqueuerExtensionBuilder.build(appView.graphLens());
classMergingEnqueuerExtensionBuilder = null;
+ assert ArtProfileCompletenessChecker.verify(appView);
+
if (!isKotlinLibraryCompilationWithInlinePassThrough
&& options.getProguardConfiguration().isOptimizing()) {
if (options.enableVerticalClassMerging) {
@@ -498,6 +505,8 @@
}
assert appView.verticallyMergedClasses() != null;
+ assert ArtProfileCompletenessChecker.verify(appView);
+
HorizontalClassMerger.createForInitialClassMerging(appViewWithLiveness)
.runIfNecessary(executorService, timing, runtimeTypeCheckInfo);
}
@@ -520,16 +529,13 @@
// TODO: we should avoid removing liveness.
Set<DexType> prunedTypes = appView.withLiveness().appInfo().getPrunedTypes();
- timing.begin("Create IR");
- CfgPrinter printer = options.printCfg ? new CfgPrinter() : null;
- try {
- IRConverter converter = new IRConverter(appView, timing, printer);
- DexApplication application =
- converter.optimize(appViewWithLiveness, executorService).asDirect();
- appView.setAppInfo(appView.appInfo().rebuildWithClassHierarchy(previous -> application));
- } finally {
- timing.end();
- }
+ assert ArtProfileCompletenessChecker.verify(appView);
+
+ new PrimaryR8IRConverter(appViewWithLiveness, timing)
+ .optimize(appViewWithLiveness, executorService);
+
+ assert ArtProfileCompletenessChecker.verify(
+ appView, ALLOW_MISSING_ENUM_UNBOXING_UTILITY_METHODS);
// Clear the reference type lattice element cache to reduce memory pressure.
appView.dexItemFactory().clearTypeElementsCache();
@@ -541,18 +547,6 @@
appView.setGraphLens(new AppliedGraphLens(appView));
timing.end();
- if (options.printCfg) {
- if (options.printCfgFile == null || options.printCfgFile.isEmpty()) {
- System.out.print(printer.toString());
- } else {
- try (OutputStreamWriter writer = new OutputStreamWriter(
- new FileOutputStream(options.printCfgFile),
- StandardCharsets.UTF_8)) {
- writer.write(printer.toString());
- }
- }
- }
-
if (options.shouldRerunEnqueuer()) {
timing.begin("Post optimization code stripping");
try {
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index c874ba9..b4cca13 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -45,9 +45,12 @@
import com.android.tools.r8.utils.InternalOptions.LineNumberOptimization;
import com.android.tools.r8.utils.ProgramClassCollection;
import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.SemanticVersion;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringDiagnostic;
+import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.ThreadUtils;
+import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import java.io.InputStream;
import java.nio.file.Path;
@@ -64,6 +67,7 @@
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Supplier;
/**
* Immutable command structure for an invocation of the {@link R8} compiler.
@@ -127,6 +131,7 @@
private String synthesizedClassPrefix = "";
private boolean enableMissingLibraryApiModeling = false;
private boolean enableExperimentalKeepAnnotations = false;
+ private SemanticVersion fakeCompilerVersion = null;
private final ProguardConfigurationParserOptions.Builder parserOptionsBuilder =
ProguardConfigurationParserOptions.builder().readEnvironment();
@@ -174,6 +179,11 @@
return self();
}
+ Builder setFakeCompilerVersion(SemanticVersion version) {
+ fakeCompilerVersion = version;
+ return self();
+ }
+
/**
* Disable tree shaking.
*
@@ -588,7 +598,10 @@
if (proguardConfigurationConsumerForTesting != null) {
proguardConfigurationConsumerForTesting.accept(configurationBuilder);
}
+
+ // Add embedded keep rules.
amendWithRulesAndProvidersForInjarsAndMetaInf(reporter, parser);
+
// Extract out rules for keep annotations and amend the configuration.
// TODO(b/248408342): Remove this and parse annotations as part of R8 root-set & enqueuer.
extractKeepAnnotationRules(parser);
@@ -645,7 +658,8 @@
getAndroidPlatformBuild(),
getArtProfilesForRewriting(),
getStartupProfileProviders(),
- getClassConflictResolver());
+ getClassConflictResolver(),
+ fakeCompilerVersion);
if (inputDependencyGraphConsumer != null) {
inputDependencyGraphConsumer.finished();
@@ -656,32 +670,6 @@
private void amendWithRulesAndProvidersForInjarsAndMetaInf(
Reporter reporter, ProguardConfigurationParser parser) {
- // Process Proguard configurations supplied through data resources in the input.
- DataResourceProvider.Visitor embeddedProguardConfigurationVisitor =
- new DataResourceProvider.Visitor() {
- @Override
- public void visit(DataDirectoryResource directory) {
- // Don't do anything.
- }
-
- @Override
- public void visit(DataEntryResource resource) {
- if (resource.getName().startsWith("META-INF/proguard/")) {
- try (InputStream in = resource.getByteStream()) {
- ProguardConfigurationSource source =
- new ProguardConfigurationSourceBytes(in, resource.getOrigin());
- parser.parse(source);
- } catch (ResourceException e) {
- reporter.error(
- new StringDiagnostic(
- "Failed to open input: " + e.getMessage(), resource.getOrigin()));
- } catch (Exception e) {
- reporter.error(new ExceptionDiagnostic(e, resource.getOrigin()));
- }
- }
- }
- };
-
// 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.
@@ -700,11 +688,39 @@
if (providers.isEmpty()) {
return;
}
+
+ Supplier<SemanticVersion> semanticVersionSupplier =
+ Suppliers.memoize(
+ () -> {
+ SemanticVersion compilerVersion =
+ fakeCompilerVersion == null
+ ? SemanticVersion.create(
+ Version.getMajorVersion(),
+ Version.getMinorVersion(),
+ Version.getPatchVersion())
+ : fakeCompilerVersion;
+ if (compilerVersion.getMajor() < 0) {
+ compilerVersion = SemanticVersion.parse(Version.ACTIVE_DEV_VERSION);
+ reporter.warning(
+ "Running R8 version "
+ + Version.getVersionString()
+ + " which cannot be represented as a semantic version. Using"
+ + " version "
+ + compilerVersion
+ + " for selecting Proguard configurations embedded under"
+ + " META-INF/");
+ }
+ 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));
}
@@ -828,6 +844,7 @@
private final FeatureSplitConfiguration featureSplitConfiguration;
private final String synthesizedClassPrefix;
private final boolean enableMissingLibraryApiModeling;
+ private final SemanticVersion fakeCompilerVersion;
/** Get a new {@link R8Command.Builder}. */
public static Builder builder() {
@@ -920,7 +937,8 @@
boolean isAndroidPlatformBuild,
List<ArtProfileForRewriting> artProfilesForRewriting,
List<StartupProfileProvider> startupProfileProviders,
- ClassConflictResolver classConflictResolver) {
+ ClassConflictResolver classConflictResolver,
+ SemanticVersion fakeCompilerVersion) {
super(
inputApp,
mode,
@@ -963,6 +981,7 @@
this.featureSplitConfiguration = featureSplitConfiguration;
this.synthesizedClassPrefix = synthesizedClassPrefix;
this.enableMissingLibraryApiModeling = enableMissingLibraryApiModeling;
+ this.fakeCompilerVersion = fakeCompilerVersion;
}
private R8Command(boolean printHelp, boolean printVersion) {
@@ -986,6 +1005,7 @@
featureSplitConfiguration = null;
synthesizedClassPrefix = null;
enableMissingLibraryApiModeling = false;
+ fakeCompilerVersion = null;
}
public DexItemFactory getDexItemFactory() {
@@ -1204,4 +1224,126 @@
.setEnableMissingLibraryApiModeling(enableMissingLibraryApiModeling)
.build();
}
+
+ private static class ExtractEmbeddedRules implements DataResourceProvider.Visitor {
+
+ private final Supplier<SemanticVersion> compilerVersionSupplier;
+ private final Reporter reporter;
+ private final List<ProguardConfigurationSource> proguardSources = new ArrayList<>();
+ private final List<ProguardConfigurationSource> r8Sources = new ArrayList<>();
+ private SemanticVersion compilerVersion;
+
+ public ExtractEmbeddedRules(
+ Reporter reporter, Supplier<SemanticVersion> compilerVersionSupplier) {
+ this.compilerVersionSupplier = compilerVersionSupplier;
+ this.reporter = reporter;
+ }
+
+ @Override
+ public void visit(DataDirectoryResource directory) {
+ // Don't do anything.
+ }
+
+ @Override
+ public void visit(DataEntryResource resource) {
+ if (relevantProguardResource(resource)) {
+ assert !relevantR8Resource(resource);
+ readProguardConfigurationSource(resource, proguardSources::add);
+ } else if (relevantR8Resource(resource)) {
+ assert !relevantProguardResource(resource);
+ readProguardConfigurationSource(resource, r8Sources::add);
+ }
+ }
+
+ private void readProguardConfigurationSource(
+ DataEntryResource resource, Consumer<ProguardConfigurationSource> consumer) {
+ try (InputStream in = resource.getByteStream()) {
+ consumer.accept(new ProguardConfigurationSourceBytes(in, resource.getOrigin()));
+ } catch (ResourceException e) {
+ reporter.error(
+ new StringDiagnostic("Failed to open input: " + e.getMessage(), resource.getOrigin()));
+ } catch (Exception e) {
+ reporter.error(new ExceptionDiagnostic(e, resource.getOrigin()));
+ }
+ }
+
+ private boolean relevantProguardResource(DataEntryResource resource) {
+ // Configurations in META-INF/com.android.tools/proguard/ are ignored.
+ final String proguardPrefix = "META-INF/proguard";
+ if (!resource.getName().startsWith(proguardPrefix)) {
+ return false;
+ }
+ String withoutPrefix = resource.getName().substring(proguardPrefix.length());
+ return withoutPrefix.startsWith("/");
+ }
+
+ private boolean relevantR8Resource(DataEntryResource resource) {
+ final String r8Prefix = "META-INF/com.android.tools/r8";
+ if (!resource.getName().startsWith(r8Prefix)) {
+ return false;
+ }
+ String withoutPrefix = resource.getName().substring(r8Prefix.length());
+ if (withoutPrefix.startsWith("/")) {
+ // Everything under META-INF/com.android.tools/r8/ is included (not version specific).
+ return true;
+ }
+ // Expect one of the following patterns:
+ // com.android.tools/r8-min-1.5.0/
+ // com.android.tools/r8-max-1.5.99/
+ // com.android.tools/r8-min-1.5.0-max-1.5.99/
+ final String minPrefix = "-min-";
+ final String maxPrefix = "-max-";
+ if (!withoutPrefix.startsWith(minPrefix) && !withoutPrefix.startsWith(maxPrefix)) {
+ return false;
+ }
+
+ SemanticVersion from = SemanticVersion.min();
+ SemanticVersion to = SemanticVersion.max();
+
+ if (withoutPrefix.startsWith(minPrefix)) {
+ withoutPrefix = withoutPrefix.substring(minPrefix.length());
+ int versionEnd = StringUtils.indexOf(withoutPrefix, '-', '/');
+ if (versionEnd == -1) {
+ return false;
+ }
+ try {
+ from = SemanticVersion.parse(withoutPrefix.substring(0, versionEnd));
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ withoutPrefix = withoutPrefix.substring(versionEnd);
+ }
+ if (withoutPrefix.startsWith(maxPrefix)) {
+ withoutPrefix = withoutPrefix.substring(maxPrefix.length());
+ int versionEnd = withoutPrefix.indexOf('/');
+ if (versionEnd == -1) {
+ return false;
+ }
+ try {
+ to = SemanticVersion.parse(withoutPrefix.substring(0, versionEnd));
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
+ if (compilerVersion == null) {
+ compilerVersion = compilerVersionSupplier.get();
+ }
+ return compilerVersion.isNewerOrEqual(from) && to.isNewerOrEqual(compilerVersion);
+ }
+
+ private void parse(
+ List<ProguardConfigurationSource> sources, ProguardConfigurationParser parser) {
+ for (ProguardConfigurationSource source : sources) {
+ try {
+ parser.parse(source);
+ } catch (Exception e) {
+ reporter.error(new ExceptionDiagnostic(e, source.getOrigin()));
+ }
+ }
+ }
+
+ void parseRelevantRules(ProguardConfigurationParser parser) {
+ parse(!r8Sources.isEmpty() ? r8Sources : proguardSources, parser);
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 5dd6284..6eac09c 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -9,10 +9,17 @@
/** Version of the D8/R8 library. */
public final class Version {
+ private static final String MAIN_LABEL = "main";
+
// This field is accessed from release scripts using simple pattern matching.
// Therefore, changing this field could break our release scripts.
public static final String LABEL = "8.1.18-dev";
+ // The prefix of the active dev version line being worked on from this branch. This is used in the
+ // few cases where the compiler makes decisions based in the compiler version and where version
+ // 'main' cannot be used.
+ public static final String ACTIVE_DEV_VERSION = "8.1.0";
+
private Version() {
}
@@ -31,7 +38,7 @@
}
static int getMajorVersion(String label) {
- if (label.equals("main")) {
+ if (label.equals(MAIN_LABEL)) {
return -1;
}
int start = 0;
@@ -49,7 +56,7 @@
}
static int getMinorVersion(String label) {
- if (label.equals("main")) {
+ if (label.equals(MAIN_LABEL)) {
return -1;
}
int start = label.indexOf('.') + 1;
@@ -67,7 +74,7 @@
}
static int getPatchVersion(String label) {
- if (label.equals("main")) {
+ if (label.equals(MAIN_LABEL)) {
return -1;
}
int skip = label.indexOf('.') + 1;
@@ -87,7 +94,7 @@
}
static String getPreReleaseString(String label) {
- if (label.equals("main")) {
+ if (label.equals(MAIN_LABEL)) {
return null;
}
int start = label.indexOf('-') + 1;
@@ -107,6 +114,10 @@
}
static boolean isDevelopmentVersion(String label, boolean isEngineering) {
- return label.equals("main") || label.endsWith("-dev") || isEngineering;
+ return label.equals(MAIN_LABEL) || label.endsWith("-dev") || isEngineering;
+ }
+
+ public static boolean isMainVersion() {
+ return LABEL.equals(MAIN_LABEL);
}
}
diff --git a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
index 35eb379..fb97359 100644
--- a/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
+++ b/src/main/java/com/android/tools/r8/androidapi/AndroidApiLevelCompute.java
@@ -19,7 +19,7 @@
private final KnownApiLevel[] knownApiLevelCache;
public AndroidApiLevelCompute() {
- knownApiLevelCache = new KnownApiLevel[AndroidApiLevel.LATEST.getLevel() + 1];
+ knownApiLevelCache = new KnownApiLevel[AndroidApiLevel.API_DATABASE_LEVEL.getLevel() + 1];
for (AndroidApiLevel value : AndroidApiLevel.values()) {
if (value != AndroidApiLevel.ANDROID_PLATFORM && value != AndroidApiLevel.MASTER) {
knownApiLevelCache[value.getLevel()] = new KnownApiLevel(value);
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
index 586e8e5..c1af115 100644
--- a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
@@ -20,7 +20,6 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessFlags;
-import com.android.tools.r8.graph.ProgramDefinition;
import com.android.tools.r8.graph.ThrowExceptionCode;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.synthesis.CommittedItems;
@@ -42,50 +41,52 @@
public class ApiReferenceStubber {
private final AppView<?> appView;
- private final Map<DexLibraryClass, Set<ProgramDefinition>> referencingContexts =
+ private final Map<DexLibraryClass, Set<DexProgramClass>> referencingContexts =
new ConcurrentHashMap<>();
private final Set<DexLibraryClass> libraryClassesToMock = Sets.newConcurrentHashSet();
private final Set<DexType> seenTypes = Sets.newConcurrentHashSet();
private final AndroidApiLevelCompute apiLevelCompute;
+ private final ApiReferenceStubberEventConsumer eventConsumer;
public ApiReferenceStubber(AppView<?> appView) {
this.appView = appView;
- apiLevelCompute = appView.apiLevelCompute();
+ this.apiLevelCompute = appView.apiLevelCompute();
+ this.eventConsumer = ApiReferenceStubberEventConsumer.create(appView);
}
public void run(ExecutorService executorService) throws ExecutionException {
- if (appView.options().isGeneratingClassFiles()
- || !appView.options().apiModelingOptions().enableStubbingOfClasses) {
- return;
+ if (appView.options().isGeneratingDex()
+ && appView.options().apiModelingOptions().enableStubbingOfClasses) {
+ ThreadUtils.processItems(appView.appInfo().classes(), this::processClass, executorService);
}
- ThreadUtils.processItems(appView.appInfo().classes(), this::processClass, executorService);
- if (libraryClassesToMock.isEmpty()) {
- return;
+ if (!libraryClassesToMock.isEmpty()) {
+ libraryClassesToMock.forEach(
+ clazz ->
+ mockMissingLibraryClass(
+ clazz,
+ ThrowExceptionCode.create(appView.dexItemFactory().noClassDefFoundErrorType),
+ eventConsumer));
+ // Commit the synthetic items.
+ CommittedItems committedItems = appView.getSyntheticItems().commit(appView.appInfo().app());
+ if (appView.hasLiveness()) {
+ AppView<AppInfoWithLiveness> appInfoWithLivenessAppView = appView.withLiveness();
+ appInfoWithLivenessAppView.setAppInfo(
+ appInfoWithLivenessAppView.appInfo().rebuildWithLiveness(committedItems));
+ } else if (appView.hasClassHierarchy()) {
+ appView
+ .withClassHierarchy()
+ .setAppInfo(
+ appView.appInfo().withClassHierarchy().rebuildWithClassHierarchy(committedItems));
+ } else {
+ appView
+ .withoutClassHierarchy()
+ .setAppInfo(
+ new AppInfo(
+ appView.appInfo().getSyntheticItems().commit(appView.app()),
+ appView.appInfo().getMainDexInfo()));
+ }
}
- libraryClassesToMock.forEach(
- clazz ->
- mockMissingLibraryClass(
- clazz,
- ThrowExceptionCode.create(appView.dexItemFactory().noClassDefFoundErrorType)));
- // Commit the synthetic items.
- CommittedItems committedItems = appView.getSyntheticItems().commit(appView.appInfo().app());
- if (appView.hasLiveness()) {
- AppView<AppInfoWithLiveness> appInfoWithLivenessAppView = appView.withLiveness();
- appInfoWithLivenessAppView.setAppInfo(
- appInfoWithLivenessAppView.appInfo().rebuildWithLiveness(committedItems));
- } else if (appView.hasClassHierarchy()) {
- appView
- .withClassHierarchy()
- .setAppInfo(
- appView.appInfo().withClassHierarchy().rebuildWithClassHierarchy(committedItems));
- } else {
- appView
- .withoutClassHierarchy()
- .setAppInfo(
- new AppInfo(
- appView.appInfo().getSyntheticItems().commit(appView.app()),
- appView.appInfo().getMainDexInfo()));
- }
+ eventConsumer.finished(appView);
}
public void processClass(DexProgramClass clazz) {
@@ -142,7 +143,8 @@
private void mockMissingLibraryClass(
DexLibraryClass libraryClass,
- ThrowExceptionCode throwExceptionCode) {
+ ThrowExceptionCode throwExceptionCode,
+ ApiReferenceStubberEventConsumer eventConsumer) {
DexItemFactory factory = appView.dexItemFactory();
// Do not stub the anything starting with java (including the object type).
if (libraryClass.getType() == appView.dexItemFactory().objectType
@@ -159,38 +161,44 @@
.isSupported(libraryClass.getType())) {
return;
}
- Set<ProgramDefinition> contexts = referencingContexts.get(libraryClass);
+ Set<DexProgramClass> contexts = referencingContexts.get(libraryClass);
if (contexts == null) {
throw new Unreachable("Attempt to create a global synthetic with no contexts");
}
- appView
- .appInfo()
- .getSyntheticItems()
- .ensureGlobalClass(
- () -> new MissingGlobalSyntheticsConsumerDiagnostic("API stubbing"),
- kinds -> kinds.API_MODEL_STUB,
- libraryClass.getType(),
- contexts,
- appView,
- classBuilder -> {
- classBuilder
- .setSuperType(libraryClass.getSuperType())
- .setInterfaces(Arrays.asList(libraryClass.getInterfaces().values))
- // Add throwing static initializer
- .addMethod(
- methodBuilder ->
- methodBuilder
- .setName(factory.classConstructorMethodName)
- .setProto(factory.createProto(factory.voidType))
- .setAccessFlags(MethodAccessFlags.createForClassInitializer())
- .setCode(method -> throwExceptionCode));
- if (libraryClass.isInterface()) {
- classBuilder.setInterface();
- }
- if (!libraryClass.isFinal()) {
- classBuilder.unsetFinal();
- }
- },
- ignored -> {});
+ DexProgramClass mockClass =
+ appView
+ .appInfo()
+ .getSyntheticItems()
+ .ensureGlobalClass(
+ () -> new MissingGlobalSyntheticsConsumerDiagnostic("API stubbing"),
+ kinds -> kinds.API_MODEL_STUB,
+ libraryClass.getType(),
+ contexts,
+ appView,
+ classBuilder -> {
+ classBuilder
+ .setSuperType(libraryClass.getSuperType())
+ .setInterfaces(Arrays.asList(libraryClass.getInterfaces().values))
+ // Add throwing static initializer
+ .addMethod(
+ methodBuilder ->
+ methodBuilder
+ .setName(factory.classConstructorMethodName)
+ .setProto(factory.createProto(factory.voidType))
+ .setAccessFlags(MethodAccessFlags.createForClassInitializer())
+ .setCode(method -> throwExceptionCode));
+ if (libraryClass.isInterface()) {
+ classBuilder.setInterface();
+ }
+ if (!libraryClass.isFinal()) {
+ classBuilder.unsetFinal();
+ }
+ },
+ clazz -> eventConsumer.acceptMockedLibraryClass(clazz, libraryClass));
+ if (!eventConsumer.isEmpty()) {
+ for (DexProgramClass context : contexts) {
+ eventConsumer.acceptMockedLibraryClassContext(mockClass, libraryClass, context);
+ }
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubberEventConsumer.java b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubberEventConsumer.java
new file mode 100644
index 0000000..ac79346
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubberEventConsumer.java
@@ -0,0 +1,73 @@
+// Copyright (c) 2023, 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.androidapi;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingApiReferenceStubberEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ConcreteArtProfileCollectionAdditions;
+
+public interface ApiReferenceStubberEventConsumer {
+
+ void acceptMockedLibraryClass(DexProgramClass mockClass, DexLibraryClass libraryClass);
+
+ void acceptMockedLibraryClassContext(
+ DexProgramClass mockClass, DexLibraryClass libraryClass, DexProgramClass context);
+
+ default void finished(AppView<?> appView) {}
+
+ boolean isEmpty();
+
+ static ApiReferenceStubberEventConsumer create(AppView<?> appView) {
+ if (appView.options().getArtProfileOptions().isIncludingApiReferenceStubs()) {
+ ArtProfileCollectionAdditions artProfileCollectionAdditions =
+ ArtProfileCollectionAdditions.create(appView);
+ if (!artProfileCollectionAdditions.isNop()) {
+ return create(artProfileCollectionAdditions.asConcrete());
+ }
+ }
+ return empty();
+ }
+
+ static ApiReferenceStubberEventConsumer create(
+ ConcreteArtProfileCollectionAdditions artProfileCollectionAdditions) {
+ return ArtProfileRewritingApiReferenceStubberEventConsumer.attach(
+ artProfileCollectionAdditions, empty());
+ }
+
+ static EmptyApiReferenceStubberEventConsumer empty() {
+ return EmptyApiReferenceStubberEventConsumer.getInstance();
+ }
+
+ class EmptyApiReferenceStubberEventConsumer implements ApiReferenceStubberEventConsumer {
+
+ private static final EmptyApiReferenceStubberEventConsumer INSTANCE =
+ new EmptyApiReferenceStubberEventConsumer();
+
+ private EmptyApiReferenceStubberEventConsumer() {}
+
+ static EmptyApiReferenceStubberEventConsumer getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public void acceptMockedLibraryClass(DexProgramClass mockClass, DexLibraryClass libraryClass) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptMockedLibraryClassContext(
+ DexProgramClass mockClass, DexLibraryClass libraryClass, DexProgramClass context) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return true;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
index d9cabaa..a1a7eb2 100644
--- a/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
+++ b/src/main/java/com/android/tools/r8/dex/DexOutputBuffer.java
@@ -5,6 +5,7 @@
import com.android.tools.r8.ByteBufferProvider;
import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexWritableCode;
import com.android.tools.r8.graph.ObjectToOffsetMapping;
import com.android.tools.r8.graph.ProgramMethod;
@@ -92,6 +93,7 @@
}
public void putInstructions(
+ AppView<?> appView,
DexWritableCode code,
ProgramMethod context,
ObjectToOffsetMapping mapping,
@@ -101,7 +103,12 @@
assert byteBuffer.position() % 2 == 0;
ShortBuffer shortBuffer = byteBuffer.asShortBuffer();
code.writeDex(
- shortBuffer, context, mapping.getGraphLens(), mapping.getLensCodeRewriter(), mapping);
+ shortBuffer,
+ context,
+ mapping.getGraphLens(),
+ code.getCodeLens(appView),
+ mapping.getLensCodeRewriter(),
+ mapping);
code.writeKeepRulesForDesugaredLibrary(desugaredLibraryCodeToKeep);
byteBuffer.position(byteBuffer.position() + shortBuffer.position() * Short.BYTES);
}
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index fc9c037..6d43e68 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -547,7 +547,7 @@
int insnSizeOffset = dest.position();
dest.forward(4);
// Write instruction stream.
- dest.putInstructions(code, method, mapping, desugaredLibraryCodeToKeep);
+ dest.putInstructions(appView, code, method, mapping, desugaredLibraryCodeToKeep);
// Compute size and do the backward/forward dance to write the size at the beginning.
int insnSize = dest.position() - insnSizeOffset - 4;
dest.rewind(insnSize + 4);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexCheckCast.java b/src/main/java/com/android/tools/r8/dex/code/DexCheckCast.java
index 650655a..6d95477 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexCheckCast.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexCheckCast.java
@@ -74,6 +74,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
DexType rewritten = graphLens.lookupType(getType());
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexConstClass.java b/src/main/java/com/android/tools/r8/dex/code/DexConstClass.java
index bc476c1..791eef5 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexConstClass.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexConstClass.java
@@ -74,6 +74,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
DexType rewritten = graphLens.lookupType(getType());
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexConstMethodHandle.java b/src/main/java/com/android/tools/r8/dex/code/DexConstMethodHandle.java
index b909f64..7cf55ad 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexConstMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexConstMethodHandle.java
@@ -78,6 +78,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
DexMethodHandle rewritten =
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexConstMethodType.java b/src/main/java/com/android/tools/r8/dex/code/DexConstMethodType.java
index 15bee8c..a4e1941 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexConstMethodType.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexConstMethodType.java
@@ -76,6 +76,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
DexProto rewritten = rewriter.rewriteProto(getMethodType());
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexConstString.java b/src/main/java/com/android/tools/r8/dex/code/DexConstString.java
index e143d89..da79e38 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexConstString.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexConstString.java
@@ -89,6 +89,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
int index = BBBB.getOffset(mapping);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFillArrayDataPayload.java b/src/main/java/com/android/tools/r8/dex/code/DexFillArrayDataPayload.java
index 228258d..62b99a3 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFillArrayDataPayload.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFillArrayDataPayload.java
@@ -56,6 +56,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(3, dest); // Pseudo-opcode = 0x0300
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFilledNewArray.java b/src/main/java/com/android/tools/r8/dex/code/DexFilledNewArray.java
index ab52b2b..08d174c 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFilledNewArray.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFilledNewArray.java
@@ -72,6 +72,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
DexType rewritten = graphLens.lookupType(getType());
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFilledNewArrayRange.java b/src/main/java/com/android/tools/r8/dex/code/DexFilledNewArrayRange.java
index 5e473dc..5a7ab7d 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFilledNewArrayRange.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFilledNewArrayRange.java
@@ -72,6 +72,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
DexType rewritten = graphLens.lookupType(getType());
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat10t.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat10t.java
index ba29cbe..1b1ad0c 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat10t.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat10t.java
@@ -35,6 +35,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(AA, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat10x.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat10x.java
index 174c89b..ce80312 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat10x.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat10x.java
@@ -27,6 +27,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(0, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat11n.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat11n.java
index 09e3fb6..805512d 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat11n.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat11n.java
@@ -49,6 +49,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(B, A, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat11x.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat11x.java
index 393ab88..7a5ada4 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat11x.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat11x.java
@@ -35,6 +35,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(AA, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat12x.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat12x.java
index 979bcc7..711072d 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat12x.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat12x.java
@@ -43,6 +43,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(B, A, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat20t.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat20t.java
index a03b9c5..e226b0b 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat20t.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat20t.java
@@ -34,6 +34,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(0, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat21h.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat21h.java
index 246c6cc..40d5684 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat21h.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat21h.java
@@ -43,6 +43,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(AA, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat21s.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat21s.java
index 3d66ad2..2351a2c 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat21s.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat21s.java
@@ -45,6 +45,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(AA, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat21t.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat21t.java
index 93cd025..c2fad2b 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat21t.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat21t.java
@@ -47,6 +47,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(AA, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat22b.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat22b.java
index 222fc09..f9c8419 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat22b.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat22b.java
@@ -49,6 +49,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(AA, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat22s.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat22s.java
index 53d247c..715f719 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat22s.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat22s.java
@@ -49,6 +49,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(B, A, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat22t.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat22t.java
index c276ca1..4ca9fcf 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat22t.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat22t.java
@@ -51,6 +51,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(B, A, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat22x.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat22x.java
index 3cd881d..9d8f38c 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat22x.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat22x.java
@@ -44,6 +44,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(AA, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat23x.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat23x.java
index 1e1b130..c6ee23e 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat23x.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat23x.java
@@ -48,6 +48,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(AA, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat30t.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat30t.java
index cfd2bf7..df14fb9 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat30t.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat30t.java
@@ -33,6 +33,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(0, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat31c.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat31c.java
index ae0ca6c..d5b1037 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat31c.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat31c.java
@@ -47,6 +47,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(AA, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat31i.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat31i.java
index 942c5e2..67ae1b5 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat31i.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat31i.java
@@ -43,6 +43,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(AA, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat31t.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat31t.java
index 7f26c21..07cea77 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat31t.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat31t.java
@@ -43,6 +43,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(AA, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat32x.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat32x.java
index 5389c2f..7cd9e2c 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat32x.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat32x.java
@@ -45,6 +45,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(0, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat45cc.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat45cc.java
index 2f4adc5..91657d8 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat45cc.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat45cc.java
@@ -119,6 +119,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
// The method is one of java.lang.MethodHandle.invoke/invokeExact.
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat4rcc.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat4rcc.java
index 8aa065a..0bf1e92 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat4rcc.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat4rcc.java
@@ -57,6 +57,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
// The method is one of java.lang.MethodHandle.invoke/invokeExact.
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexFormat51l.java b/src/main/java/com/android/tools/r8/dex/code/DexFormat51l.java
index ddce5ad..15cb63a 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexFormat51l.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexFormat51l.java
@@ -43,6 +43,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(AA, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexIgetOrIput.java b/src/main/java/com/android/tools/r8/dex/code/DexIgetOrIput.java
index d1c298a..1f77b9f 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexIgetOrIput.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexIgetOrIput.java
@@ -42,9 +42,10 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
- DexField lookup = graphLens.lookupField(getField());
+ DexField lookup = graphLens.lookupField(getField(), codeLens);
writeFirst(B, A, dest);
write16BitReference(lookup, dest, mapping);
}
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexInitClass.java b/src/main/java/com/android/tools/r8/dex/code/DexInitClass.java
index 8591fd9..77a2f9f 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexInitClass.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexInitClass.java
@@ -118,6 +118,7 @@
ShortBuffer buffer,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
// We intentionally apply the graph lens first, and then the init class lens, using the fact
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexInstanceOf.java b/src/main/java/com/android/tools/r8/dex/code/DexInstanceOf.java
index 601dffb..ba204cd 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexInstanceOf.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexInstanceOf.java
@@ -88,6 +88,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
DexType lookup = graphLens.lookupType(getType());
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexInstruction.java b/src/main/java/com/android/tools/r8/dex/code/DexInstruction.java
index e6362d0..118556a 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexInstruction.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexInstruction.java
@@ -393,6 +393,7 @@
ShortBuffer buffer,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexInvokeCustom.java b/src/main/java/com/android/tools/r8/dex/code/DexInvokeCustom.java
index 605b420..28464af 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexInvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexInvokeCustom.java
@@ -79,6 +79,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(A, G, dest);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexInvokeCustomRange.java b/src/main/java/com/android/tools/r8/dex/code/DexInvokeCustomRange.java
index a88f338..51f0908 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexInvokeCustomRange.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexInvokeCustomRange.java
@@ -79,6 +79,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
DexCallSite rewritten = rewriter.rewriteCallSite(getCallSite(), context);
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexInvokeMethod.java b/src/main/java/com/android/tools/r8/dex/code/DexInvokeMethod.java
index 9340e4a..a390eff 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexInvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexInvokeMethod.java
@@ -50,6 +50,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
MethodLookupResult lookup =
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexInvokeMethodRange.java b/src/main/java/com/android/tools/r8/dex/code/DexInvokeMethodRange.java
index 34243ac..3c1ffc7 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexInvokeMethodRange.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexInvokeMethodRange.java
@@ -50,6 +50,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
MethodLookupResult lookup =
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexItemBasedConstString.java b/src/main/java/com/android/tools/r8/dex/code/DexItemBasedConstString.java
index ddaf074..451eef1 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexItemBasedConstString.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexItemBasedConstString.java
@@ -101,6 +101,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
throw new Unreachable(
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexNewArray.java b/src/main/java/com/android/tools/r8/dex/code/DexNewArray.java
index 8d35555..285b82e 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexNewArray.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexNewArray.java
@@ -78,6 +78,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
DexType lookup = graphLens.lookupType(getType());
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexNewInstance.java b/src/main/java/com/android/tools/r8/dex/code/DexNewInstance.java
index 4382e71..e3bc04d 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexNewInstance.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexNewInstance.java
@@ -65,6 +65,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
DexType rewritten = graphLens.lookupType(getType());
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexNewUnboxedEnumInstance.java b/src/main/java/com/android/tools/r8/dex/code/DexNewUnboxedEnumInstance.java
index 387952c..3992859 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexNewUnboxedEnumInstance.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexNewUnboxedEnumInstance.java
@@ -64,6 +64,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
throw new Unreachable();
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexPackedSwitchPayload.java b/src/main/java/com/android/tools/r8/dex/code/DexPackedSwitchPayload.java
index 95f6eed..77b35f6 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexPackedSwitchPayload.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexPackedSwitchPayload.java
@@ -52,6 +52,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(1, dest); // Pseudo-opcode = 0x0100
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexRecordFieldValues.java b/src/main/java/com/android/tools/r8/dex/code/DexRecordFieldValues.java
index 3101663..8f0b9e8 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexRecordFieldValues.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexRecordFieldValues.java
@@ -117,6 +117,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
throw new Unreachable(
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexSgetOrSput.java b/src/main/java/com/android/tools/r8/dex/code/DexSgetOrSput.java
index 85c8ea3..0e9b3b8 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexSgetOrSput.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexSgetOrSput.java
@@ -38,9 +38,10 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
- DexField rewritten = graphLens.lookupField(getField());
+ DexField rewritten = graphLens.lookupField(getField(), codeLens);
writeFirst(AA, dest);
write16BitReference(rewritten, dest, mapping);
}
diff --git a/src/main/java/com/android/tools/r8/dex/code/DexSparseSwitchPayload.java b/src/main/java/com/android/tools/r8/dex/code/DexSparseSwitchPayload.java
index 838e4ea..f0059d9 100644
--- a/src/main/java/com/android/tools/r8/dex/code/DexSparseSwitchPayload.java
+++ b/src/main/java/com/android/tools/r8/dex/code/DexSparseSwitchPayload.java
@@ -56,6 +56,7 @@
ShortBuffer dest,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
ObjectToOffsetMapping mapping,
LensCodeRewriterUtils rewriter) {
writeFirst(2, dest); // Pseudo-opcode = 0x0200
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
index c3a72b9..72908ca 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/instrumentation/StartupInstrumentation.java
@@ -34,6 +34,7 @@
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.IRToDexFinalizer;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
import com.android.tools.r8.startup.generated.InstrumentationServerFactory;
import com.android.tools.r8.startup.generated.InstrumentationServerImplFactory;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -97,7 +98,8 @@
}
List<DexProgramClass> extraProgramClasses = createStartupRuntimeLibraryClasses();
- converter.processClassesConcurrently(extraProgramClasses, executorService);
+ MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty();
+ converter.processClassesConcurrently(extraProgramClasses, eventConsumer, executorService);
DexApplication newApplication =
appView.app().builder().addProgramClasses(extraProgramClasses).build();
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 6a0652a..b5fe77a 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -199,7 +199,7 @@
public static <T extends AppInfo> AppView<T> createForD8(T appInfo) {
return new AppView<>(
appInfo,
- ArtProfileCollection.createInitialArtProfileCollection(appInfo.options()),
+ ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
WholeProgramOptimizations.OFF,
defaultTypeRewriter(appInfo));
}
@@ -216,7 +216,7 @@
T appInfo, TypeRewriter mapper, Timing timing) {
return new AppView<>(
appInfo,
- ArtProfileCollection.createInitialArtProfileCollection(appInfo.options()),
+ ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
WholeProgramOptimizations.OFF,
mapper,
timing);
@@ -240,7 +240,7 @@
startupOrder);
return new AppView<>(
appInfo,
- ArtProfileCollection.createInitialArtProfileCollection(application.options),
+ ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
WholeProgramOptimizations.ON,
defaultTypeRewriter(appInfo));
}
@@ -248,7 +248,7 @@
public static <T extends AppInfo> AppView<T> createForL8(T appInfo, TypeRewriter mapper) {
return new AppView<>(
appInfo,
- ArtProfileCollection.createInitialArtProfileCollection(appInfo.options()),
+ ArtProfileCollection.createInitialArtProfileCollection(appInfo, appInfo.options()),
WholeProgramOptimizations.OFF,
mapper);
}
diff --git a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
index c239b75..a3c20e3 100644
--- a/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
+++ b/src/main/java/com/android/tools/r8/graph/AssemblyWriter.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.ClassFileConsumer;
import com.android.tools.r8.contexts.CompilationContext;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.kotlin.Kotlin;
@@ -176,8 +177,10 @@
private void writeIR(ProgramMethod method, PrintStream ps) {
CfgPrinter printer = new CfgPrinter();
IRConverter converter = new IRConverter(appInfo, timing, printer);
+ MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty();
OneTimeMethodProcessor methodProcessor =
- OneTimeMethodProcessor.create(method, compilationContext.createProcessorContext());
+ OneTimeMethodProcessor.create(
+ method, eventConsumer, compilationContext.createProcessorContext());
methodProcessor.forEachWaveWithExtension(
(ignore, methodProcessingContext) ->
converter.processDesugaredMethod(
diff --git a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
index 165ff81..601ce08 100644
--- a/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DefaultInstanceInitializerCode.java
@@ -371,13 +371,14 @@
ShortBuffer shortBuffer,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
LensCodeRewriterUtils lensCodeRewriter,
ObjectToOffsetMapping mapping) {
DexMethod parentConstructor = getParentConstructor(context, mapping.dexItemFactory());
MethodLookupResult lookupResult = graphLens.lookupInvokeDirect(parentConstructor, context);
new DexInvokeDirect(1, lookupResult.getReference(), 0, 0, 0, 0, 0)
- .write(shortBuffer, context, graphLens, mapping, lensCodeRewriter);
- new DexReturnVoid().write(shortBuffer, context, graphLens, mapping, lensCodeRewriter);
+ .write(shortBuffer, context, graphLens, codeLens, mapping, lensCodeRewriter);
+ new DexReturnVoid().write(shortBuffer, context, graphLens, codeLens, mapping, lensCodeRewriter);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/DexCode.java b/src/main/java/com/android/tools/r8/graph/DexCode.java
index c7e251e..8c7c2ff 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCode.java
@@ -90,6 +90,20 @@
.withItemArray(c -> c.instructions);
}
+ private DexCode(DexCode code) {
+ this(
+ code.registerSize,
+ code.incomingRegisterSize,
+ code.outgoingRegisterSize,
+ code.instructions,
+ code.tries,
+ code.handlers,
+ code.debugInfo,
+ code.metadata);
+ this.debugInfoForWriting = code.debugInfoForWriting;
+ this.highestSortingString = code.highestSortingString;
+ }
+
public DexCode(int registerSize, int insSize, int outsSize, DexInstruction[] instructions) {
this(
registerSize,
@@ -144,6 +158,16 @@
hashCode(); // Cache the hash code eagerly.
}
+ public DexCode withCodeLens(GraphLens codeLens) {
+ return new DexCode(this) {
+
+ @Override
+ public GraphLens getCodeLens(AppView<?> appView) {
+ return codeLens;
+ }
+ };
+ }
+
@Override
public DexCode self() {
return this;
@@ -803,10 +827,11 @@
ShortBuffer shortBuffer,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
LensCodeRewriterUtils lensCodeRewriter,
ObjectToOffsetMapping mapping) {
for (DexInstruction instruction : instructions) {
- instruction.write(shortBuffer, context, graphLens, mapping, lensCodeRewriter);
+ instruction.write(shortBuffer, context, graphLens, codeLens, mapping, lensCodeRewriter);
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
index 1ba1122..4ffba91 100644
--- a/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
+++ b/src/main/java/com/android/tools/r8/graph/DexItemFactory.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.horizontalclassmerging.ClassMerger.CLASS_ID_FIELD_NAME;
import static com.android.tools.r8.ir.analysis.type.ClassTypeElement.computeLeastUpperBoundOfInterfaces;
import static com.android.tools.r8.ir.desugar.LambdaClass.LAMBDA_INSTANCE_FIELD_NAME;
+import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.dex.Marker;
@@ -35,17 +36,17 @@
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.utils.ArrayUtils;
+import com.android.tools.r8.utils.DequeUtils;
import com.android.tools.r8.utils.DescriptorUtils;
-import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.LRUCacheTable;
import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.SetUtils;
import com.google.common.base.Strings;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2ReferenceArrayMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -53,20 +54,20 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
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 DexItemFactory {
@@ -2640,12 +2641,21 @@
}
public DexMethod createInstanceInitializerWithFreshProto(
- DexMethod method, List<DexType> extraTypes, Predicate<DexMethod> isFresh) {
+ DexMethod method, List<Supplier<DexType>> extraTypes, Predicate<DexMethod> isFresh) {
+ return createInstanceInitializerWithFreshProto(method, extraTypes, isFresh, emptyConsumer());
+ }
+
+ public DexMethod createInstanceInitializerWithFreshProto(
+ DexMethod method,
+ List<Supplier<DexType>> extraTypes,
+ Predicate<DexMethod> isFresh,
+ Consumer<Set<DexType>> usedExtraTypesConsumer) {
assert method.isInstanceInitializer(this);
return createInstanceInitializerWithFreshProto(
method.proto,
extraTypes,
- proto -> Optional.of(method.withProto(proto, this)).filter(isFresh));
+ proto -> Optional.of(method.withProto(proto, this)).filter(isFresh),
+ usedExtraTypesConsumer);
}
public DexMethod createInstanceInitializerWithFreshProto(
@@ -2653,31 +2663,64 @@
assert method.isInstanceInitializer(this);
return createInstanceInitializerWithFreshProto(
method.proto,
- ImmutableList.of(extraType),
- proto -> Optional.of(method.withProto(proto, this)).filter(isFresh));
+ ImmutableList.of(() -> extraType),
+ proto -> Optional.of(method.withProto(proto, this)).filter(isFresh),
+ emptyConsumer());
+ }
+
+ private class FreshInstanceInitializerCandidate {
+
+ DexProto protoWithoutExtraType;
+ Supplier<DexType> extraTypeSupplier;
+ Set<DexType> usedExtraTypes;
+
+ FreshInstanceInitializerCandidate(
+ DexProto protoWithoutExtraType,
+ Supplier<DexType> extraTypeSupplier,
+ Set<DexType> usedExtraTypes) {
+ this.protoWithoutExtraType = protoWithoutExtraType;
+ this.extraTypeSupplier = extraTypeSupplier;
+ this.usedExtraTypes = SetUtils.newIdentityHashSet(usedExtraTypes);
+ }
+
+ DexProto createProto() {
+ DexType extraType = extraTypeSupplier.get();
+ usedExtraTypes.add(extraType);
+ return appendTypeToProto(protoWithoutExtraType, extraType);
+ }
}
private DexMethod createInstanceInitializerWithFreshProto(
- DexProto proto, List<DexType> extraTypes, Function<DexProto, Optional<DexMethod>> isFresh) {
- Queue<Iterable<DexProto>> tryProtos = new LinkedList<>();
- Iterator<DexProto> current = IterableUtils.singleton(proto).iterator();
-
+ DexProto proto,
+ List<Supplier<DexType>> extraTypes,
+ Function<DexProto, Optional<DexMethod>> isFresh,
+ Consumer<Set<DexType>> usedExtraTypesConsumer) {
+ Optional<DexMethod> resultWithNoExtraTypes = isFresh.apply(proto);
+ if (resultWithNoExtraTypes.isPresent()) {
+ return resultWithNoExtraTypes.get();
+ }
+ assert !extraTypes.isEmpty();
+ Deque<FreshInstanceInitializerCandidate> worklist =
+ DequeUtils.newArrayDeque(
+ new FreshInstanceInitializerCandidate(
+ proto, extraTypes.iterator().next(), Collections.emptySet()));
int count = 0;
while (true) {
assert count++ < 100;
- if (!current.hasNext()) {
- assert !tryProtos.isEmpty();
- current = tryProtos.remove().iterator();
- assert current.hasNext();
- }
- DexProto tryProto = current.next();
+ assert !worklist.isEmpty();
+ FreshInstanceInitializerCandidate candidate = worklist.removeFirst();
+ DexProto tryProto = candidate.createProto();
Optional<DexMethod> object = isFresh.apply(tryProto);
if (object.isPresent()) {
+ assert !candidate.usedExtraTypes.isEmpty();
+ usedExtraTypesConsumer.accept(candidate.usedExtraTypes);
return object.get();
}
- assert !extraTypes.isEmpty();
- tryProtos.add(
- Iterables.transform(extraTypes, extraType -> appendTypeToProto(tryProto, extraType)));
+ for (Supplier<DexType> extraTypeSupplier : extraTypes) {
+ worklist.addLast(
+ new FreshInstanceInitializerCandidate(
+ tryProto, extraTypeSupplier, candidate.usedExtraTypes));
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 0cb6881..22d3978 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -339,6 +339,12 @@
return toProgramMethodOrNull(getClassInitializer());
}
+ public void acceptProgramClassInitializer(Consumer<ProgramMethod> consumer) {
+ if (hasClassInitializer()) {
+ consumer.accept(getProgramClassInitializer());
+ }
+ }
+
public ProgramMethod getProgramDefaultInitializer() {
return getProgramInitializer(DexType.EMPTY_ARRAY);
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexWritableCode.java b/src/main/java/com/android/tools/r8/graph/DexWritableCode.java
index 1458f08..af4b8a0 100644
--- a/src/main/java/com/android/tools/r8/graph/DexWritableCode.java
+++ b/src/main/java/com/android/tools/r8/graph/DexWritableCode.java
@@ -63,6 +63,8 @@
void writeKeepRulesForDesugaredLibrary(CodeToKeep codeToKeep);
+ GraphLens getCodeLens(AppView<?> appView);
+
DexDebugInfoForWriting getDebugInfoForWriting();
DexWritableCodeKind getDexWritableCodeKind();
@@ -99,6 +101,7 @@
ShortBuffer shortBuffer,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
LensCodeRewriterUtils lensCodeRewriter,
ObjectToOffsetMapping mapping);
}
diff --git a/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java b/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
index fe15a02..4c61282 100644
--- a/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
+++ b/src/main/java/com/android/tools/r8/graph/NestedGraphLens.java
@@ -135,7 +135,7 @@
if (this == codeLens) {
return originalField;
}
- DexField renamedField = getPrevious().getRenamedFieldSignature(originalField);
+ DexField renamedField = getPrevious().getRenamedFieldSignature(originalField, codeLens);
return internalGetNextFieldSignature(renamedField);
}
diff --git a/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java b/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java
index dc18cfe..57baddf 100644
--- a/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java
+++ b/src/main/java/com/android/tools/r8/graph/ThrowExceptionCode.java
@@ -211,19 +211,21 @@
ShortBuffer shortBuffer,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
LensCodeRewriterUtils lensCodeRewriter,
ObjectToOffsetMapping mapping) {
int register = 0;
int notUsed = 0;
int argumentCount = 1;
new DexNewInstance(register, exceptionType)
- .write(shortBuffer, context, graphLens, mapping, lensCodeRewriter);
+ .write(shortBuffer, context, graphLens, codeLens, mapping, lensCodeRewriter);
DexMethod instanceInitializer =
lensCodeRewriter.dexItemFactory().createInstanceInitializer(exceptionType);
new DexInvokeDirect(
argumentCount, instanceInitializer, register, notUsed, notUsed, notUsed, notUsed)
- .write(shortBuffer, context, graphLens, mapping, lensCodeRewriter);
- new DexThrow(register).write(shortBuffer, context, graphLens, mapping, lensCodeRewriter);
+ .write(shortBuffer, context, graphLens, codeLens, mapping, lensCodeRewriter);
+ new DexThrow(register)
+ .write(shortBuffer, context, graphLens, codeLens, mapping, lensCodeRewriter);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
index 661cefb..a15e551 100644
--- a/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
+++ b/src/main/java/com/android/tools/r8/graph/ThrowNullCode.java
@@ -248,11 +248,14 @@
ShortBuffer shortBuffer,
ProgramMethod context,
GraphLens graphLens,
+ GraphLens codeLens,
LensCodeRewriterUtils lensCodeRewriter,
ObjectToOffsetMapping mapping) {
int register = 0;
- new DexConst4(register, 0).write(shortBuffer, context, graphLens, mapping, lensCodeRewriter);
- new DexThrow(register).write(shortBuffer, context, graphLens, mapping, lensCodeRewriter);
+ new DexConst4(register, 0)
+ .write(shortBuffer, context, graphLens, codeLens, mapping, lensCodeRewriter);
+ new DexThrow(register)
+ .write(shortBuffer, context, graphLens, codeLens, mapping, lensCodeRewriter);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 8050e50..b84809e 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.horizontalclassmerging.code.SyntheticInitializerConverter;
import com.android.tools.r8.ir.code.Invoke.Type;
+import com.android.tools.r8.profile.art.ArtProfileCompletenessChecker;
import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.FieldAccessInfoCollectionModifier;
@@ -91,6 +92,8 @@
: IRCodeProvider.createThrowing();
run(runtimeTypeCheckInfo, codeProvider, executorService, timing);
+ assert ArtProfileCompletenessChecker.verify(appView);
+
// Clear type elements cache after IR building.
appView.dexItemFactory().clearTypeElementsCache();
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
index 716a9f3..d827984 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMerger.java
@@ -27,6 +27,7 @@
import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.Box;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.structural.Ordered;
@@ -312,11 +313,23 @@
DexMethod newMethodReferenceTemplate = getNewMethodReference(representative, needsClassId);
assert mode.isInitial() || classMethodsBuilder.isFresh(newMethodReferenceTemplate);
+ Box<Set<DexType>> usedSyntheticArgumentClasses = new Box<>();
DexMethod newMethodReference =
dexItemFactory.createInstanceInitializerWithFreshProto(
newMethodReferenceTemplate,
mode.isInitial() ? syntheticArgumentClass.getArgumentClasses() : ImmutableList.of(),
- classMethodsBuilder::isFresh);
+ classMethodsBuilder::isFresh,
+ usedSyntheticArgumentClasses::set);
+
+ // Amend the art profile collection.
+ if (usedSyntheticArgumentClasses.isSet()) {
+ for (ProgramMethod instanceInitializer : instanceInitializers) {
+ artProfileCollectionAdditions.applyIfContextIsInProfile(
+ instanceInitializer.getReference(),
+ additionsBuilder ->
+ usedSyntheticArgumentClasses.get().forEach(additionsBuilder::addRule));
+ }
+ }
// Compute the extra unused null parameters.
List<ExtraUnusedNullParameter> extraUnusedNullParameters =
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
index ec19e83..1074a5c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/SyntheticArgumentClass.java
@@ -9,9 +9,11 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.synthesis.SyntheticItems.SyntheticKindSelector;
+import com.google.common.base.Suppliers;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.function.Supplier;
/**
* Lets assume we are merging a class A that looks like:
@@ -32,13 +34,13 @@
*/
public class SyntheticArgumentClass {
- private final List<DexType> syntheticClassTypes;
+ private final List<Supplier<DexType>> syntheticClassTypes;
- private SyntheticArgumentClass(List<DexType> syntheticClassTypes) {
+ private SyntheticArgumentClass(List<Supplier<DexType>> syntheticClassTypes) {
this.syntheticClassTypes = syntheticClassTypes;
}
- public List<DexType> getArgumentClasses() {
+ public List<Supplier<DexType>> getArgumentClasses() {
return syntheticClassTypes;
}
@@ -59,13 +61,22 @@
public SyntheticArgumentClass build(Collection<MergeGroup> mergeGroups) {
DexProgramClass context = getDeterministicContext(mergeGroups);
- List<DexType> syntheticArgumentTypes = new ArrayList<>();
+ List<Supplier<DexType>> syntheticArgumentTypes = new ArrayList<>();
syntheticArgumentTypes.add(
- synthesizeClass(context, kinds -> kinds.HORIZONTAL_INIT_TYPE_ARGUMENT_1).getType());
+ Suppliers.memoize(
+ () ->
+ synthesizeClass(context, kinds -> kinds.HORIZONTAL_INIT_TYPE_ARGUMENT_1)
+ .getType()));
syntheticArgumentTypes.add(
- synthesizeClass(context, kinds -> kinds.HORIZONTAL_INIT_TYPE_ARGUMENT_2).getType());
+ Suppliers.memoize(
+ () ->
+ synthesizeClass(context, kinds -> kinds.HORIZONTAL_INIT_TYPE_ARGUMENT_2)
+ .getType()));
syntheticArgumentTypes.add(
- synthesizeClass(context, kinds -> kinds.HORIZONTAL_INIT_TYPE_ARGUMENT_3).getType());
+ Suppliers.memoize(
+ () ->
+ synthesizeClass(context, kinds -> kinds.HORIZONTAL_INIT_TYPE_ARGUMENT_3)
+ .getType()));
return new SyntheticArgumentClass(syntheticArgumentTypes);
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
index 7d26bd1..b47b947 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/VirtualMethodMerger.java
@@ -331,7 +331,7 @@
for (ProgramMethod oldMethod : methods) {
artProfileCollectionAdditions.applyIfContextIsInProfile(
oldMethod.getReference(),
- additionsBuilder -> additionsBuilder.addRule(newMethodReference));
+ additionsBuilder -> additionsBuilder.addRule(representativeMethod.getReference()));
}
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
index a3e5972..1872754 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/code/SyntheticInitializerConverter.java
@@ -6,7 +6,9 @@
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.Code;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMerger.Mode;
import com.android.tools.r8.horizontalclassmerging.IRCodeProvider;
@@ -78,10 +80,18 @@
assert appView.options().isGeneratingDex();
assert mode.isFinal();
clazz.forEachProgramInstanceInitializerMatching(
- method -> !method.getCode().isDexCode(),
+ method -> method.getCode().isCfCode(),
method -> {
+ GraphLens codeLens = method.getDefinition().getCode().getCodeLens(appView);
+ assert codeLens != appView.codeLens();
+
+ // Convert to dex.
processMethod(method, converter);
- assert method.getDefinition().getCode().isDexCode();
+
+ // Recover code lens.
+ Code code = method.getDefinition().getCode();
+ assert code.isDexCode();
+ method.setCode(code.asDexCode().withCodeLens(codeLens), appView);
});
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
index 66d04da..a78dc55 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedExtensionRegistryShrinker.java
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.StaticPut;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -168,7 +169,9 @@
timing.begin("[Proto] Post optimize generated extension registry");
ProgramMethodSet wave =
ProgramMethodSet.create(this::forEachMethodThatRequiresPostOptimization);
- OneTimeMethodProcessor methodProcessor = OneTimeMethodProcessor.create(wave, appView);
+ MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty();
+ OneTimeMethodProcessor methodProcessor =
+ OneTimeMethodProcessor.create(wave, eventConsumer, appView);
methodProcessor.forEachWaveWithExtension(
(method, methodProcessingContext) ->
converter.processDesugaredMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
index 104b854..2efe2c3 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -39,6 +39,7 @@
import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -224,7 +225,9 @@
throws ExecutionException {
timing.begin("[Proto] Post optimize dynamic methods");
ProgramMethodSet wave = ProgramMethodSet.create(this::forEachDynamicMethod);
- OneTimeMethodProcessor methodProcessor = OneTimeMethodProcessor.create(wave, appView);
+ MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty();
+ OneTimeMethodProcessor methodProcessor =
+ OneTimeMethodProcessor.create(wave, eventConsumer, appView);
methodProcessor.forEachWaveWithExtension(
(method, methodProcessingContext) ->
converter.processDesugaredMethod(
diff --git a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
index 13f8213..c521bf5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/ArrayPut.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.ir.conversion.DexBuilder;
import com.android.tools.r8.ir.conversion.MethodConversionOptions;
import com.android.tools.r8.ir.conversion.TypeConstraintResolver;
+import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
@@ -117,8 +118,7 @@
}
@Override
- public boolean instructionMayHaveSideEffects(
- AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
+ public boolean instructionInstanceCanThrow(AppView<?> appView, ProgramMethod context) {
// In debug mode, ArrayPut has a side-effect on the locals.
if (appView.options().debug) {
return true;
@@ -158,29 +158,24 @@
if (!valueType.lessThanOrEqualUpToNullability(memberType, appView)) {
return true;
}
-
- if (array.hasPhiUsers()) {
- // The array could be used indirectly.
- return true;
- }
-
- // Check that all usages of the array are array stores.
- for (Instruction user : array.aliasedUsers()) {
- if (user.isAssume()) {
- if (user.outValue().hasPhiUsers()) {
- return true;
- }
- continue;
- }
- if (!user.isArrayPut() || user.asArrayPut().array().getAliasedValue() != array) {
- return true;
- }
- }
-
return false;
}
@Override
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, ProgramMethod context, SideEffectAssumption assumption) {
+ // This modifies the array (or throws).
+ return true;
+ }
+
+ @Override
+ public DeadInstructionResult canBeDeadCode(AppView<?> appView, IRCode code) {
+ return instructionInstanceCanThrow(appView, code.context())
+ ? DeadInstructionResult.notDead()
+ : DeadInstructionResult.deadIfInValueIsDead(array());
+ }
+
+ @Override
public boolean identicalAfterRegisterAllocation(
Instruction other, RegisterAllocator allocator, MethodConversionOptions conversionOptions) {
// We cannot share ArrayPut instructions without knowledge of the type of the array input.
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 0a79887..2daa545 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -662,15 +662,6 @@
return !phis.isEmpty();
}
- public boolean hasDeadPhi(AppView<?> appView, IRCode code) {
- for (Phi phi : phis) {
- if (phi.isDead(appView, code)) {
- return true;
- }
- }
- return false;
- }
-
public List<Phi> getPhis() {
return phis;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 54710df..b0eb2bd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -29,7 +29,6 @@
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.NumberFromIntervalValue;
import com.android.tools.r8.ir.analysis.value.UnknownValue;
-import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
import com.android.tools.r8.ir.regalloc.LiveIntervals;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.position.MethodPosition;
@@ -464,6 +463,10 @@
return !users.isEmpty() || !phiUsers.isEmpty() || numberOfDebugUsers() > 0;
}
+ public boolean isUnused() {
+ return !isUsed();
+ }
+
public boolean isAlwaysNull(AppView<?> appView) {
if (hasLocalInfo()) {
// Not always null as the value can be changed via the debugger.
@@ -972,73 +975,6 @@
}
}
- public boolean isDead(AppView<?> appView, IRCode code) {
- // Totally unused values are trivially dead.
- return !isUsed() || isDead(appView, code, Predicates.alwaysFalse());
- }
-
- public boolean isDead(AppView<?> appView, IRCode code, Predicate<Instruction> ignoreUser) {
- // Totally unused values are trivially dead.
- return !isUsed() || isDead(appView, code, ignoreUser, Sets.newIdentityHashSet());
- }
-
- /**
- * Used to determine if a given value is dead.
- *
- * <p>The predicate `ignoreUser` can be used to determine if a given value is dead under the
- * assumption that the instructions for which `ignoreUser` returns true are also dead.
- *
- * <p>One use case of this is when we attempt to determine if a call to {@code <init>()} can be
- * removed: calls to {@code <init>()} can only be removed if the receiver is dead except for the
- * constructor call.
- */
- protected boolean isDead(
- AppView<?> appView, IRCode code, Predicate<Instruction> ignoreUser, Set<Value> active) {
- // Give up when the dependent set of values reach a given threshold (otherwise this fails with
- // a StackOverflowError on Art003_omnibus_opcodesTest).
- if (active.size() > 100) {
- return false;
- }
-
- // If the value has debug users we cannot eliminate it since it represents a value in a local
- // variable that should be visible in the debugger.
- if (hasDebugUsers()) {
- return false;
- }
- // This is a candidate for a dead value. Guard against looping by adding it to the set of
- // currently active values.
- active.add(this);
- for (Instruction instruction : uniqueUsers()) {
- if (ignoreUser.test(instruction)) {
- continue;
- }
- DeadInstructionResult result = instruction.canBeDeadCode(appView, code);
- if (result.isNotDead()) {
- return false;
- }
- if (result.isMaybeDead()) {
- for (Value valueRequiredToBeDead : result.getValuesRequiredToBeDead()) {
- if (!active.contains(valueRequiredToBeDead)
- && !valueRequiredToBeDead.isDead(appView, code, ignoreUser, active)) {
- return false;
- }
- }
- }
- Value outValue = instruction.outValue();
- if (outValue != null
- && !active.contains(outValue)
- && !outValue.isDead(appView, code, ignoreUser, active)) {
- return false;
- }
- }
- for (Phi phi : uniquePhiUsers()) {
- if (!active.contains(phi) && !phi.isDead(appView, code, ignoreUser, active)) {
- return false;
- }
- }
- return true;
- }
-
public boolean isZero() {
return isConstant()
&& getConstInstruction().isConstNumber()
diff --git a/src/main/java/com/android/tools/r8/ir/code/ValueIsDeadAnalysis.java b/src/main/java/com/android/tools/r8/ir/code/ValueIsDeadAnalysis.java
new file mode 100644
index 0000000..73d0a99
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/ValueIsDeadAnalysis.java
@@ -0,0 +1,265 @@
+// Copyright (c) 2023, 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.code;
+
+import static com.android.tools.r8.utils.MapUtils.ignoreKey;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.ir.optimize.DeadCodeRemover.DeadInstructionResult;
+import com.android.tools.r8.utils.BooleanBox;
+import com.android.tools.r8.utils.MapUtils;
+import com.android.tools.r8.utils.WorkList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+public class ValueIsDeadAnalysis {
+
+ private enum ValueIsDeadResult {
+ DEAD,
+ NOT_DEAD;
+
+ boolean isDead() {
+ return this == DEAD;
+ }
+
+ boolean isNotDead() {
+ return this == NOT_DEAD;
+ }
+ }
+
+ private final AppView<?> appView;
+ private final IRCode code;
+
+ private final Map<Value, ValueIsDeadResult> analysisCache = new IdentityHashMap<>();
+
+ public ValueIsDeadAnalysis(AppView<?> appView, IRCode code) {
+ this.appView = appView;
+ this.code = code;
+ }
+
+ public boolean isDead(Value value) {
+ // Totally unused values are trivially dead.
+ if (value.isUnused()) {
+ return true;
+ }
+ // Create an is-dead dependence graph. If the deadness of value v depends on value u being dead,
+ // a directed edge [v -> u] is added to the graph.
+ //
+ // This graph serves two purposes:
+ // 1) If the analysis finds that `u` is *not* dead, then using the graph we can find all the
+ // values whose deadness depend (directly or indirectly) on `u` being dead, and mark them as
+ // being *not* dead in the analysis cache.
+ // 2) If the analysis finds that `u` *is* dead, we can remove the node from the dependence graph
+ // (as it is necessarily a leaf), and repeatedly mark direct and indirect predecessors of `u`
+ // that have now become leaves as being dead in the analysis cache.
+ WorkList<Value> worklist = WorkList.newIdentityWorkList(value);
+ BooleanBox foundCycle = new BooleanBox();
+ Value notDeadWitness = findNotDeadWitness(worklist, foundCycle);
+ boolean isDead = Objects.isNull(notDeadWitness);
+ if (isDead) {
+ if (foundCycle.isTrue()) {
+ for (Value deadValue : worklist.getSeenSet()) {
+ recordValueIsDead(deadValue);
+ }
+ } else {
+ assert worklist.getSeenSet().stream()
+ .allMatch(deadValue -> analysisCache.get(deadValue) == ValueIsDeadResult.DEAD);
+ }
+ }
+ return isDead;
+ }
+
+ public boolean hasDeadPhi(BasicBlock block) {
+ return Iterables.any(block.getPhis(), this::isDead);
+ }
+
+ private Value findNotDeadWitness(WorkList<Value> worklist, BooleanBox foundCycle) {
+ DependenceGraph dependenceGraph = new DependenceGraph();
+ while (worklist.hasNext()) {
+ Value value = worklist.next();
+
+ // The first time we visit a value we have not yet added any outgoing edges to the dependence
+ // graph.
+ assert dependenceGraph.isLeaf(value);
+
+ // Lookup if we have already analyzed the deadness of this value.
+ ValueIsDeadResult cacheResult = analysisCache.get(value);
+ if (cacheResult != null) {
+ // If it is dead, then continue the search for a non-dead dependent. Otherwise this value is
+ // a witness that the analysis failed.
+ if (cacheResult.isDead()) {
+ continue;
+ } else {
+ recordDependentsAreNotDead(value, dependenceGraph);
+ return value;
+ }
+ }
+
+ // If the value has debug users we cannot eliminate it since it represents a value in a local
+ // variable that should be visible in the debugger.
+ if (value.hasDebugUsers()) {
+ recordValueAndDependentsAreNotDead(value, dependenceGraph);
+ return value;
+ }
+
+ Set<Value> valuesRequiredToBeDead = new LinkedHashSet<>(value.uniquePhiUsers());
+ for (Instruction instruction : value.uniqueUsers()) {
+ DeadInstructionResult result = instruction.canBeDeadCode(appView, code);
+ if (result.isNotDead()) {
+ recordValueAndDependentsAreNotDead(value, dependenceGraph);
+ return value;
+ }
+ if (result.isMaybeDead()) {
+ result.getValuesRequiredToBeDead().forEach(valuesRequiredToBeDead::add);
+ }
+ if (instruction.hasOutValue()) {
+ valuesRequiredToBeDead.add(instruction.outValue());
+ }
+ }
+
+ Iterator<Value> valuesRequiredToBeDeadIterator = valuesRequiredToBeDead.iterator();
+ while (valuesRequiredToBeDeadIterator.hasNext()) {
+ Value valueRequiredToBeDead = valuesRequiredToBeDeadIterator.next();
+ if (hasProvenThatValueIsNotDead(valueRequiredToBeDead)) {
+ recordValueAndDependentsAreNotDead(value, dependenceGraph);
+ return value;
+ }
+ if (!needsToProveThatValueIsDead(value, valueRequiredToBeDead)) {
+ valuesRequiredToBeDeadIterator.remove();
+ }
+ }
+
+ if (valuesRequiredToBeDead.isEmpty()) {
+ // We have now proven that this value is dead.
+ recordValueIsDeadAndPropagateToDependents(value, dependenceGraph);
+ } else {
+ // Record the current value as a dependent of each value required to be dead.
+ for (Value valueRequiredToBeDead : valuesRequiredToBeDead) {
+ dependenceGraph.addDependenceEdge(value, valueRequiredToBeDead);
+ foundCycle.or(worklist.isSeen(valueRequiredToBeDead));
+ }
+
+ // Continue the analysis of the dependents.
+ worklist.addIfNotSeen(valuesRequiredToBeDead);
+ }
+ }
+ return null;
+ }
+
+ private boolean hasProvenThatValueIsNotDead(Value valueRequiredToBeDead) {
+ return analysisCache.get(valueRequiredToBeDead) == ValueIsDeadResult.NOT_DEAD;
+ }
+
+ private boolean needsToProveThatValueIsDead(Value value, Value valueRequiredToBeDead) {
+ // No need to record that the deadness of a values relies on its own removal.
+ assert !hasProvenThatValueIsNotDead(valueRequiredToBeDead);
+ return valueRequiredToBeDead != value && !analysisCache.containsKey(valueRequiredToBeDead);
+ }
+
+ private void recordValueIsDeadAndPropagateToDependents(
+ Value value, DependenceGraph dependenceGraph) {
+ WorkList<Value> worklist = WorkList.newIdentityWorkList(value);
+ while (worklist.hasNext()) {
+ Value current = worklist.next();
+ recordValueIsDead(current);
+
+ // This value is now proven to be dead, thus there is no need to keep track of its successors.
+ dependenceGraph.unlinkSuccessors(current);
+
+ // Continue processing of new leaves.
+ for (Value dependent : dependenceGraph.removeLeaf(current)) {
+ if (dependenceGraph.isLeaf(dependent)) {
+ worklist.addIfNotSeen(dependent);
+ }
+ }
+ }
+ }
+
+ private void recordValueIsDead(Value value) {
+ ValueIsDeadResult existingResult = analysisCache.put(value, ValueIsDeadResult.DEAD);
+ assert existingResult == null || existingResult.isDead();
+ }
+
+ private void recordValueAndDependentsAreNotDead(Value value, DependenceGraph dependenceGraph) {
+ recordValueIsNotDead(value, dependenceGraph);
+ recordDependentsAreNotDead(value, dependenceGraph);
+ }
+
+ private void recordValueIsNotDead(Value value, DependenceGraph dependenceGraph) {
+ // This value is now proven to be dead, thus there is no need to keep track of its successors.
+ dependenceGraph.unlinkSuccessors(value);
+ ValueIsDeadResult existingResult = analysisCache.put(value, ValueIsDeadResult.NOT_DEAD);
+ assert existingResult == null || existingResult.isNotDead();
+ }
+
+ private void recordDependentsAreNotDead(Value value, DependenceGraph dependenceGraph) {
+ WorkList<Value> worklist = WorkList.newIdentityWorkList(value);
+ while (worklist.hasNext()) {
+ Value current = worklist.next();
+ for (Value dependent : dependenceGraph.removeLeaf(current)) {
+ recordValueIsNotDead(dependent, dependenceGraph);
+ worklist.addIfNotSeen(dependent);
+ }
+ }
+ }
+
+ private static class DependenceGraph {
+
+ private final Map<Value, Set<Value>> successors = new IdentityHashMap<>();
+ private final Map<Value, Set<Value>> predecessors = new IdentityHashMap<>();
+
+ /**
+ * Records that the removal of {@param value} depends on the removal of {@param
+ * valueRequiredToBeDead} by adding an edge from {@param value} to {@param
+ * valueRequiredToBeDead} in this graph.
+ */
+ void addDependenceEdge(Value value, Value valueRequiredToBeDead) {
+ successors
+ .computeIfAbsent(value, ignoreKey(Sets::newIdentityHashSet))
+ .add(valueRequiredToBeDead);
+ predecessors
+ .computeIfAbsent(valueRequiredToBeDead, ignoreKey(Sets::newIdentityHashSet))
+ .add(value);
+ }
+
+ Set<Value> removeLeaf(Value value) {
+ assert isLeaf(value);
+ Set<Value> dependents = MapUtils.removeOrDefault(predecessors, value, Collections.emptySet());
+ for (Value dependent : dependents) {
+ Set<Value> dependentSuccessors = successors.get(dependent);
+ boolean removed = dependentSuccessors.remove(value);
+ assert removed;
+ if (dependentSuccessors.isEmpty()) {
+ successors.remove(dependent);
+ }
+ }
+ return dependents;
+ }
+
+ void unlinkSuccessors(Value value) {
+ Set<Value> valueSuccessors =
+ MapUtils.removeOrDefault(successors, value, Collections.emptySet());
+ for (Value successor : valueSuccessors) {
+ Set<Value> successorPredecessors = predecessors.get(successor);
+ boolean removed = successorPredecessors.remove(value);
+ assert removed;
+ if (successorPredecessors.isEmpty()) {
+ predecessors.remove(successor);
+ }
+ }
+ }
+
+ boolean isLeaf(Value value) {
+ return !successors.containsKey(value);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 23e1ab3..3dfe55f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -57,6 +57,7 @@
import com.android.tools.r8.ir.optimize.PeepholeOptimizer;
import com.android.tools.r8.ir.optimize.PhiOptimizations;
import com.android.tools.r8.ir.optimize.peepholes.BasicBlockMuncher;
+import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.WorkList;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
@@ -143,17 +144,22 @@
this.bytecodeMetadataBuilder = BytecodeMetadata.builder(bytecodeMetadataProvider);
}
- public CfCode build(DeadCodeRemover deadCodeRemover) {
- code.traceBlocks();
- computeInitializers();
+ public CfCode build(DeadCodeRemover deadCodeRemover, Timing timing) {
+ timing.time("Trace blocks", code::traceBlocks);
+ timing.time("Compute Initializers", () -> computeInitializers());
+ timing.begin("Compute verification types");
TypeVerificationHelper typeVerificationHelper = new TypeVerificationHelper(appView, code);
typeVerificationHelper.computeVerificationTypes();
+ timing.end();
assert deadCodeRemover.verifyNoDeadCode(code);
- rewriteNots();
+ timing.time("Rewrite nots", this::rewriteNots);
+ timing.begin("Insert loads and stores");
LoadStoreHelper loadStoreHelper = new LoadStoreHelper(appView, code, typeVerificationHelper);
loadStoreHelper.insertLoadsAndStores();
+ timing.end();
// Run optimizations on phis and basic blocks in a fixpoint.
if (appView.options().enableLoadStoreOptimization) {
+ timing.begin("Load store optimizations (BasicBlockMunching)");
PhiOptimizations phiOptimizations = new PhiOptimizations();
boolean reachedFixpoint = false;
phiOptimizations.optimize(code);
@@ -161,23 +167,33 @@
BasicBlockMuncher.optimize(code, appView.options());
reachedFixpoint = !phiOptimizations.optimize(code);
}
+ timing.end();
}
assert code.isConsistentSSA(appView);
// Insert reads for uninitialized read blocks to ensure correct stack maps.
+ timing.begin("Insert uninitialized local reads");
Set<UninitializedThisLocalRead> uninitializedThisLocalReads =
insertUninitializedThisLocalReads();
+ timing.end();
+ timing.begin("Register allocation");
registerAllocator = new CfRegisterAllocator(appView, code, typeVerificationHelper);
registerAllocator.allocateRegisters();
+ timing.end();
// Remove any uninitializedThisLocalRead now that the register allocation will preserve this
// for instance initializers.
+ timing.begin("Remove uninitialized local reads");
if (!uninitializedThisLocalReads.isEmpty()) {
for (UninitializedThisLocalRead uninitializedThisLocalRead : uninitializedThisLocalReads) {
uninitializedThisLocalRead.getBlock().removeInstruction(uninitializedThisLocalRead);
}
}
+ timing.end();
+ timing.begin("Insert phi moves");
loadStoreHelper.insertPhiMoves(registerAllocator);
+ timing.end();
+ timing.begin("BasicBlock peephole optimizations");
if (code.getConversionOptions().isPeepholeOptimizationsEnabled()) {
for (int i = 0; i < PEEPHOLE_OPTIMIZATION_PASSES; i++) {
CodeRewriter.collapseTrivialGotos(appView, code);
@@ -186,12 +202,17 @@
code, registerAllocator, SUFFIX_SHARING_OVERHEAD);
}
}
+ timing.end();
- rewriteIincPatterns();
+ timing.time("Rewrite Iinc patterns", this::rewriteIincPatterns);
CodeRewriter.collapseTrivialGotos(appView, code);
+ timing.begin("Remove redundant debug positions");
DexBuilder.removeRedundantDebugPositions(code);
+ timing.end();
+ timing.begin("Build CF Code");
CfCode code = buildCfCode();
+ timing.end();
assert verifyInvokeInterface(code, appView);
assert code.getOrComputeStackMapStatus(method, appView, appView.graphLens())
.isValidOrNotPresent();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
index 5dc2584..7b3641f 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/ClassConverter.java
@@ -34,18 +34,16 @@
public abstract class ClassConverter {
protected final AppView<?> appView;
- private final ArtProfileCollectionAdditions artProfileCollectionAdditions;
- private final IRConverter converter;
+ private final PrimaryD8L8IRConverter converter;
private final D8MethodProcessor methodProcessor;
private final InterfaceProcessor interfaceProcessor;
ClassConverter(
AppView<?> appView,
- IRConverter converter,
+ PrimaryD8L8IRConverter converter,
D8MethodProcessor methodProcessor,
InterfaceProcessor interfaceProcessor) {
this.appView = appView;
- this.artProfileCollectionAdditions = ArtProfileCollectionAdditions.create(appView);
this.converter = converter;
this.methodProcessor = methodProcessor;
this.interfaceProcessor = interfaceProcessor;
@@ -53,7 +51,7 @@
public static ClassConverter create(
AppView<?> appView,
- IRConverter converter,
+ PrimaryD8L8IRConverter converter,
D8MethodProcessor methodProcessor,
InterfaceProcessor interfaceProcessor) {
return appView.options().desugarSpecificOptions().allowAllDesugaredInput
@@ -66,7 +64,6 @@
throws ExecutionException {
ClassConverterResult.Builder resultBuilder = ClassConverterResult.builder();
internalConvertClasses(resultBuilder, executorService);
- artProfileCollectionAdditions.commit(appView);
notifyAllClassesConverted();
return resultBuilder.build();
}
@@ -117,9 +114,10 @@
ClassConverterResult.Builder resultBuilder, ExecutorService executorService)
throws ExecutionException {
Collection<DexProgramClass> classes = appView.appInfo().classes();
-
+ ArtProfileCollectionAdditions artProfileCollectionAdditions =
+ methodProcessor.getArtProfileCollectionAdditions();
CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer =
- new CfClassSynthesizerDesugaringEventConsumer();
+ CfClassSynthesizerDesugaringEventConsumer.create(artProfileCollectionAdditions);
converter.classSynthesisDesugaring(executorService, classSynthesizerEventConsumer);
if (!classSynthesizerEventConsumer.getSynthesizedClasses().isEmpty()) {
classes =
@@ -132,8 +130,7 @@
CfInstructionDesugaringEventConsumer instructionDesugaringEventConsumerForPrepareStep =
CfInstructionDesugaringEventConsumer.createForD8(
appView, artProfileCollectionAdditions, resultBuilder, methodProcessor);
- converter.prepareDesugaringForD8(
- instructionDesugaringEventConsumerForPrepareStep, executorService);
+ converter.prepareDesugaring(instructionDesugaringEventConsumerForPrepareStep, executorService);
assert instructionDesugaringEventConsumerForPrepareStep.verifyNothingToFinalize();
// When adding nest members to the wave we must do so deterministically.
@@ -236,7 +233,7 @@
DefaultClassConverter(
AppView<?> appView,
- IRConverter converter,
+ PrimaryD8L8IRConverter converter,
D8MethodProcessor methodProcessor,
InterfaceProcessor interfaceProcessor) {
super(appView, converter, methodProcessor, interfaceProcessor);
@@ -260,7 +257,7 @@
LibraryDesugaredClassConverter(
AppView<?> appView,
- IRConverter converter,
+ PrimaryD8L8IRConverter converter,
D8MethodProcessor methodProcessor,
InterfaceProcessor interfaceProcessor) {
super(appView, converter, methodProcessor, interfaceProcessor);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
index c6b278c..7a04167 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/D8MethodProcessor.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer.D8CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
import com.android.tools.r8.utils.ThreadUtils;
import com.google.common.collect.Sets;
import java.util.ArrayList;
@@ -25,7 +26,9 @@
public class D8MethodProcessor extends MethodProcessor {
- private final IRConverter converter;
+ private final ArtProfileCollectionAdditions artProfileCollectionAdditions;
+ private final PrimaryD8L8IRConverter converter;
+ private final MethodProcessorEventConsumer eventConsumer;
private final ExecutorService executorService;
private final Set<DexType> scheduled = Sets.newIdentityHashSet();
@@ -41,8 +44,13 @@
private ProcessorContext processorContext;
- public D8MethodProcessor(IRConverter converter, ExecutorService executorService) {
+ public D8MethodProcessor(
+ ArtProfileCollectionAdditions artProfileCollectionAdditions,
+ PrimaryD8L8IRConverter converter,
+ ExecutorService executorService) {
+ this.artProfileCollectionAdditions = artProfileCollectionAdditions;
this.converter = converter;
+ this.eventConsumer = MethodProcessorEventConsumer.create(artProfileCollectionAdditions);
this.executorService = executorService;
this.processorContext = converter.appView.createProcessorContext();
}
@@ -61,6 +69,15 @@
return processorContext.createMethodProcessingContext(method);
}
+ public ArtProfileCollectionAdditions getArtProfileCollectionAdditions() {
+ return artProfileCollectionAdditions;
+ }
+
+ @Override
+ public MethodProcessorEventConsumer getEventConsumer() {
+ return eventConsumer;
+ }
+
@Override
public boolean isProcessedConcurrently(ProgramMethod method) {
// In D8 all methods are considered independently compiled.
@@ -116,9 +133,8 @@
executorService));
}
- public D8MethodProcessor scheduleDesugaredMethodsForProcessing(Iterable<ProgramMethod> methods) {
+ public void scheduleDesugaredMethodsForProcessing(Iterable<ProgramMethod> methods) {
methods.forEach(this::scheduleDesugaredMethodForProcessing);
- return this;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index fbcfcba..6d6edbf 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -3,50 +3,28 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.conversion;
-import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor.ExcludeDexResources;
-import static com.android.tools.r8.ir.desugar.lambda.D8LambdaDesugaring.rewriteEnclosingLambdaMethodAttributes;
-
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
-import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.Code;
-import com.android.tools.r8.graph.DexApplication;
-import com.android.tools.r8.graph.DexApplication.Builder;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexString;
-import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
import com.android.tools.r8.ir.analysis.TypeChecker;
import com.android.tools.r8.ir.analysis.VerifyTypesHelper;
import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
import com.android.tools.r8.ir.analysis.fieldaccess.FieldAccessAnalysis;
-import com.android.tools.r8.ir.analysis.fieldaccess.TrivialFieldAccessReprocessor;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.InstanceFieldValueAnalysis;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValueAnalysis;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValues;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.IRCode;
-import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringCollection;
-import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
-import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
-import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringCollection;
-import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.CovariantReturnTypeAnnotationTransformer;
-import com.android.tools.r8.ir.desugar.ProgramAdditions;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter;
-import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceApplicationRewriter;
-import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
-import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
-import com.android.tools.r8.ir.desugar.itf.L8InnerOuterAttributeEraser;
-import com.android.tools.r8.ir.desugar.lambda.LambdaDeserializationMethodRemover;
-import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
import com.android.tools.r8.ir.optimize.AssertionErrorTwoArgsConstructorRewriter;
import com.android.tools.r8.ir.optimize.AssertionsRewriter;
import com.android.tools.r8.ir.optimize.AssumeInserter;
@@ -61,11 +39,11 @@
import com.android.tools.r8.ir.optimize.IdempotentFunctionCallCanonicalizer;
import com.android.tools.r8.ir.optimize.Inliner;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
-import com.android.tools.r8.ir.optimize.InstanceInitializerOutliner;
import com.android.tools.r8.ir.optimize.NaturalIntLoopRemover;
import com.android.tools.r8.ir.optimize.RedundantFieldLoadAndStoreElimination;
import com.android.tools.r8.ir.optimize.ReflectionOptimizer;
import com.android.tools.r8.ir.optimize.ServiceLoaderRewriter;
+import com.android.tools.r8.ir.optimize.api.InstanceInitializerOutliner;
import com.android.tools.r8.ir.optimize.classinliner.ClassInliner;
import com.android.tools.r8.ir.optimize.enums.EnumUnboxer;
import com.android.tools.r8.ir.optimize.enums.EnumValueOptimizer;
@@ -87,10 +65,8 @@
import com.android.tools.r8.lightir.LIRCode;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.IdentifierNameStringMarker;
-import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorIROptimizer;
import com.android.tools.r8.position.MethodPosition;
-import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepMethodInfo;
import com.android.tools.r8.shaking.LibraryMethodOverrideAnalysis;
@@ -101,15 +77,16 @@
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.NeverMergeGroup;
import com.android.tools.r8.utils.LazyBox;
-import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.Sets;
-import java.util.ArrayList;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -120,32 +97,32 @@
public final AppView<?> appView;
- private final Timing timing;
+ protected final Timing timing;
public final Outliner outliner;
private final ClassInitializerDefaultsOptimization classInitializerDefaultsOptimization;
- private final CfInstructionDesugaringCollection instructionDesugaring;
- private final FieldAccessAnalysis fieldAccessAnalysis;
- private final LibraryMethodOverrideAnalysis libraryMethodOverrideAnalysis;
- private final StringOptimizer stringOptimizer;
- private final IdempotentFunctionCallCanonicalizer idempotentFunctionCallCanonicalizer;
+ protected final CfInstructionDesugaringCollection instructionDesugaring;
+ protected final FieldAccessAnalysis fieldAccessAnalysis;
+ protected final LibraryMethodOverrideAnalysis libraryMethodOverrideAnalysis;
+ protected final StringOptimizer stringOptimizer;
+ protected final IdempotentFunctionCallCanonicalizer idempotentFunctionCallCanonicalizer;
private final ClassInliner classInliner;
- private final InternalOptions options;
- private final CfgPrinter printer;
+ protected final InternalOptions options;
+ protected final CfgPrinter printer;
public final CodeRewriter codeRewriter;
public final AssertionErrorTwoArgsConstructorRewriter assertionErrorTwoArgsConstructorRewriter;
private final NaturalIntLoopRemover naturalIntLoopRemover = new NaturalIntLoopRemover();
public final MemberValuePropagation<?> memberValuePropagation;
private final LensCodeRewriter lensCodeRewriter;
- private final Inliner inliner;
- private final IdentifierNameStringMarker identifierNameStringMarker;
+ protected final Inliner inliner;
+ protected final IdentifierNameStringMarker identifierNameStringMarker;
private final Devirtualizer devirtualizer;
- private final CovariantReturnTypeAnnotationTransformer covariantReturnTypeAnnotationTransformer;
+ protected final CovariantReturnTypeAnnotationTransformer covariantReturnTypeAnnotationTransformer;
private final StringSwitchRemover stringSwitchRemover;
private final TypeChecker typeChecker;
- private final ServiceLoaderRewriter serviceLoaderRewriter;
+ protected final ServiceLoaderRewriter serviceLoaderRewriter;
private final EnumValueOptimizer enumValueOptimizer;
- private final EnumUnboxer enumUnboxer;
- private final InstanceInitializerOutliner instanceInitializerOutliner;
+ protected final EnumUnboxer enumUnboxer;
+ protected final InstanceInitializerOutliner instanceInitializerOutliner;
public final AssumeInserter assumeInserter;
private final DynamicTypeOptimization dynamicTypeOptimization;
@@ -155,16 +132,16 @@
private final MethodOptimizationInfoCollector methodOptimizationInfoCollector;
- private final OptimizationFeedbackDelayed delayedOptimizationFeedback =
+ protected final OptimizationFeedbackDelayed delayedOptimizationFeedback =
new OptimizationFeedbackDelayed();
- private final OptimizationFeedback simpleOptimizationFeedback =
+ protected final OptimizationFeedback simpleOptimizationFeedback =
OptimizationFeedbackSimple.getInstance();
- private DexString highestSortingString;
+ protected DexString highestSortingString;
- private List<Action> onWaveDoneActions = null;
- private final Set<DexMethod> prunedMethodsInWave = Sets.newIdentityHashSet();
+ protected List<Action> onWaveDoneActions = null;
+ protected final Set<DexMethod> prunedMethodsInWave = Sets.newIdentityHashSet();
- private final NeverMergeGroup<DexString> neverMerge;
+ protected final NeverMergeGroup<DexString> neverMerge;
// Use AtomicBoolean to satisfy TSAN checking (see b/153714743).
AtomicBoolean seenNotNeverMergePrefix = new AtomicBoolean();
AtomicBoolean seenNeverMergePrefix = new AtomicBoolean();
@@ -328,239 +305,6 @@
return inliner;
}
- private void synthesizeBridgesForNestBasedAccessesOnClasspath(
- D8MethodProcessor methodProcessor, ExecutorService executorService)
- throws ExecutionException {
- instructionDesugaring.withD8NestBasedAccessDesugaring(
- d8NestBasedAccessDesugaring ->
- d8NestBasedAccessDesugaring.synthesizeBridgesForNestBasedAccessesOnClasspath(
- methodProcessor, executorService));
- methodProcessor.awaitMethodProcessing();
- }
-
- private void reportNestDesugarDependencies() {
- instructionDesugaring.withD8NestBasedAccessDesugaring(
- D8NestBasedAccessDesugaring::reportDesugarDependencies);
- }
-
- private void clearNestAttributes() {
- instructionDesugaring.withD8NestBasedAccessDesugaring(
- D8NestBasedAccessDesugaring::clearNestAttributes);
- }
-
- private void processCovariantReturnTypeAnnotations(Builder<?> builder) {
- if (covariantReturnTypeAnnotationTransformer != null) {
- covariantReturnTypeAnnotationTransformer.process(builder);
- }
- }
-
- public void convert(AppView<AppInfo> appView, ExecutorService executor)
- throws ExecutionException {
- LambdaDeserializationMethodRemover.run(appView);
- workaroundAbstractMethodOnNonAbstractClassVerificationBug(executor);
- DexApplication application = appView.appInfo().app();
- D8MethodProcessor methodProcessor = new D8MethodProcessor(this, executor);
- InterfaceProcessor interfaceProcessor = InterfaceProcessor.create(appView);
-
- timing.begin("IR conversion");
-
- convertClasses(methodProcessor, interfaceProcessor, executor);
-
- reportNestDesugarDependencies();
- clearNestAttributes();
-
- if (instanceInitializerOutliner != null) {
- processSimpleSynthesizeMethods(instanceInitializerOutliner.getSynthesizedMethods(), executor);
- }
- if (assertionErrorTwoArgsConstructorRewriter != null) {
- processSimpleSynthesizeMethods(
- assertionErrorTwoArgsConstructorRewriter.getSynthesizedMethods(), executor);
- }
-
- application = commitPendingSyntheticItemsD8(appView, application);
-
- postProcessingDesugaringForD8(methodProcessor, interfaceProcessor, executor);
-
- application = commitPendingSyntheticItemsD8(appView, application);
-
- // Build a new application with jumbo string info,
- Builder<?> builder = application.builder().setHighestSortingString(highestSortingString);
-
- if (appView.options().isDesugaredLibraryCompilation()) {
- new EmulatedInterfaceApplicationRewriter(appView).rewriteApplication(builder);
- new L8InnerOuterAttributeEraser(appView).run();
- }
- processCovariantReturnTypeAnnotations(builder);
-
- timing.end();
-
- application = builder.build();
- appView.setAppInfo(
- new AppInfo(
- appView.appInfo().getSyntheticItems().commit(application),
- appView.appInfo().getMainDexInfo()));
- }
-
- private DexApplication commitPendingSyntheticItemsD8(
- AppView<AppInfo> appView, DexApplication application) {
- if (appView.getSyntheticItems().hasPendingSyntheticClasses()) {
- appView.setAppInfo(
- new AppInfo(
- appView.appInfo().getSyntheticItems().commit(application),
- appView.appInfo().getMainDexInfo()));
- application = appView.appInfo().app();
- }
- return application;
- }
-
- private static void commitPendingSyntheticItemsR8(AppView<AppInfoWithLiveness> appView) {
- if (appView.getSyntheticItems().hasPendingSyntheticClasses()) {
- appView.setAppInfo(
- appView
- .appInfo()
- .rebuildWithLiveness(appView.getSyntheticItems().commit(appView.appInfo().app())));
- }
- }
-
- public void classSynthesisDesugaring(
- ExecutorService executorService,
- CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer)
- throws ExecutionException {
- CfClassSynthesizerDesugaringCollection.create(appView)
- .synthesizeClasses(executorService, classSynthesizerEventConsumer);
- }
-
- private void postProcessingDesugaringForD8(
- D8MethodProcessor methodProcessor,
- InterfaceProcessor interfaceProcessor,
- ExecutorService executorService)
- throws ExecutionException {
- ArtProfileCollectionAdditions artProfileCollectionAdditions =
- ArtProfileCollectionAdditions.create(appView);
- CfPostProcessingDesugaringEventConsumer eventConsumer =
- CfPostProcessingDesugaringEventConsumer.createForD8(
- artProfileCollectionAdditions, methodProcessor, instructionDesugaring);
- methodProcessor.newWave();
- InterfaceMethodProcessorFacade interfaceDesugaring =
- instructionDesugaring.getInterfaceMethodPostProcessingDesugaringD8(
- ExcludeDexResources, interfaceProcessor);
- CfPostProcessingDesugaringCollection.create(appView, interfaceDesugaring)
- .postProcessingDesugaring(
- appView.appInfo().classes(), m -> true, eventConsumer, executorService);
- methodProcessor.awaitMethodProcessing();
- eventConsumer.finalizeDesugaring();
- artProfileCollectionAdditions.commit(appView);
- }
-
- private void convertClasses(
- D8MethodProcessor methodProcessor,
- InterfaceProcessor interfaceProcessor,
- ExecutorService executorService)
- throws ExecutionException {
- ClassConverterResult classConverterResult =
- ClassConverter.create(appView, this, methodProcessor, interfaceProcessor)
- .convertClasses(executorService);
-
- // The synthesis of accessibility bridges in nest based access desugaring will schedule and
- // await the processing of synthesized methods.
- synthesizeBridgesForNestBasedAccessesOnClasspath(methodProcessor, executorService);
-
- // There should be no outstanding method processing.
- methodProcessor.verifyNoPendingMethodProcessing();
-
- rewriteEnclosingLambdaMethodAttributes(
- appView, classConverterResult.getForcefullyMovedLambdaMethods());
-
- instructionDesugaring.withDesugaredLibraryAPIConverter(
- DesugaredLibraryAPIConverter::generateTrackingWarnings);
- }
-
- public void prepareDesugaringForD8(
- CfInstructionDesugaringEventConsumer desugaringEventConsumer, ExecutorService executorService)
- throws ExecutionException {
- // Prepare desugaring by collecting all the synthetic methods required on program classes.
- ProgramAdditions programAdditions = new ProgramAdditions();
- ThreadUtils.processItems(
- appView.appInfo().classes(),
- clazz -> {
- clazz.forEachProgramMethodMatching(
- method -> method.hasCode() && method.getCode().isCfCode(),
- method ->
- instructionDesugaring.prepare(method, desugaringEventConsumer, programAdditions));
- },
- executorService);
- programAdditions.apply(executorService);
- }
-
- void convertMethods(
- DexProgramClass clazz,
- CfInstructionDesugaringEventConsumer desugaringEventConsumer,
- D8MethodProcessor methodProcessor,
- InterfaceProcessor interfaceProcessor) {
- // When converting all methods on a class always convert <clinit> first.
- ProgramMethod classInitializer = clazz.getProgramClassInitializer();
-
- // TODO(b/179755192): We currently need to copy the class' methods, to avoid a
- // ConcurrentModificationException from the insertion of methods due to invoke-special
- // desugaring. By building up waves of methods in the class converter, we would not need to
- // iterate the methods of a class during while its methods are being processed, which avoids
- // the need to copy the method list.
- List<ProgramMethod> methods = ListUtils.newArrayList(clazz::forEachProgramMethod);
- if (classInitializer != null) {
- methodProcessor.processMethod(classInitializer, desugaringEventConsumer);
- }
-
- for (ProgramMethod method : methods) {
- if (!method.getDefinition().isClassInitializer()) {
- methodProcessor.processMethod(method, desugaringEventConsumer);
- if (interfaceProcessor != null) {
- interfaceProcessor.processMethod(method, desugaringEventConsumer);
- }
- }
- }
-
- // The class file version is downgraded after compilation. Some of the desugaring might need
- // the initial class file version to determine how far a method can be downgraded.
- if (options.isGeneratingClassFiles() && clazz.hasClassFileVersion()) {
- clazz.downgradeInitialClassFileVersion(
- appView.options().classFileVersionAfterDesugaring(clazz.getInitialClassFileVersion()));
- }
- }
-
- void convertMethod(
- ProgramMethod method,
- CfInstructionDesugaringEventConsumer desugaringEventConsumer,
- MethodProcessor methodProcessor,
- MethodProcessingContext methodProcessingContext) {
- DexEncodedMethod definition = method.getDefinition();
- if (options.isGeneratingClassFiles() && definition.hasClassFileVersion()) {
- definition.downgradeClassFileVersion(
- appView.options().classFileVersionAfterDesugaring(definition.getClassFileVersion()));
- }
- if (definition.getCode() == null) {
- return;
- }
- if (!options.methodMatchesFilter(definition)) {
- return;
- }
- checkPrefixMerging(method);
- if (options.isGeneratingClassFiles()
- || !(options.passthroughDexCode && definition.getCode().isDexCode())) {
- // We do not process in call graph order, so anything could be a leaf.
- rewriteNonDesugaredCode(
- method,
- desugaringEventConsumer,
- simpleOptimizationFeedback,
- methodProcessor,
- methodProcessingContext);
- } else {
- assert definition.getCode().isDexCode();
- }
- if (!options.isGeneratingClassFiles()) {
- updateHighestSortingStrings(definition);
- }
- }
-
private boolean needsIRConversion(ProgramMethod method) {
if (method.getDefinition().getCode().isThrowNullCode()) {
return false;
@@ -575,90 +319,7 @@
return !options.isGeneratingClassFiles();
}
- private void checkPrefixMerging(ProgramMethod method) {
- if (!appView.options().enableNeverMergePrefixes) {
- return;
- }
- DexString descriptor = method.getHolderType().descriptor;
- for (DexString neverMergePrefix : neverMerge.getPrefixes()) {
- if (descriptor.startsWith(neverMergePrefix)) {
- seenNeverMergePrefix.getAndSet(true);
- } else {
- for (DexString exceptionPrefix : neverMerge.getExceptionPrefixes()) {
- if (!descriptor.startsWith(exceptionPrefix)) {
- seenNotNeverMergePrefix.getAndSet(true);
- break;
- }
- }
- }
- // Don't mix.
- // TODO(b/168001352): Consider requiring that no 'never merge' prefix is ever seen as a
- // passthrough object.
- if (seenNeverMergePrefix.get() && seenNotNeverMergePrefix.get()) {
- StringBuilder message = new StringBuilder();
- message
- .append("Merging DEX file containing classes with prefix")
- .append(neverMerge.getPrefixes().size() > 1 ? "es " : " ");
- for (int i = 0; i < neverMerge.getPrefixes().size(); i++) {
- message
- .append("'")
- .append(neverMerge.getPrefixes().get(i).toString().substring(1).replace('/', '.'))
- .append("'")
- .append(i < neverMerge.getPrefixes().size() - 1 ? ", " : "");
- }
- if (!neverMerge.getExceptionPrefixes().isEmpty()) {
- message
- .append(" with other classes, except classes with prefix")
- .append(neverMerge.getExceptionPrefixes().size() > 1 ? "es " : " ");
- for (int i = 0; i < neverMerge.getExceptionPrefixes().size(); i++) {
- message
- .append("'")
- .append(
- neverMerge
- .getExceptionPrefixes()
- .get(i)
- .toString()
- .substring(1)
- .replace('/', '.'))
- .append("'")
- .append(i < neverMerge.getExceptionPrefixes().size() - 1 ? ", " : "");
- }
- message.append(",");
- } else {
- message.append(" with classes with any other prefixes");
- }
- message.append(" is not allowed: ");
- boolean first = true;
- int limit = 11;
- for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
- if (!clazz.type.descriptor.startsWith(neverMergePrefix)) {
- boolean hasExceptionPrefix = false;
- for (DexString exceptionPrefix : neverMerge.getExceptionPrefixes()) {
- hasExceptionPrefix =
- hasExceptionPrefix | clazz.type.descriptor.startsWith(exceptionPrefix);
- }
- if (hasExceptionPrefix) {
- continue;
- }
- if (limit-- < 0) {
- message.append("..");
- break;
- }
- if (first) {
- first = false;
- } else {
- message.append(", ");
- }
- message.append(clazz.type);
- }
- }
- message.append(".");
- throw new CompilationError(message.toString());
- }
- }
- }
-
- private void workaroundAbstractMethodOnNonAbstractClassVerificationBug(
+ protected void workaroundAbstractMethodOnNonAbstractClassVerificationBug(
ExecutorService executorService) throws ExecutionException {
if (!options.canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug()) {
return;
@@ -675,225 +336,6 @@
executorService);
}
- public DexApplication optimize(
- AppView<AppInfoWithLiveness> appView, ExecutorService executorService)
- throws ExecutionException {
- // Desugaring happens in the enqueuer.
- assert instructionDesugaring.isEmpty();
-
- workaroundAbstractMethodOnNonAbstractClassVerificationBug(executorService);
-
- // The process is in two phases in general.
- // 1) Subject all DexEncodedMethods to optimization, except some optimizations that require
- // reprocessing IR code of methods, e.g., outlining, double-inlining, class staticizer, etc.
- // - a side effect is candidates for those optimizations are identified.
- // 2) Revisit DexEncodedMethods for the collected candidates.
-
- printPhase("Primary optimization pass");
-
- GraphLens graphLensForPrimaryOptimizationPass = appView.graphLens();
-
- // Setup optimizations for the primary optimization pass.
- appView.withArgumentPropagator(
- argumentPropagator -> argumentPropagator.initializeCodeScanner(executorService, timing));
- enumUnboxer.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass);
- outliner.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass);
-
- if (fieldAccessAnalysis != null) {
- fieldAccessAnalysis.fieldAssignmentTracker().initialize();
- }
-
- // Process the application identifying outlining candidates.
- OptimizationFeedbackDelayed feedback = delayedOptimizationFeedback;
- PostMethodProcessor.Builder postMethodProcessorBuilder =
- new PostMethodProcessor.Builder(graphLensForPrimaryOptimizationPass);
- {
- timing.begin("Build primary method processor");
- PrimaryMethodProcessor primaryMethodProcessor =
- PrimaryMethodProcessor.create(appView.withLiveness(), executorService, timing);
- timing.end();
- timing.begin("IR conversion phase 1");
- assert appView.graphLens() == graphLensForPrimaryOptimizationPass;
- primaryMethodProcessor.forEachMethod(
- (method, methodProcessingContext) ->
- processDesugaredMethod(
- method, feedback, primaryMethodProcessor, methodProcessingContext),
- this::waveStart,
- this::waveDone,
- timing,
- executorService);
- lastWaveDone(postMethodProcessorBuilder, executorService);
- assert appView.graphLens() == graphLensForPrimaryOptimizationPass;
- timing.end();
- }
-
- // The field access info collection is not maintained during IR processing.
- appView.appInfo().withLiveness().getFieldAccessInfoCollection().destroyAccessContexts();
-
- // Assure that no more optimization feedback left after primary processing.
- assert feedback.noUpdatesLeft();
- appView.setAllCodeProcessed();
-
- // All the code has been processed so the rewriting required by the lenses is done everywhere,
- // we clear lens code rewriting so that the lens rewriter can be re-executed in phase 2 if new
- // lenses with code rewriting are added.
- appView.clearCodeRewritings();
-
- // Commit synthetics from the primary optimization pass.
- commitPendingSyntheticItemsR8(appView);
-
- // Post processing:
- // 1) Second pass for methods whose collected call site information become more precise.
- // 2) Second inlining pass for dealing with double inline callers.
- printPhase("Post optimization pass");
-
- // Analyze the data collected by the argument propagator, use the analysis result to update
- // the parameter optimization infos, and rewrite the application.
- // TODO(b/199237357): Automatically rewrite state when lens changes.
- enumUnboxer.rewriteWithLens();
- outliner.rewriteWithLens();
- appView.withArgumentPropagator(
- argumentPropagator ->
- argumentPropagator.tearDownCodeScanner(
- this, postMethodProcessorBuilder, executorService, timing));
-
- if (libraryMethodOverrideAnalysis != null) {
- libraryMethodOverrideAnalysis.finish();
- }
-
- if (!options.debug) {
- new TrivialFieldAccessReprocessor(appView.withLiveness(), postMethodProcessorBuilder)
- .run(executorService, feedback, timing);
- }
-
- outliner.rewriteWithLens();
- enumUnboxer.unboxEnums(appView, this, postMethodProcessorBuilder, executorService, feedback);
- appView.unboxedEnums().checkEnumsUnboxed(appView);
-
- GraphLens graphLensForSecondaryOptimizationPass = appView.graphLens();
-
- outliner.rewriteWithLens();
-
- timing.begin("IR conversion phase 2");
- timing.begin("Build post method processor");
- PostMethodProcessor postMethodProcessor =
- postMethodProcessorBuilder.build(appView, executorService, timing);
- timing.end();
- if (postMethodProcessor != null) {
- assert !options.debug;
- assert appView.graphLens() == graphLensForSecondaryOptimizationPass;
- timing.begin("Process code");
- postMethodProcessor.forEachMethod(
- (method, methodProcessingContext) ->
- processDesugaredMethod(
- method, feedback, postMethodProcessor, methodProcessingContext),
- feedback,
- executorService,
- timing);
- timing.end();
- timing.begin("Update visible optimization info");
- feedback.updateVisibleOptimizationInfo();
- timing.end();
- assert appView.graphLens() == graphLensForSecondaryOptimizationPass;
- }
- timing.end();
-
- enumUnboxer.unsetRewriter();
-
- // All the code that should be impacted by the lenses inserted between phase 1 and phase 2
- // have now been processed and rewritten, we clear code lens rewriting so that the class
- // staticizer and phase 3 does not perform again the rewriting.
- appView.clearCodeRewritings();
-
- // Commit synthetics before creating a builder (otherwise the builder will not include the
- // synthetics.)
- commitPendingSyntheticItemsR8(appView);
-
- // Build a new application with jumbo string info.
- Builder<?> builder = appView.appInfo().app().builder();
- builder.setHighestSortingString(highestSortingString);
-
- if (serviceLoaderRewriter != null) {
- processSimpleSynthesizeMethods(
- serviceLoaderRewriter.getServiceLoadMethods(), executorService);
- }
-
- if (instanceInitializerOutliner != null) {
- processSimpleSynthesizeMethods(
- instanceInitializerOutliner.getSynthesizedMethods(), executorService);
- }
- if (assertionErrorTwoArgsConstructorRewriter != null) {
- processSimpleSynthesizeMethods(
- assertionErrorTwoArgsConstructorRewriter.getSynthesizedMethods(), executorService);
- }
-
- // Update optimization info for all synthesized methods at once.
- feedback.updateVisibleOptimizationInfo();
-
- // TODO(b/127694949): Adapt to PostOptimization.
- outliner.performOutlining(this, feedback, executorService, timing);
- clearDexMethodCompilationState();
-
- if (identifierNameStringMarker != null) {
- identifierNameStringMarker.decoupleIdentifierNameStringsInFields(executorService);
- }
-
- if (Log.ENABLED) {
- if (idempotentFunctionCallCanonicalizer != null) {
- idempotentFunctionCallCanonicalizer.logResults();
- }
- if (libraryMethodOverrideAnalysis != null) {
- libraryMethodOverrideAnalysis.logResults();
- }
- if (stringOptimizer != null) {
- stringOptimizer.logResult();
- }
- }
-
- // Assure that no more optimization feedback left after post processing.
- assert feedback.noUpdatesLeft();
- return builder.build();
- }
-
- private void waveStart(ProgramMethodSet wave) {
- onWaveDoneActions = Collections.synchronizedList(new ArrayList<>());
- }
-
- private void waveDone(ProgramMethodSet wave, ExecutorService executorService)
- throws ExecutionException {
- delayedOptimizationFeedback.refineAppInfoWithLiveness(appView.appInfo().withLiveness());
- delayedOptimizationFeedback.updateVisibleOptimizationInfo();
- fieldAccessAnalysis.fieldAssignmentTracker().waveDone(wave, delayedOptimizationFeedback);
- appView.withArgumentPropagator(ArgumentPropagator::publishDelayedReprocessingCriteria);
- if (appView.options().protoShrinking().enableRemoveProtoEnumSwitchMap()) {
- appView.protoShrinker().protoEnumSwitchMapRemover.updateVisibleStaticFieldValues();
- }
- enumUnboxer.updateEnumUnboxingCandidatesInfo();
- assert delayedOptimizationFeedback.noUpdatesLeft();
- onWaveDoneActions.forEach(com.android.tools.r8.utils.Action::execute);
- onWaveDoneActions = null;
- if (!prunedMethodsInWave.isEmpty()) {
- appView.pruneItems(
- PrunedItems.builder()
- .setRemovedMethods(prunedMethodsInWave)
- .setPrunedApp(appView.appInfo().app())
- .build(),
- executorService);
- prunedMethodsInWave.clear();
- }
- }
-
- private void lastWaveDone(
- PostMethodProcessor.Builder postMethodProcessorBuilder, ExecutorService executorService)
- throws ExecutionException {
- if (inliner != null) {
- inliner.onLastWaveDone(postMethodProcessorBuilder, executorService, timing);
- }
-
- // Ensure determinism of method-to-reprocess set.
- appView.testing().checkDeterminism(postMethodProcessorBuilder::dump);
- }
-
public void addWaveDoneAction(com.android.tools.r8.utils.Action action) {
if (!appView.enableWholeProgramOptimizations()) {
throw new Unreachable("addWaveDoneAction() should never be used in D8.");
@@ -908,7 +350,7 @@
return onWaveDoneActions != null;
}
- private void processSimpleSynthesizeMethods(
+ protected void processSimpleSynthesizeMethods(
List<ProgramMethod> serviceLoadMethods, ExecutorService executorService)
throws ExecutionException {
ThreadUtils.processItems(
@@ -922,14 +364,6 @@
removeDeadCodeAndFinalizeIR(code, OptimizationFeedbackIgnore.getInstance(), Timing.empty());
}
- private void clearDexMethodCompilationState() {
- appView.appInfo().classes().forEach(this::clearDexMethodCompilationState);
- }
-
- private void clearDexMethodCompilationState(DexProgramClass clazz) {
- clazz.forEachMethod(DexEncodedMethod::markNotProcessed);
- }
-
/**
* This will replace the Dex code in the method with the Dex code generated from the provided IR.
*
@@ -958,18 +392,21 @@
}
public void optimizeSynthesizedMethods(
- List<ProgramMethod> programMethods, ExecutorService executorService)
+ List<ProgramMethod> programMethods,
+ MethodProcessorEventConsumer eventConsumer,
+ ExecutorService executorService)
throws ExecutionException {
// Process the generated class, but don't apply any outlining.
ProgramMethodSet methods = ProgramMethodSet.create(programMethods::forEach);
- processMethodsConcurrently(methods, executorService);
+ processMethodsConcurrently(methods, eventConsumer, executorService);
}
- public void optimizeSynthesizedMethod(ProgramMethod synthesizedMethod) {
+ public void optimizeSynthesizedMethod(
+ ProgramMethod synthesizedMethod, MethodProcessorEventConsumer eventConsumer) {
if (!synthesizedMethod.getDefinition().isProcessed()) {
// Process the generated method, but don't apply any outlining.
OneTimeMethodProcessor methodProcessor =
- OneTimeMethodProcessor.create(synthesizedMethod, appView);
+ OneTimeMethodProcessor.create(synthesizedMethod, eventConsumer, appView);
methodProcessor.forEachWaveWithExtension(
(method, methodProcessingContext) ->
processDesugaredMethod(
@@ -978,19 +415,25 @@
}
public void processClassesConcurrently(
- Collection<DexProgramClass> classes, ExecutorService executorService)
+ Collection<DexProgramClass> classes,
+ MethodProcessorEventConsumer eventConsumer,
+ ExecutorService executorService)
throws ExecutionException {
ProgramMethodSet wave = ProgramMethodSet.create();
for (DexProgramClass clazz : classes) {
clazz.forEachProgramMethod(wave::add);
}
- processMethodsConcurrently(wave, executorService);
+ processMethodsConcurrently(wave, eventConsumer, executorService);
}
- public void processMethodsConcurrently(ProgramMethodSet wave, ExecutorService executorService)
+ public void processMethodsConcurrently(
+ ProgramMethodSet wave,
+ MethodProcessorEventConsumer eventConsumer,
+ ExecutorService executorService)
throws ExecutionException {
if (!wave.isEmpty()) {
- OneTimeMethodProcessor methodProcessor = OneTimeMethodProcessor.create(wave, appView);
+ OneTimeMethodProcessor methodProcessor =
+ OneTimeMethodProcessor.create(wave, eventConsumer, appView);
methodProcessor.forEachWaveWithExtension(
(method, methodProcessingContext) ->
processDesugaredMethod(
@@ -999,10 +442,24 @@
}
}
- private String logCode(InternalOptions options, DexEncodedMethod method) {
+ String logCode(InternalOptions options, DexEncodedMethod method) {
return options.useSmaliSyntax ? method.toSmaliString(null) : method.codeToString();
}
+ void printCfg() throws IOException {
+ if (printer != null) {
+ if (options.printCfgFile == null || options.printCfgFile.isEmpty()) {
+ System.out.print(printer.toString());
+ } else {
+ try (OutputStreamWriter writer =
+ new OutputStreamWriter(
+ new FileOutputStream(options.printCfgFile), StandardCharsets.UTF_8)) {
+ writer.write(printer.toString());
+ }
+ }
+ }
+ }
+
// TODO(b/140766440): Make this receive a list of CodeOptimizations to conduct.
public Timing processDesugaredMethod(
ProgramMethod method,
@@ -1029,24 +486,6 @@
}
}
- Timing rewriteNonDesugaredCode(
- ProgramMethod method,
- CfInstructionDesugaringEventConsumer desugaringEventConsumer,
- OptimizationFeedback feedback,
- MethodProcessor methodProcessor,
- MethodProcessingContext methodProcessingContext) {
- return ExceptionUtils.withOriginAndPositionAttachmentHandler(
- method.getOrigin(),
- new MethodPosition(method.getReference().asMethodReference()),
- () ->
- rewriteNonDesugaredCodeInternal(
- method,
- desugaringEventConsumer,
- feedback,
- methodProcessor,
- methodProcessingContext));
- }
-
Timing rewriteDesugaredCode(
ProgramMethod method,
OptimizationFeedback feedback,
@@ -1060,24 +499,7 @@
method, feedback, methodProcessor, methodProcessingContext));
}
- private Timing rewriteNonDesugaredCodeInternal(
- ProgramMethod method,
- CfInstructionDesugaringEventConsumer desugaringEventConsumer,
- OptimizationFeedback feedback,
- MethodProcessor methodProcessor,
- MethodProcessingContext methodProcessingContext) {
- boolean didDesugar = desugar(method, desugaringEventConsumer, methodProcessingContext);
- if (Log.ENABLED && didDesugar) {
- Log.debug(
- getClass(),
- "Desugared code for %s:\n%s",
- method.toSourceString(),
- logCode(options, method.getDefinition()));
- }
- return rewriteDesugaredCodeInternal(method, feedback, methodProcessor, methodProcessingContext);
- }
-
- private Timing rewriteDesugaredCodeInternal(
+ protected Timing rewriteDesugaredCodeInternal(
ProgramMethod method,
OptimizationFeedback feedback,
MethodProcessor methodProcessor,
@@ -1110,24 +532,8 @@
return optimize(code, feedback, methodProcessor, methodProcessingContext);
}
- private boolean desugar(
- ProgramMethod method,
- CfInstructionDesugaringEventConsumer desugaringEventConsumer,
- MethodProcessingContext methodProcessingContext) {
- // Due to some mandatory desugarings, we need to run desugaring even if desugaring is disabled.
- if (!method.getDefinition().getCode().isCfCode()) {
- return false;
- }
- instructionDesugaring.scan(method, desugaringEventConsumer);
- if (instructionDesugaring.needsDesugaring(method)) {
- instructionDesugaring.desugar(method, methodProcessingContext, desugaringEventConsumer);
- return true;
- }
- return false;
- }
-
// TODO(b/140766440): Convert all sub steps an implementer of CodeOptimization
- private Timing optimize(
+ Timing optimize(
IRCode code,
OptimizationFeedback feedback,
MethodProcessor methodProcessor,
@@ -1253,7 +659,7 @@
if (instanceInitializerOutliner != null) {
instanceInitializerOutliner.rewriteInstanceInitializers(
- code, context, methodProcessingContext);
+ code, context, methodProcessor, methodProcessingContext);
assert code.verifyTypes(appView);
}
@@ -1755,7 +1161,7 @@
return true;
}
- private synchronized void updateHighestSortingStrings(DexEncodedMethod method) {
+ protected synchronized void updateHighestSortingStrings(DexEncodedMethod method) {
Code code = method.getCode();
assert code.isDexWritableCode();
DexString highestSortingReferencedString = code.asDexWritableCode().getHighestSortingString();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRToCfFinalizer.java b/src/main/java/com/android/tools/r8/ir/conversion/IRToCfFinalizer.java
index 74d0376..0f3a0aa 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRToCfFinalizer.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRToCfFinalizer.java
@@ -22,6 +22,11 @@
public CfCode finalizeCode(
IRCode code, BytecodeMetadataProvider bytecodeMetadataProvider, Timing timing) {
ProgramMethod method = code.context();
- return new CfBuilder(appView, method, code, bytecodeMetadataProvider).build(deadCodeRemover);
+ timing.begin("Finalize CF code");
+ CfCode build =
+ new CfBuilder(appView, method, code, bytecodeMetadataProvider)
+ .build(deadCodeRemover, timing);
+ timing.end();
+ return build;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
index dbe72e8..ae28180 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessor.java
@@ -23,6 +23,8 @@
public abstract MethodProcessingContext createMethodProcessingContext(ProgramMethod method);
+ public abstract MethodProcessorEventConsumer getEventConsumer();
+
public abstract boolean isProcessedConcurrently(ProgramMethod method);
public abstract boolean shouldApplyCodeRewritings(ProgramMethod method);
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java
new file mode 100644
index 0000000..d002cd5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/MethodProcessorEventConsumer.java
@@ -0,0 +1,97 @@
+// Copyright (c) 2023, 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.conversion;
+
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizationsEventConsumer;
+import com.android.tools.r8.ir.optimize.api.InstanceInitializerOutlinerEventConsumer;
+import com.android.tools.r8.ir.optimize.enums.EnumUnboxerMethodProcessorEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingMethodProcessorEventConsumer;
+
+public abstract class MethodProcessorEventConsumer
+ implements EnumUnboxerMethodProcessorEventConsumer,
+ InstanceInitializerOutlinerEventConsumer,
+ UtilityMethodsForCodeOptimizationsEventConsumer {
+
+ public static MethodProcessorEventConsumer create(
+ ArtProfileCollectionAdditions artProfileCollectionAdditions) {
+ return ArtProfileRewritingMethodProcessorEventConsumer.attach(
+ artProfileCollectionAdditions, empty());
+ }
+
+ public static MethodProcessorEventConsumer empty() {
+ return EmptyMethodProcessorEventConsumer.getInstance();
+ }
+
+ private static class EmptyMethodProcessorEventConsumer extends MethodProcessorEventConsumer {
+
+ private static final EmptyMethodProcessorEventConsumer INSTANCE =
+ new EmptyMethodProcessorEventConsumer();
+
+ private EmptyMethodProcessorEventConsumer() {}
+
+ static EmptyMethodProcessorEventConsumer getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public void acceptEnumUnboxerCheckNotZeroContext(ProgramMethod method, ProgramMethod context) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptEnumUnboxerLocalUtilityClassMethodContext(
+ ProgramMethod method, ProgramMethod context) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptEnumUnboxerSharedUtilityClassMethodContext(
+ ProgramMethod method, ProgramMethod context) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptInstanceInitializerOutline(ProgramMethod method, ProgramMethod context) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptUtilityToStringIfNotNullMethod(ProgramMethod method, ProgramMethod context) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptUtilityThrowClassCastExceptionIfNotNullMethod(
+ ProgramMethod method, ProgramMethod context) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptUtilityThrowIllegalAccessErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptUtilityThrowIncompatibleClassChangeErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptUtilityThrowNoSuchMethodErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptUtilityThrowRuntimeExceptionWithMessageMethod(
+ ProgramMethod method, ProgramMethod context) {
+ // Intentionally empty.
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
index 65a3287..c243d46 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/OneTimeMethodProcessor.java
@@ -18,34 +18,49 @@
*/
public class OneTimeMethodProcessor extends MethodProcessorWithWave {
+ private final MethodProcessorEventConsumer eventConsumer;
private final ProcessorContext processorContext;
- private OneTimeMethodProcessor(ProcessorContext processorContext, ProgramMethodSet wave) {
+ private OneTimeMethodProcessor(
+ MethodProcessorEventConsumer eventConsumer,
+ ProcessorContext processorContext,
+ ProgramMethodSet wave) {
+ this.eventConsumer = eventConsumer;
this.processorContext = processorContext;
this.wave = wave;
}
- public static Builder builder(ProcessorContext processorContext) {
- return new Builder(processorContext);
- }
-
- public static OneTimeMethodProcessor create(ProgramMethod methodToProcess, AppView<?> appView) {
- return create(ProgramMethodSet.create(methodToProcess), appView);
+ public static Builder builder(
+ MethodProcessorEventConsumer eventConsumer, ProcessorContext processorContext) {
+ return new Builder(eventConsumer, processorContext);
}
public static OneTimeMethodProcessor create(
- ProgramMethod methodToProcess, ProcessorContext processorContext) {
- return create(ProgramMethodSet.create(methodToProcess), processorContext);
+ ProgramMethod methodToProcess,
+ MethodProcessorEventConsumer eventConsumer,
+ AppView<?> appView) {
+ return create(ProgramMethodSet.create(methodToProcess), eventConsumer, appView);
}
public static OneTimeMethodProcessor create(
- ProgramMethodSet methodsToProcess, AppView<?> appView) {
- return create(methodsToProcess, appView.createProcessorContext());
+ ProgramMethod methodToProcess,
+ MethodProcessorEventConsumer eventConsumer,
+ ProcessorContext processorContext) {
+ return create(ProgramMethodSet.create(methodToProcess), eventConsumer, processorContext);
}
public static OneTimeMethodProcessor create(
- ProgramMethodSet methodsToProcess, ProcessorContext processorContext) {
- return new OneTimeMethodProcessor(processorContext, methodsToProcess);
+ ProgramMethodSet methodsToProcess,
+ MethodProcessorEventConsumer eventConsumer,
+ AppView<?> appView) {
+ return create(methodsToProcess, eventConsumer, appView.createProcessorContext());
+ }
+
+ public static OneTimeMethodProcessor create(
+ ProgramMethodSet methodsToProcess,
+ MethodProcessorEventConsumer eventConsumer,
+ ProcessorContext processorContext) {
+ return new OneTimeMethodProcessor(eventConsumer, processorContext, methodsToProcess);
}
@Override
@@ -54,6 +69,11 @@
}
@Override
+ public MethodProcessorEventConsumer getEventConsumer() {
+ return eventConsumer;
+ }
+
+ @Override
public boolean shouldApplyCodeRewritings(ProgramMethod method) {
return true;
}
@@ -86,9 +106,12 @@
public static class Builder {
private final ProgramMethodSet methodsToProcess = ProgramMethodSet.create();
+
+ private final MethodProcessorEventConsumer eventConsumer;
private final ProcessorContext processorContext;
- Builder(ProcessorContext processorContext) {
+ Builder(MethodProcessorEventConsumer eventConsumer, ProcessorContext processorContext) {
+ this.eventConsumer = eventConsumer;
this.processorContext = processorContext;
}
@@ -98,7 +121,7 @@
}
public OneTimeMethodProcessor build() {
- return create(methodsToProcess, processorContext);
+ return create(methodsToProcess, eventConsumer, processorContext);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index dd82c2d..c7b0ae3 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -36,11 +36,16 @@
public class PostMethodProcessor extends MethodProcessorWithWave {
+ private final MethodProcessorEventConsumer eventConsumer;
private final ProcessorContext processorContext;
private final Deque<ProgramMethodSet> waves;
private final ProgramMethodSet processed = ProgramMethodSet.create();
- private PostMethodProcessor(AppView<AppInfoWithLiveness> appView, CallGraph callGraph) {
+ private PostMethodProcessor(
+ AppView<AppInfoWithLiveness> appView,
+ CallGraph callGraph,
+ MethodProcessorEventConsumer eventConsumer) {
+ this.eventConsumer = eventConsumer;
this.processorContext = appView.createProcessorContext();
this.waves = createWaves(callGraph);
}
@@ -51,6 +56,11 @@
}
@Override
+ public MethodProcessorEventConsumer getEventConsumer() {
+ return eventConsumer;
+ }
+
+ @Override
public boolean isPostMethodProcessor() {
return true;
}
@@ -120,7 +130,10 @@
}
PostMethodProcessor build(
- AppView<AppInfoWithLiveness> appView, ExecutorService executorService, Timing timing)
+ AppView<AppInfoWithLiveness> appView,
+ MethodProcessorEventConsumer eventConsumer,
+ ExecutorService executorService,
+ Timing timing)
throws ExecutionException {
Set<DexMethod> reprocessMethods = appView.appInfo().getReprocessMethods();
if (!reprocessMethods.isEmpty()) {
@@ -142,7 +155,7 @@
ProgramMethodSet methodsToReprocess = methodsToReprocessBuilder.build(appView);
CallGraph callGraph =
new PartialCallGraphBuilder(appView, methodsToReprocess).build(executorService, timing);
- return new PostMethodProcessor(appView, callGraph);
+ return new PostMethodProcessor(appView, callGraph, eventConsumer);
}
public void dump(DeterminismChecker determinismChecker) throws IOException {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
new file mode 100644
index 0000000..1d06a25
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryD8L8IRConverter.java
@@ -0,0 +1,417 @@
+// Copyright (c) 2023, 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.conversion;
+
+import static com.android.tools.r8.ir.desugar.itf.InterfaceMethodRewriter.Flavor.ExcludeDexResources;
+import static com.android.tools.r8.ir.desugar.lambda.D8LambdaDesugaring.rewriteEnclosingLambdaMethodAttributes;
+
+import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
+import com.android.tools.r8.errors.CompilationError;
+import com.android.tools.r8.graph.AppInfo;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexApplication.Builder;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexString;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringCollection;
+import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringCollection;
+import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.ProgramAdditions;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryAPIConverter;
+import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceApplicationRewriter;
+import com.android.tools.r8.ir.desugar.itf.InterfaceMethodProcessorFacade;
+import com.android.tools.r8.ir.desugar.itf.InterfaceProcessor;
+import com.android.tools.r8.ir.desugar.itf.L8InnerOuterAttributeEraser;
+import com.android.tools.r8.ir.desugar.lambda.LambdaDeserializationMethodRemover;
+import com.android.tools.r8.ir.desugar.nest.D8NestBasedAccessDesugaring;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.position.MethodPosition;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.ExceptionUtils;
+import com.android.tools.r8.utils.ListUtils;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public class PrimaryD8L8IRConverter extends IRConverter {
+
+ public PrimaryD8L8IRConverter(AppView<AppInfo> appView, Timing timing) {
+ super(appView, timing, appView.options().printCfg ? new CfgPrinter() : null);
+ }
+
+ public void convert(AppView<AppInfo> appView, ExecutorService executorService)
+ throws ExecutionException, IOException {
+ LambdaDeserializationMethodRemover.run(appView);
+ workaroundAbstractMethodOnNonAbstractClassVerificationBug(executorService);
+ DexApplication application = appView.appInfo().app();
+ ArtProfileCollectionAdditions artProfileCollectionAdditions =
+ ArtProfileCollectionAdditions.create(appView);
+ D8MethodProcessor methodProcessor =
+ new D8MethodProcessor(artProfileCollectionAdditions, this, executorService);
+ InterfaceProcessor interfaceProcessor = InterfaceProcessor.create(appView);
+
+ timing.begin("IR conversion");
+
+ convertClasses(methodProcessor, interfaceProcessor, executorService);
+
+ reportNestDesugarDependencies();
+ clearNestAttributes();
+
+ if (instanceInitializerOutliner != null) {
+ processSimpleSynthesizeMethods(
+ instanceInitializerOutliner.getSynthesizedMethods(), executorService);
+ }
+ if (assertionErrorTwoArgsConstructorRewriter != null) {
+ processSimpleSynthesizeMethods(
+ assertionErrorTwoArgsConstructorRewriter.getSynthesizedMethods(), executorService);
+ }
+
+ application = commitPendingSyntheticItems(appView, application);
+
+ postProcessingDesugaringForD8(methodProcessor, interfaceProcessor, executorService);
+
+ application = commitPendingSyntheticItems(appView, application);
+
+ // Build a new application with jumbo string info,
+ Builder<?> builder = application.builder().setHighestSortingString(highestSortingString);
+
+ if (appView.options().isDesugaredLibraryCompilation()) {
+ new EmulatedInterfaceApplicationRewriter(appView).rewriteApplication(builder);
+ new L8InnerOuterAttributeEraser(appView).run();
+ }
+
+ processCovariantReturnTypeAnnotations(builder);
+
+ timing.end();
+
+ application = builder.build();
+ appView.setAppInfo(
+ new AppInfo(
+ appView.appInfo().getSyntheticItems().commit(application),
+ appView.appInfo().getMainDexInfo()));
+
+ artProfileCollectionAdditions.commit(appView);
+
+ printCfg();
+ }
+
+ void convertMethods(
+ DexProgramClass clazz,
+ CfInstructionDesugaringEventConsumer desugaringEventConsumer,
+ D8MethodProcessor methodProcessor,
+ InterfaceProcessor interfaceProcessor) {
+ // When converting all methods on a class always convert <clinit> first.
+ ProgramMethod classInitializer = clazz.getProgramClassInitializer();
+
+ // TODO(b/179755192): We currently need to copy the class' methods, to avoid a
+ // ConcurrentModificationException from the insertion of methods due to invoke-special
+ // desugaring. By building up waves of methods in the class converter, we would not need to
+ // iterate the methods of a class during while its methods are being processed, which avoids
+ // the need to copy the method list.
+ List<ProgramMethod> methods = ListUtils.newArrayList(clazz::forEachProgramMethod);
+ if (classInitializer != null) {
+ methodProcessor.processMethod(classInitializer, desugaringEventConsumer);
+ }
+
+ for (ProgramMethod method : methods) {
+ if (!method.getDefinition().isClassInitializer()) {
+ methodProcessor.processMethod(method, desugaringEventConsumer);
+ if (interfaceProcessor != null) {
+ interfaceProcessor.processMethod(method, desugaringEventConsumer);
+ }
+ }
+ }
+
+ // The class file version is downgraded after compilation. Some of the desugaring might need
+ // the initial class file version to determine how far a method can be downgraded.
+ if (options.isGeneratingClassFiles() && clazz.hasClassFileVersion()) {
+ clazz.downgradeInitialClassFileVersion(
+ appView.options().classFileVersionAfterDesugaring(clazz.getInitialClassFileVersion()));
+ }
+ }
+
+ void convertMethod(
+ ProgramMethod method,
+ CfInstructionDesugaringEventConsumer desugaringEventConsumer,
+ MethodProcessor methodProcessor,
+ MethodProcessingContext methodProcessingContext) {
+ DexEncodedMethod definition = method.getDefinition();
+ if (options.isGeneratingClassFiles() && definition.hasClassFileVersion()) {
+ definition.downgradeClassFileVersion(
+ appView.options().classFileVersionAfterDesugaring(definition.getClassFileVersion()));
+ }
+ if (definition.getCode() == null) {
+ return;
+ }
+ if (!options.methodMatchesFilter(definition)) {
+ return;
+ }
+ checkPrefixMerging(method);
+ if (options.isGeneratingClassFiles()
+ || !(options.passthroughDexCode && definition.getCode().isDexCode())) {
+ // We do not process in call graph order, so anything could be a leaf.
+ rewriteNonDesugaredCode(
+ method,
+ desugaringEventConsumer,
+ simpleOptimizationFeedback,
+ methodProcessor,
+ methodProcessingContext);
+ } else {
+ assert definition.getCode().isDexCode();
+ }
+ if (!options.isGeneratingClassFiles()) {
+ updateHighestSortingStrings(definition);
+ }
+ }
+
+ private void checkPrefixMerging(ProgramMethod method) {
+ if (!appView.options().enableNeverMergePrefixes) {
+ return;
+ }
+ DexString descriptor = method.getHolderType().descriptor;
+ for (DexString neverMergePrefix : neverMerge.getPrefixes()) {
+ if (descriptor.startsWith(neverMergePrefix)) {
+ seenNeverMergePrefix.getAndSet(true);
+ } else {
+ for (DexString exceptionPrefix : neverMerge.getExceptionPrefixes()) {
+ if (!descriptor.startsWith(exceptionPrefix)) {
+ seenNotNeverMergePrefix.getAndSet(true);
+ break;
+ }
+ }
+ }
+ // Don't mix.
+ // TODO(b/168001352): Consider requiring that no 'never merge' prefix is ever seen as a
+ // passthrough object.
+ if (seenNeverMergePrefix.get() && seenNotNeverMergePrefix.get()) {
+ StringBuilder message = new StringBuilder();
+ message
+ .append("Merging DEX file containing classes with prefix")
+ .append(neverMerge.getPrefixes().size() > 1 ? "es " : " ");
+ for (int i = 0; i < neverMerge.getPrefixes().size(); i++) {
+ message
+ .append("'")
+ .append(neverMerge.getPrefixes().get(i).toString().substring(1).replace('/', '.'))
+ .append("'")
+ .append(i < neverMerge.getPrefixes().size() - 1 ? ", " : "");
+ }
+ if (!neverMerge.getExceptionPrefixes().isEmpty()) {
+ message
+ .append(" with other classes, except classes with prefix")
+ .append(neverMerge.getExceptionPrefixes().size() > 1 ? "es " : " ");
+ for (int i = 0; i < neverMerge.getExceptionPrefixes().size(); i++) {
+ message
+ .append("'")
+ .append(
+ neverMerge
+ .getExceptionPrefixes()
+ .get(i)
+ .toString()
+ .substring(1)
+ .replace('/', '.'))
+ .append("'")
+ .append(i < neverMerge.getExceptionPrefixes().size() - 1 ? ", " : "");
+ }
+ message.append(",");
+ } else {
+ message.append(" with classes with any other prefixes");
+ }
+ message.append(" is not allowed: ");
+ boolean first = true;
+ int limit = 11;
+ for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
+ if (!clazz.type.descriptor.startsWith(neverMergePrefix)) {
+ boolean hasExceptionPrefix = false;
+ for (DexString exceptionPrefix : neverMerge.getExceptionPrefixes()) {
+ hasExceptionPrefix =
+ hasExceptionPrefix | clazz.type.descriptor.startsWith(exceptionPrefix);
+ }
+ if (hasExceptionPrefix) {
+ continue;
+ }
+ if (limit-- < 0) {
+ message.append("..");
+ break;
+ }
+ if (first) {
+ first = false;
+ } else {
+ message.append(", ");
+ }
+ message.append(clazz.type);
+ }
+ }
+ message.append(".");
+ throw new CompilationError(message.toString());
+ }
+ }
+ }
+
+ void classSynthesisDesugaring(
+ ExecutorService executorService,
+ CfClassSynthesizerDesugaringEventConsumer classSynthesizerEventConsumer)
+ throws ExecutionException {
+ CfClassSynthesizerDesugaringCollection.create(appView)
+ .synthesizeClasses(executorService, classSynthesizerEventConsumer);
+ }
+
+ private DexApplication commitPendingSyntheticItems(
+ AppView<AppInfo> appView, DexApplication application) {
+ if (appView.getSyntheticItems().hasPendingSyntheticClasses()) {
+ appView.setAppInfo(
+ new AppInfo(
+ appView.appInfo().getSyntheticItems().commit(application),
+ appView.appInfo().getMainDexInfo()));
+ application = appView.appInfo().app();
+ }
+ return application;
+ }
+
+ private void convertClasses(
+ D8MethodProcessor methodProcessor,
+ InterfaceProcessor interfaceProcessor,
+ ExecutorService executorService)
+ throws ExecutionException {
+ ClassConverterResult classConverterResult =
+ ClassConverter.create(appView, this, methodProcessor, interfaceProcessor)
+ .convertClasses(executorService);
+
+ // The synthesis of accessibility bridges in nest based access desugaring will schedule and
+ // await the processing of synthesized methods.
+ synthesizeBridgesForNestBasedAccessesOnClasspath(methodProcessor, executorService);
+
+ // There should be no outstanding method processing.
+ methodProcessor.verifyNoPendingMethodProcessing();
+
+ rewriteEnclosingLambdaMethodAttributes(
+ appView, classConverterResult.getForcefullyMovedLambdaMethods());
+
+ instructionDesugaring.withDesugaredLibraryAPIConverter(
+ DesugaredLibraryAPIConverter::generateTrackingWarnings);
+ }
+
+ private void postProcessingDesugaringForD8(
+ D8MethodProcessor methodProcessor,
+ InterfaceProcessor interfaceProcessor,
+ ExecutorService executorService)
+ throws ExecutionException {
+ CfPostProcessingDesugaringEventConsumer eventConsumer =
+ CfPostProcessingDesugaringEventConsumer.createForD8(
+ methodProcessor.getArtProfileCollectionAdditions(),
+ methodProcessor,
+ instructionDesugaring);
+ methodProcessor.newWave();
+ InterfaceMethodProcessorFacade interfaceDesugaring =
+ instructionDesugaring.getInterfaceMethodPostProcessingDesugaringD8(
+ ExcludeDexResources, interfaceProcessor);
+ CfPostProcessingDesugaringCollection.create(appView, interfaceDesugaring, m -> true)
+ .postProcessingDesugaring(appView.appInfo().classes(), eventConsumer, executorService);
+ methodProcessor.awaitMethodProcessing();
+ eventConsumer.finalizeDesugaring();
+ }
+
+ void prepareDesugaring(
+ CfInstructionDesugaringEventConsumer desugaringEventConsumer, ExecutorService executorService)
+ throws ExecutionException {
+ // Prepare desugaring by collecting all the synthetic methods required on program classes.
+ ProgramAdditions programAdditions = new ProgramAdditions();
+ ThreadUtils.processItems(
+ appView.appInfo().classes(),
+ clazz -> {
+ clazz.forEachProgramMethodMatching(
+ method -> method.hasCode() && method.getCode().isCfCode(),
+ method ->
+ instructionDesugaring.prepare(method, desugaringEventConsumer, programAdditions));
+ },
+ executorService);
+ programAdditions.apply(executorService);
+ }
+
+ private void processCovariantReturnTypeAnnotations(Builder<?> builder) {
+ if (covariantReturnTypeAnnotationTransformer != null) {
+ covariantReturnTypeAnnotationTransformer.process(builder);
+ }
+ }
+
+ Timing rewriteNonDesugaredCode(
+ ProgramMethod method,
+ CfInstructionDesugaringEventConsumer desugaringEventConsumer,
+ OptimizationFeedback feedback,
+ MethodProcessor methodProcessor,
+ MethodProcessingContext methodProcessingContext) {
+ return ExceptionUtils.withOriginAndPositionAttachmentHandler(
+ method.getOrigin(),
+ new MethodPosition(method.getReference().asMethodReference()),
+ () ->
+ rewriteNonDesugaredCodeInternal(
+ method,
+ desugaringEventConsumer,
+ feedback,
+ methodProcessor,
+ methodProcessingContext));
+ }
+
+ private Timing rewriteNonDesugaredCodeInternal(
+ ProgramMethod method,
+ CfInstructionDesugaringEventConsumer desugaringEventConsumer,
+ OptimizationFeedback feedback,
+ MethodProcessor methodProcessor,
+ MethodProcessingContext methodProcessingContext) {
+ boolean didDesugar = desugar(method, desugaringEventConsumer, methodProcessingContext);
+ if (Log.ENABLED && didDesugar) {
+ Log.debug(
+ getClass(),
+ "Desugared code for %s:\n%s",
+ method.toSourceString(),
+ logCode(options, method.getDefinition()));
+ }
+ return rewriteDesugaredCodeInternal(method, feedback, methodProcessor, methodProcessingContext);
+ }
+
+ private boolean desugar(
+ ProgramMethod method,
+ CfInstructionDesugaringEventConsumer desugaringEventConsumer,
+ MethodProcessingContext methodProcessingContext) {
+ // Due to some mandatory desugarings, we need to run desugaring even if desugaring is disabled.
+ if (!method.getDefinition().getCode().isCfCode()) {
+ return false;
+ }
+ instructionDesugaring.scan(method, desugaringEventConsumer);
+ if (instructionDesugaring.needsDesugaring(method)) {
+ instructionDesugaring.desugar(method, methodProcessingContext, desugaringEventConsumer);
+ return true;
+ }
+ return false;
+ }
+
+ private void clearNestAttributes() {
+ instructionDesugaring.withD8NestBasedAccessDesugaring(
+ D8NestBasedAccessDesugaring::clearNestAttributes);
+ }
+
+ private void reportNestDesugarDependencies() {
+ instructionDesugaring.withD8NestBasedAccessDesugaring(
+ D8NestBasedAccessDesugaring::reportDesugarDependencies);
+ }
+
+ private void synthesizeBridgesForNestBasedAccessesOnClasspath(
+ D8MethodProcessor methodProcessor, ExecutorService executorService)
+ throws ExecutionException {
+ instructionDesugaring.withD8NestBasedAccessDesugaring(
+ d8NestBasedAccessDesugaring ->
+ d8NestBasedAccessDesugaring.synthesizeBridgesForNestBasedAccessesOnClasspath(
+ methodProcessor, executorService));
+ methodProcessor.awaitMethodProcessing();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
index c1ce6d2..805067a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryMethodProcessor.java
@@ -43,23 +43,29 @@
private final AppView<?> appView;
private final CallSiteInformation callSiteInformation;
+ private final MethodProcessorEventConsumer eventConsumer;
private final Deque<ProgramMethodSet> waves;
private ProcessorContext processorContext;
- private PrimaryMethodProcessor(AppView<AppInfoWithLiveness> appView, CallGraph callGraph) {
+ private PrimaryMethodProcessor(
+ AppView<AppInfoWithLiveness> appView,
+ CallGraph callGraph,
+ MethodProcessorEventConsumer eventConsumer) {
this.appView = appView;
this.callSiteInformation = callGraph.createCallSiteInformation(appView);
+ this.eventConsumer = eventConsumer;
this.waves = createWaves(appView, callGraph);
}
static PrimaryMethodProcessor create(
AppView<AppInfoWithLiveness> appView,
+ MethodProcessorEventConsumer eventConsumer,
ExecutorService executorService,
Timing timing)
throws ExecutionException {
CallGraph callGraph = CallGraph.builder(appView).build(executorService, timing);
- return new PrimaryMethodProcessor(appView, callGraph);
+ return new PrimaryMethodProcessor(appView, callGraph, eventConsumer);
}
@Override
@@ -68,6 +74,11 @@
}
@Override
+ public MethodProcessorEventConsumer getEventConsumer() {
+ return eventConsumer;
+ }
+
+ @Override
public boolean isPrimaryMethodProcessor() {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
new file mode 100644
index 0000000..aae02ab
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
@@ -0,0 +1,301 @@
+// Copyright (c) 2023, 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.conversion;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexApplication.Builder;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.GraphLens;
+import com.android.tools.r8.graph.PrunedItems;
+import com.android.tools.r8.ir.analysis.fieldaccess.TrivialFieldAccessReprocessor;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
+import com.android.tools.r8.logging.Log;
+import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.CfgPrinter;
+import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public class PrimaryR8IRConverter extends IRConverter {
+
+ public PrimaryR8IRConverter(AppView<? extends AppInfoWithClassHierarchy> appView, Timing timing) {
+ super(appView, timing, appView.options().printCfg ? new CfgPrinter() : null);
+ }
+
+ public void optimize(AppView<AppInfoWithLiveness> appView, ExecutorService executorService)
+ throws ExecutionException, IOException {
+ timing.begin("Create IR");
+ try {
+ DexApplication application =
+ internalOptimize(appView.withLiveness(), executorService).asDirect();
+ AppInfoWithClassHierarchy newAppInfo =
+ appView.appInfo().rebuildWithClassHierarchy(previous -> application);
+ appView.withClassHierarchy().setAppInfo(newAppInfo);
+ } finally {
+ timing.end();
+ }
+ printCfg();
+ }
+
+ private DexApplication internalOptimize(
+ AppView<AppInfoWithLiveness> appView, ExecutorService executorService)
+ throws ExecutionException {
+ // Desugaring happens in the enqueuer.
+ assert instructionDesugaring.isEmpty();
+
+ workaroundAbstractMethodOnNonAbstractClassVerificationBug(executorService);
+
+ // The process is in two phases in general.
+ // 1) Subject all DexEncodedMethods to optimization, except some optimizations that require
+ // reprocessing IR code of methods, e.g., outlining, double-inlining, class staticizer, etc.
+ // - a side effect is candidates for those optimizations are identified.
+ // 2) Revisit DexEncodedMethods for the collected candidates.
+
+ printPhase("Primary optimization pass");
+
+ GraphLens graphLensForPrimaryOptimizationPass = appView.graphLens();
+
+ // Setup optimizations for the primary optimization pass.
+ appView.withArgumentPropagator(
+ argumentPropagator -> argumentPropagator.initializeCodeScanner(executorService, timing));
+ enumUnboxer.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass);
+ outliner.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass);
+
+ if (fieldAccessAnalysis != null) {
+ fieldAccessAnalysis.fieldAssignmentTracker().initialize();
+ }
+
+ // Process the application identifying outlining candidates.
+ OptimizationFeedbackDelayed feedback = delayedOptimizationFeedback;
+ PostMethodProcessor.Builder postMethodProcessorBuilder =
+ new PostMethodProcessor.Builder(graphLensForPrimaryOptimizationPass);
+ {
+ timing.begin("Build primary method processor");
+ ArtProfileCollectionAdditions artProfileCollectionAdditions =
+ ArtProfileCollectionAdditions.create(appView);
+ MethodProcessorEventConsumer eventConsumer =
+ MethodProcessorEventConsumer.create(artProfileCollectionAdditions);
+ PrimaryMethodProcessor primaryMethodProcessor =
+ PrimaryMethodProcessor.create(
+ appView.withLiveness(), eventConsumer, executorService, timing);
+ timing.end();
+ timing.begin("IR conversion phase 1");
+ assert appView.graphLens() == graphLensForPrimaryOptimizationPass;
+ primaryMethodProcessor.forEachMethod(
+ (method, methodProcessingContext) ->
+ processDesugaredMethod(
+ method, feedback, primaryMethodProcessor, methodProcessingContext),
+ this::waveStart,
+ this::waveDone,
+ timing,
+ executorService);
+ lastWaveDone(postMethodProcessorBuilder, executorService);
+ timing.time("Commit profile additions", () -> artProfileCollectionAdditions.commit(appView));
+ assert appView.graphLens() == graphLensForPrimaryOptimizationPass;
+ timing.end();
+ }
+
+ // The field access info collection is not maintained during IR processing.
+ appView.appInfo().withLiveness().getFieldAccessInfoCollection().destroyAccessContexts();
+
+ // Assure that no more optimization feedback left after primary processing.
+ assert feedback.noUpdatesLeft();
+ appView.setAllCodeProcessed();
+
+ // All the code has been processed so the rewriting required by the lenses is done everywhere,
+ // we clear lens code rewriting so that the lens rewriter can be re-executed in phase 2 if new
+ // lenses with code rewriting are added.
+ appView.clearCodeRewritings();
+
+ // Commit synthetics from the primary optimization pass.
+ commitPendingSyntheticItems(appView);
+
+ // Post processing:
+ // 1) Second pass for methods whose collected call site information become more precise.
+ // 2) Second inlining pass for dealing with double inline callers.
+ printPhase("Post optimization pass");
+
+ // Analyze the data collected by the argument propagator, use the analysis result to update
+ // the parameter optimization infos, and rewrite the application.
+ // TODO(b/199237357): Automatically rewrite state when lens changes.
+ enumUnboxer.rewriteWithLens();
+ outliner.rewriteWithLens();
+ appView.withArgumentPropagator(
+ argumentPropagator ->
+ argumentPropagator.tearDownCodeScanner(
+ this, postMethodProcessorBuilder, executorService, timing));
+
+ if (libraryMethodOverrideAnalysis != null) {
+ libraryMethodOverrideAnalysis.finish();
+ }
+
+ if (!options.debug) {
+ new TrivialFieldAccessReprocessor(appView.withLiveness(), postMethodProcessorBuilder)
+ .run(executorService, feedback, timing);
+ }
+
+ outliner.rewriteWithLens();
+ enumUnboxer.unboxEnums(appView, this, postMethodProcessorBuilder, executorService, feedback);
+ appView.unboxedEnums().checkEnumsUnboxed(appView);
+
+ GraphLens graphLensForSecondaryOptimizationPass = appView.graphLens();
+
+ outliner.rewriteWithLens();
+
+ {
+ timing.begin("IR conversion phase 2");
+ ArtProfileCollectionAdditions artProfileCollectionAdditions =
+ ArtProfileCollectionAdditions.create(appView);
+ PostMethodProcessor postMethodProcessor =
+ timing.time(
+ "Build post method processor",
+ () -> {
+ MethodProcessorEventConsumer eventConsumer =
+ MethodProcessorEventConsumer.create(artProfileCollectionAdditions);
+ return postMethodProcessorBuilder.build(
+ appView, eventConsumer, executorService, timing);
+ });
+ if (postMethodProcessor != null) {
+ assert !options.debug;
+ assert appView.graphLens() == graphLensForSecondaryOptimizationPass;
+ timing.begin("Process code");
+ postMethodProcessor.forEachMethod(
+ (method, methodProcessingContext) ->
+ processDesugaredMethod(
+ method, feedback, postMethodProcessor, methodProcessingContext),
+ feedback,
+ executorService,
+ timing);
+ timing.end();
+ timing.time("Update visible optimization info", feedback::updateVisibleOptimizationInfo);
+ timing.time(
+ "Commit profile additions", () -> artProfileCollectionAdditions.commit(appView));
+ assert appView.graphLens() == graphLensForSecondaryOptimizationPass;
+ }
+ timing.end();
+ }
+
+ enumUnboxer.unsetRewriter();
+
+ // All the code that should be impacted by the lenses inserted between phase 1 and phase 2
+ // have now been processed and rewritten, we clear code lens rewriting so that the class
+ // staticizer and phase 3 does not perform again the rewriting.
+ appView.clearCodeRewritings();
+
+ // Commit synthetics before creating a builder (otherwise the builder will not include the
+ // synthetics.)
+ commitPendingSyntheticItems(appView);
+
+ // Build a new application with jumbo string info.
+ Builder<?> builder = appView.appInfo().app().builder();
+ builder.setHighestSortingString(highestSortingString);
+
+ if (serviceLoaderRewriter != null) {
+ processSimpleSynthesizeMethods(
+ serviceLoaderRewriter.getServiceLoadMethods(), executorService);
+ }
+
+ if (instanceInitializerOutliner != null) {
+ processSimpleSynthesizeMethods(
+ instanceInitializerOutliner.getSynthesizedMethods(), executorService);
+ }
+ if (assertionErrorTwoArgsConstructorRewriter != null) {
+ processSimpleSynthesizeMethods(
+ assertionErrorTwoArgsConstructorRewriter.getSynthesizedMethods(), executorService);
+ }
+
+ // Update optimization info for all synthesized methods at once.
+ feedback.updateVisibleOptimizationInfo();
+
+ // TODO(b/127694949): Adapt to PostOptimization.
+ outliner.performOutlining(this, feedback, executorService, timing);
+ clearDexMethodCompilationState();
+
+ if (identifierNameStringMarker != null) {
+ identifierNameStringMarker.decoupleIdentifierNameStringsInFields(executorService);
+ }
+
+ if (Log.ENABLED) {
+ if (idempotentFunctionCallCanonicalizer != null) {
+ idempotentFunctionCallCanonicalizer.logResults();
+ }
+ if (libraryMethodOverrideAnalysis != null) {
+ libraryMethodOverrideAnalysis.logResults();
+ }
+ if (stringOptimizer != null) {
+ stringOptimizer.logResult();
+ }
+ }
+
+ // Assure that no more optimization feedback left after post processing.
+ assert feedback.noUpdatesLeft();
+ return builder.build();
+ }
+
+ private void clearDexMethodCompilationState() {
+ appView.appInfo().classes().forEach(this::clearDexMethodCompilationState);
+ }
+
+ private void clearDexMethodCompilationState(DexProgramClass clazz) {
+ clazz.forEachMethod(DexEncodedMethod::markNotProcessed);
+ }
+
+ private static void commitPendingSyntheticItems(AppView<AppInfoWithLiveness> appView) {
+ if (appView.getSyntheticItems().hasPendingSyntheticClasses()) {
+ appView.setAppInfo(
+ appView
+ .appInfo()
+ .rebuildWithLiveness(appView.getSyntheticItems().commit(appView.appInfo().app())));
+ }
+ }
+
+ private void waveStart(ProgramMethodSet wave) {
+ onWaveDoneActions = Collections.synchronizedList(new ArrayList<>());
+ }
+
+ private void waveDone(ProgramMethodSet wave, ExecutorService executorService)
+ throws ExecutionException {
+ delayedOptimizationFeedback.refineAppInfoWithLiveness(appView.appInfo().withLiveness());
+ delayedOptimizationFeedback.updateVisibleOptimizationInfo();
+ fieldAccessAnalysis.fieldAssignmentTracker().waveDone(wave, delayedOptimizationFeedback);
+ appView.withArgumentPropagator(ArgumentPropagator::publishDelayedReprocessingCriteria);
+ if (appView.options().protoShrinking().enableRemoveProtoEnumSwitchMap()) {
+ appView.protoShrinker().protoEnumSwitchMapRemover.updateVisibleStaticFieldValues();
+ }
+ enumUnboxer.updateEnumUnboxingCandidatesInfo();
+ assert delayedOptimizationFeedback.noUpdatesLeft();
+ onWaveDoneActions.forEach(com.android.tools.r8.utils.Action::execute);
+ onWaveDoneActions = null;
+ if (!prunedMethodsInWave.isEmpty()) {
+ appView.pruneItems(
+ PrunedItems.builder()
+ .setRemovedMethods(prunedMethodsInWave)
+ .setPrunedApp(appView.appInfo().app())
+ .build(),
+ executorService);
+ prunedMethodsInWave.clear();
+ }
+ }
+
+ private void lastWaveDone(
+ PostMethodProcessor.Builder postMethodProcessorBuilder, ExecutorService executorService)
+ throws ExecutionException {
+ if (inliner != null) {
+ inliner.onLastWaveDone(postMethodProcessorBuilder, executorService, timing);
+ }
+
+ // Ensure determinism of method-to-reprocess set.
+ appView.testing().checkDeterminism(postMethodProcessorBuilder::dump);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
index dfaa50d..f2cdd81 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfClassSynthesizerDesugaringEventConsumer.java
@@ -9,56 +9,81 @@
import com.android.tools.r8.ir.desugar.desugaredlibrary.apiconversion.DesugaredLibraryWrapperSynthesizerEventConsumer.DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterL8SynthesizerEventConsumer;
import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.L8ProgramEmulatedInterfaceSynthesizerEventConsumer;
-import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer;
+import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordClassSynthesizerDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.varhandle.VarHandleDesugaringEventConsumer;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer;
import com.google.common.collect.Sets;
import java.util.Set;
-public class CfClassSynthesizerDesugaringEventConsumer
+public abstract class CfClassSynthesizerDesugaringEventConsumer
implements L8ProgramEmulatedInterfaceSynthesizerEventConsumer,
DesugaredLibraryL8ProgramWrapperSynthesizerEventConsumer,
DesugaredLibraryRetargeterL8SynthesizerEventConsumer,
- RecordDesugaringEventConsumer,
+ RecordClassSynthesizerDesugaringEventConsumer,
VarHandleDesugaringEventConsumer {
- private Set<DexProgramClass> synthesizedClasses = Sets.newConcurrentHashSet();
+ protected CfClassSynthesizerDesugaringEventConsumer() {}
- @Override
- public void acceptProgramEmulatedInterface(DexProgramClass clazz) {
- synthesizedClasses.add(clazz);
+ public static CfClassSynthesizerDesugaringEventConsumer create(
+ ArtProfileCollectionAdditions artProfileCollectionAdditions) {
+ CfClassSynthesizerDesugaringEventConsumer eventConsumer =
+ new D8R8CfClassSynthesizerDesugaringEventConsumer();
+ return ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.attach(
+ artProfileCollectionAdditions, eventConsumer);
}
- @Override
- public void acceptWrapperProgramClass(DexProgramClass clazz) {
- synthesizedClasses.add(clazz);
- }
+ public abstract Set<DexProgramClass> getSynthesizedClasses();
- @Override
- public void acceptEnumConversionProgramClass(DexProgramClass clazz) {
- synthesizedClasses.add(clazz);
- }
+ private static class D8R8CfClassSynthesizerDesugaringEventConsumer
+ extends CfClassSynthesizerDesugaringEventConsumer {
- @Override
- public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
- synthesizedClasses.add(clazz);
- }
+ private final Set<DexProgramClass> synthesizedClasses = Sets.newConcurrentHashSet();
- @Override
- public void acceptRecordClass(DexProgramClass clazz) {
- synthesizedClasses.add(clazz);
- }
+ @Override
+ public void acceptProgramEmulatedInterface(DexProgramClass clazz) {
+ synthesizedClasses.add(clazz);
+ }
- @Override
- public void acceptVarHandleDesugaringClass(DexProgramClass clazz) {
- synthesizedClasses.add(clazz);
- }
+ @Override
+ public void acceptWrapperProgramClass(DexProgramClass clazz) {
+ synthesizedClasses.add(clazz);
+ }
- public Set<DexProgramClass> getSynthesizedClasses() {
- return synthesizedClasses;
- }
+ @Override
+ public void acceptEnumConversionProgramClass(DexProgramClass clazz) {
+ synthesizedClasses.add(clazz);
+ }
- @Override
- public void acceptCollectionConversion(ProgramMethod arrayConversion) {
- synthesizedClasses.add(arrayConversion.getHolder());
+ @Override
+ public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
+ synthesizedClasses.add(clazz);
+ }
+
+ @Override
+ public void acceptRecordClass(DexProgramClass recordTagClass) {
+ synthesizedClasses.add(recordTagClass);
+ }
+
+ @Override
+ public void acceptRecordClassContext(
+ DexProgramClass recordTagClass, DexProgramClass recordClass) {
+ // Intentionally empty.
+ }
+
+ @Override
+ public void acceptVarHandleDesugaringClass(DexProgramClass clazz) {
+ synthesizedClasses.add(clazz);
+ }
+
+ @Override
+ public Set<DexProgramClass> getSynthesizedClasses() {
+ return synthesizedClasses;
+ }
+
+ @Override
+ public void acceptCollectionConversion(ProgramMethod arrayConversion) {
+ synthesizedClasses.add(arrayConversion.getHolder());
+ }
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index d852270..595d1db 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -75,7 +75,7 @@
new D8CfInstructionDesugaringEventConsumer(
appView, classConverterResultBuilder, methodProcessor);
return ArtProfileRewritingCfInstructionDesugaringEventConsumer.attach(
- artProfileCollectionAdditions, eventConsumer);
+ appView, artProfileCollectionAdditions, eventConsumer);
}
public static CfInstructionDesugaringEventConsumer createForR8(
@@ -95,7 +95,7 @@
additions,
companionMethodConsumer);
return ArtProfileRewritingCfInstructionDesugaringEventConsumer.attach(
- artProfileCollectionAdditions, eventConsumer);
+ appView, artProfileCollectionAdditions, eventConsumer);
}
public abstract List<ProgramMethod> finalizeDesugaring();
@@ -173,7 +173,23 @@
}
@Override
- public void acceptRecordMethod(ProgramMethod method) {
+ public void acceptRecordEqualsHelperMethod(ProgramMethod method, ProgramMethod context) {
+ // Intentionally empty. Added to the program using ProgramAdditions.
+ }
+
+ @Override
+ public void acceptRecordGetFieldsAsObjectsHelperMethod(
+ ProgramMethod method, ProgramMethod context) {
+ // Intentionally empty. Added to the program using ProgramAdditions.
+ }
+
+ @Override
+ public void acceptRecordHashCodeHelperMethod(ProgramMethod method, ProgramMethod context) {
+ methodProcessor.scheduleDesugaredMethodForProcessing(method);
+ }
+
+ @Override
+ public void acceptRecordToStringHelperMethod(ProgramMethod method, ProgramMethod context) {
methodProcessor.scheduleDesugaredMethodForProcessing(method);
}
@@ -191,6 +207,11 @@
}
@Override
+ public void acceptRecordClassContext(DexProgramClass recordTagClass, ProgramMethod context) {
+ // Intentionally empty.
+ }
+
+ @Override
public void acceptVarHandleDesugaringClass(DexProgramClass clazz) {
clazz
.programMethods()
@@ -245,7 +266,41 @@
}
@Override
- public void acceptThrowMethod(ProgramMethod method, ProgramMethod context) {
+ public void acceptUtilityToStringIfNotNullMethod(ProgramMethod method, ProgramMethod context) {
+ acceptUtilityMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowClassCastExceptionIfNotNullMethod(
+ ProgramMethod method, ProgramMethod context) {
+ acceptUtilityMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowIllegalAccessErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ acceptUtilityMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowIncompatibleClassChangeErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ acceptUtilityMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowNoSuchMethodErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ acceptUtilityMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowRuntimeExceptionWithMessageMethod(
+ ProgramMethod method, ProgramMethod context) {
+ acceptUtilityMethod(method, context);
+ }
+
+ private void acceptUtilityMethod(ProgramMethod method, ProgramMethod context) {
methodProcessor.scheduleDesugaredMethodForProcessing(method);
}
@@ -423,6 +478,11 @@
}
@Override
+ public void acceptRecordClassContext(DexProgramClass recordTagClass, ProgramMethod context) {
+ // Intentionally empty.
+ }
+
+ @Override
public void acceptVarHandleDesugaringClass(DexProgramClass clazz) {
// Intentionally empty. The class will be hit by tracing if required.
}
@@ -433,7 +493,23 @@
}
@Override
- public void acceptRecordMethod(ProgramMethod method) {
+ public void acceptRecordEqualsHelperMethod(ProgramMethod method, ProgramMethod context) {
+ // Intentionally empty. The method will be hit by tracing if required.
+ }
+
+ @Override
+ public void acceptRecordGetFieldsAsObjectsHelperMethod(
+ ProgramMethod method, ProgramMethod context) {
+ // Intentionally empty. The method will be hit by tracing if required.
+ }
+
+ @Override
+ public void acceptRecordHashCodeHelperMethod(ProgramMethod method, ProgramMethod context) {
+ // Intentionally empty. The method will be hit by tracing if required.
+ }
+
+ @Override
+ public void acceptRecordToStringHelperMethod(ProgramMethod method, ProgramMethod context) {
// Intentionally empty. The method will be hit by tracing if required.
}
@@ -443,7 +519,41 @@
}
@Override
- public void acceptThrowMethod(ProgramMethod method, ProgramMethod context) {
+ public void acceptUtilityToStringIfNotNullMethod(ProgramMethod method, ProgramMethod context) {
+ acceptUtilityMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowClassCastExceptionIfNotNullMethod(
+ ProgramMethod method, ProgramMethod context) {
+ acceptUtilityMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowIllegalAccessErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ acceptUtilityMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowIncompatibleClassChangeErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ acceptUtilityMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowNoSuchMethodErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ acceptUtilityMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowRuntimeExceptionWithMessageMethod(
+ ProgramMethod method, ProgramMethod context) {
+ acceptUtilityMethod(method, context);
+ }
+
+ private void acceptUtilityMethod(ProgramMethod method, ProgramMethod context) {
// Intentionally empty. The method will be hit by tracing if required.
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
index 8770cd1..e3f1f89 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringCollection.java
@@ -21,10 +21,12 @@
public abstract class CfPostProcessingDesugaringCollection {
public static CfPostProcessingDesugaringCollection create(
- AppView<?> appView, InterfaceMethodProcessorFacade interfaceMethodProcessorFacade) {
+ AppView<?> appView,
+ InterfaceMethodProcessorFacade interfaceMethodProcessorFacade,
+ Predicate<ProgramMethod> isLiveMethod) {
if (appView.options().desugarState.isOn()) {
return NonEmptyCfPostProcessingDesugaringCollection.create(
- appView, interfaceMethodProcessorFacade);
+ appView, interfaceMethodProcessorFacade, isLiveMethod);
}
return empty();
}
@@ -35,7 +37,6 @@
public abstract void postProcessingDesugaring(
Collection<DexProgramClass> programClasses,
- Predicate<ProgramMethod> isLiveMethod,
CfPostProcessingDesugaringEventConsumer eventConsumer,
ExecutorService executorService)
throws ExecutionException;
@@ -51,7 +52,9 @@
}
public static CfPostProcessingDesugaringCollection create(
- AppView<?> appView, InterfaceMethodProcessorFacade interfaceMethodProcessorFacade) {
+ AppView<?> appView,
+ InterfaceMethodProcessorFacade interfaceMethodProcessorFacade,
+ Predicate<ProgramMethod> isLiveMethod) {
ArrayList<CfPostProcessingDesugaring> desugarings = new ArrayList<>();
if (appView.options().machineDesugaredLibrarySpecification.hasRetargeting()
&& !appView.options().isDesugaredLibraryCompilation()) {
@@ -62,7 +65,7 @@
}
DesugaredLibraryAPICallbackSynthesizer apiCallbackSynthesizor =
appView.typeRewriter.isRewriting()
- ? new DesugaredLibraryAPICallbackSynthesizer(appView)
+ ? new DesugaredLibraryAPICallbackSynthesizer(appView, isLiveMethod)
: null;
// At this point the desugaredLibraryAPIConverter is required to be last to generate
// call-backs on the forwarding methods.
@@ -87,7 +90,6 @@
@Override
public void postProcessingDesugaring(
Collection<DexProgramClass> programClasses,
- Predicate<ProgramMethod> isLiveMethod,
CfPostProcessingDesugaringEventConsumer eventConsumer,
ExecutorService executorService)
throws ExecutionException {
@@ -112,7 +114,6 @@
@Override
public void postProcessingDesugaring(
Collection<DexProgramClass> programClasses,
- Predicate<ProgramMethod> isLiveMethod,
CfPostProcessingDesugaringEventConsumer eventConsumer,
ExecutorService executorService)
throws ExecutionException {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
index 23d3e1a..29cb618 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfPostProcessingDesugaringEventConsumer.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
@@ -18,6 +19,8 @@
import com.android.tools.r8.profile.art.rewriting.ArtProfileRewritingCfPostProcessingDesugaringEventConsumer;
import com.android.tools.r8.shaking.Enqueuer.SyntheticAdditions;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import java.util.Collections;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.function.BiConsumer;
@@ -52,6 +55,8 @@
artProfileCollectionAdditions, eventConsumer);
}
+ public abstract Set<DexMethod> getNewlyLiveMethods();
+
public abstract void finalizeDesugaring() throws ExecutionException;
public static class D8CfPostProcessingDesugaringEventConsumer
@@ -125,6 +130,11 @@
}
@Override
+ public Set<DexMethod> getNewlyLiveMethods() {
+ return Collections.emptySet();
+ }
+
+ @Override
public void finalizeDesugaring() throws ExecutionException {
assert methodProcessor.verifyNoPendingMethodProcessing();
methodProcessor.newWave();
@@ -176,6 +186,13 @@
}
@Override
+ public Set<DexMethod> getNewlyLiveMethods() {
+ // Note: this answers the newly live methods up until the point where this is called.
+ // This has to be called in between post processing to be deterministic.
+ return additions.getNewlyLiveMethods();
+ }
+
+ @Override
public void finalizeDesugaring() {
// Intentionally empty.
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
index 5d774b2..1143afd 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CovariantReturnTypeAnnotationTransformer.java
@@ -23,6 +23,7 @@
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
import com.google.common.base.Predicates;
import java.util.HashSet;
@@ -51,7 +52,9 @@
// several CovariantReturnType annotations. In this case, a new method is synthesized for each of
// the contained CovariantReturnType annotations.
public final class CovariantReturnTypeAnnotationTransformer {
+
private final IRConverter converter;
+ private final MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty();
private final DexItemFactory factory;
public CovariantReturnTypeAnnotationTransformer(IRConverter converter, DexItemFactory factory) {
@@ -180,7 +183,7 @@
.build();
// Optimize to generate DexCode instead of CfCode.
ProgramMethod programMethod = new ProgramMethod(methodHolder, newVirtualMethod);
- converter.optimizeSynthesizedMethod(programMethod);
+ converter.optimizeSynthesizedMethod(programMethod, eventConsumer);
return newVirtualMethod;
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index 9d6e3df..f9d4355 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -576,7 +576,11 @@
return callTarget;
}
- boolean isInterface() {
+ public Type getInvokeType() {
+ return invokeType;
+ }
+
+ public boolean isInterface() {
return isInterface;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/UnrepresentableInDexInstructionRemover.java b/src/main/java/com/android/tools/r8/ir/desugar/UnrepresentableInDexInstructionRemover.java
index 24cd2b5..1b07110 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/UnrepresentableInDexInstructionRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/UnrepresentableInDexInstructionRemover.java
@@ -87,12 +87,11 @@
void invokeThrowingStub(
MethodProcessingContext methodProcessingContext,
CfInstructionDesugaringEventConsumer eventConsumer,
- ProgramMethod context,
ImmutableList.Builder<CfInstruction> builder) {
UtilityMethodForCodeOptimizations throwUtility =
- synthesizeThrowRuntimeExceptionWithMessageMethod(appView, methodProcessingContext);
+ synthesizeThrowRuntimeExceptionWithMessageMethod(
+ appView, eventConsumer, methodProcessingContext);
ProgramMethod throwMethod = throwUtility.uncheckedGetMethod();
- eventConsumer.acceptThrowMethod(throwMethod, context);
builder.add(
createMessageString(),
new CfInvoke(Opcodes.INVOKESTATIC, throwMethod.getReference(), false),
@@ -173,7 +172,7 @@
DexCallSite callSite = invokeDynamic.getCallSite();
pop(callSite.getMethodProto(), replacement);
localStackAllocator.allocateLocalStack(1);
- invokeThrowingStub(methodProcessingContext, eventConsumer, context, replacement);
+ invokeThrowingStub(methodProcessingContext, eventConsumer, replacement);
pushReturnValue(callSite.getMethodProto().getReturnType(), replacement);
return replacement.build();
})
@@ -224,7 +223,7 @@
pop(dexItemFactory.objectType, replacement);
}
localStackAllocator.allocateLocalStack(1);
- invokeThrowingStub(methodProcessingContext, eventConsumer, context, replacement);
+ invokeThrowingStub(methodProcessingContext, eventConsumer, replacement);
pushReturnValue(invoke.getMethod().getReturnType(), replacement);
return replacement.build();
})
@@ -264,7 +263,7 @@
dexItemFactory) -> {
report(context);
Builder<CfInstruction> replacement = ImmutableList.builder();
- invokeThrowingStub(methodProcessingContext, eventConsumer, context, replacement);
+ invokeThrowingStub(methodProcessingContext, eventConsumer, replacement);
return replacement.add(new CfConstNull()).build();
})
.build();
@@ -303,7 +302,7 @@
dexItemFactory) -> {
report(context);
Builder<CfInstruction> replacement = ImmutableList.builder();
- invokeThrowingStub(methodProcessingContext, eventConsumer, context, replacement);
+ invokeThrowingStub(methodProcessingContext, eventConsumer, replacement);
return replacement.add(new CfConstNull()).build();
})
.build();
@@ -343,7 +342,7 @@
dexItemFactory) -> {
report(context);
Builder<CfInstruction> replacement = ImmutableList.builder();
- invokeThrowingStub(methodProcessingContext, eventConsumer, context, replacement);
+ invokeThrowingStub(methodProcessingContext, eventConsumer, replacement);
return pushReturnValue(constDynamic.getType(), replacement).build();
})
.build();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
index a4b79c0..5105707 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
@@ -76,8 +76,6 @@
public static final String CONST_FIELD_NAME = "CONST";
private final AppView<?> appView;
- private final ConstantDynamicInstructionDesugaring desugaring;
- private final ProgramMethod context;
public final ConstantDynamicReference reference;
public final DexField initializedValueField;
public final DexField constantValueField;
@@ -93,13 +91,10 @@
public ConstantDynamicClass(
SyntheticProgramClassBuilder builder,
AppView<?> appView,
- ConstantDynamicInstructionDesugaring desugaring,
ProgramMethod context,
CfConstDynamic constantDynamic) {
DexItemFactory factory = appView.dexItemFactory();
this.appView = appView;
- this.desugaring = desugaring;
- this.context = context;
this.reference = constantDynamic.getReference();
this.constantValueField =
factory.createField(
@@ -190,19 +185,16 @@
? UtilityMethodsForCodeOptimizations::synthesizeThrowNoSuchMethodErrorMethod
: UtilityMethodsForCodeOptimizations::synthesizeThrowIncompatibleClassChangeErrorMethod,
eventConsumer,
- context,
methodProcessingContext);
}
private Collection<CfInstruction> desugarToThrow(
MethodSynthesizerConsumer methodSynthesizerConsumer,
ConstantDynamicDesugaringEventConsumer eventConsumer,
- ProgramMethod context,
MethodProcessingContext methodProcessingContext) {
UtilityMethodForCodeOptimizations throwMethod =
- methodSynthesizerConsumer.synthesizeMethod(appView, methodProcessingContext);
+ methodSynthesizerConsumer.synthesizeMethod(appView, eventConsumer, methodProcessingContext);
ProgramMethod throwProgramMethod = throwMethod.uncheckedGetMethod();
- eventConsumer.acceptThrowMethod(throwProgramMethod, context);
return ImmutableList.of(new CfInvoke(INVOKESTATIC, throwProgramMethod.getReference(), false));
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicDesugaringEventConsumer.java
index 1d2dcf6..052b8b9 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicDesugaringEventConsumer.java
@@ -4,10 +4,10 @@
package com.android.tools.r8.ir.desugar.constantdynamic;
import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.UtilityMethodsForCodeOptimizationsEventConsumer;
-public interface ConstantDynamicDesugaringEventConsumer {
+public interface ConstantDynamicDesugaringEventConsumer
+ extends UtilityMethodsForCodeOptimizationsEventConsumer {
void acceptConstantDynamicClass(ConstantDynamicClass lambdaClass, ProgramMethod context);
-
- void acceptThrowMethod(ProgramMethod method, ProgramMethod context);
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java
index b9826d5..67f3e0b 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicInstructionDesugaring.java
@@ -203,9 +203,7 @@
methodProcessingContext.createUniqueContext(),
appView,
builder ->
- box.set(
- new ConstantDynamicClass(
- builder, appView, this, context, constantDynamic)));
+ box.set(new ConstantDynamicClass(builder, appView, context, constantDynamic)));
// Immediately set the actual program class on the constant dynamic.
ConstantDynamicClass constantDynamicClass = box.get();
constantDynamicClass.setClass(clazz);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
index c1e11d4..8f6ad62 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/apiconversion/DesugaredLibraryAPICallbackSynthesizer.java
@@ -26,6 +26,7 @@
import java.util.Collection;
import java.util.Set;
import java.util.concurrent.ExecutorService;
+import java.util.function.Predicate;
public class DesugaredLibraryAPICallbackSynthesizer implements CfPostProcessingDesugaring {
@@ -35,9 +36,13 @@
private final DesugaredLibraryWrapperSynthesizer wrapperSynthesizor;
private final Set<DexMethod> trackedCallBackAPIs;
- public DesugaredLibraryAPICallbackSynthesizer(AppView<?> appView) {
+ private final Predicate<ProgramMethod> isLiveMethod;
+
+ public DesugaredLibraryAPICallbackSynthesizer(
+ AppView<?> appView, Predicate<ProgramMethod> isLiveMethod) {
this.appView = appView;
this.factory = appView.dexItemFactory();
+ this.isLiveMethod = isLiveMethod;
this.wrapperSynthesizor = new DesugaredLibraryWrapperSynthesizer(appView);
if (appView.options().testing.trackDesugaredAPIConversions) {
trackedCallBackAPIs = Sets.newConcurrentHashSet();
@@ -54,6 +59,7 @@
ExecutorService executorService) {
ProcessorContext processorContext = appView.createProcessorContext();
MainThreadContext mainThreadContext = processorContext.createMainThreadContext();
+ Set<DexMethod> newlyLiveMethods = eventConsumer.getNewlyLiveMethods();
assert noPendingWrappersOrConversions();
for (DexProgramClass clazz : programClasses) {
if (!appView.isAlreadyLibraryDesugared(clazz)) {
@@ -62,6 +68,10 @@
// always be live in R8.
for (ProgramMethod virtualProgramMethod : clazz.virtualProgramMethods()) {
if (shouldRegisterCallback(virtualProgramMethod)) {
+ if (!isLiveMethod(virtualProgramMethod, newlyLiveMethods)) {
+ // This happens for live non instantiated types, library overrides are not live there.
+ continue;
+ }
if (trackedCallBackAPIs != null) {
trackedCallBackAPIs.add(virtualProgramMethod.getReference());
}
@@ -82,6 +92,12 @@
generateTrackingWarnings();
}
+ private boolean isLiveMethod(
+ ProgramMethod virtualProgramMethod, Set<DexMethod> newlyLiveMethods) {
+ return isLiveMethod.test(virtualProgramMethod)
+ || newlyLiveMethods.contains(virtualProgramMethod.getReference());
+ }
+
private boolean noPendingWrappersOrConversions() {
for (DexProgramClass pendingSyntheticClass :
appView.getSyntheticItems().getPendingSyntheticClasses()) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java
new file mode 100644
index 0000000..c76c17f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/AbstractGenerateFiles.java
@@ -0,0 +1,121 @@
+// Copyright (c) 2023, 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.desugar.desugaredlibrary.lint;
+
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.ImmutableList;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.concurrent.ExecutorService;
+
+public abstract class AbstractGenerateFiles {
+
+ private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
+
+ // If we increment this api level, we need to verify everything works correctly.
+ static final AndroidApiLevel MAX_TESTED_ANDROID_API_LEVEL = AndroidApiLevel.T;
+
+ private final DexItemFactory factory = new DexItemFactory();
+ private final Reporter reporter = new Reporter();
+ final InternalOptions options = new InternalOptions(factory, reporter);
+
+ final MachineDesugaredLibrarySpecification desugaredLibrarySpecification;
+ final Path desugaredLibrarySpecificationPath;
+ final Collection<Path> desugaredLibraryImplementation;
+ final Path outputDirectory;
+
+ public AbstractGenerateFiles(
+ String desugarConfigurationPath, String desugarImplementationPath, String outputDirectory)
+ throws Exception {
+ this(
+ Paths.get(desugarConfigurationPath),
+ ImmutableList.of(Paths.get(desugarImplementationPath)),
+ Paths.get(outputDirectory));
+ }
+
+ AbstractGenerateFiles(
+ Path desugarConfigurationPath,
+ Collection<Path> desugarImplementationPath,
+ Path outputDirectory)
+ throws Exception {
+ this.desugaredLibrarySpecificationPath = desugarConfigurationPath;
+ DesugaredLibrarySpecification specification =
+ readDesugaredLibraryConfiguration(desugarConfigurationPath);
+ Path androidJarPath = getAndroidJarPath(specification.getRequiredCompilationApiLevel());
+ DexApplication app = createApp(androidJarPath, options);
+ this.desugaredLibrarySpecification = specification.toMachineSpecification(app, Timing.empty());
+ this.desugaredLibraryImplementation = desugarImplementationPath;
+ this.outputDirectory = outputDirectory;
+ if (!Files.isDirectory(this.outputDirectory)) {
+ throw new Exception("Output directory " + outputDirectory + " is not a directory");
+ }
+ }
+
+ static Path getAndroidJarPath(AndroidApiLevel apiLevel) {
+ String jar =
+ apiLevel == AndroidApiLevel.MASTER
+ ? "third_party/android_jar/lib-master/android.jar"
+ : String.format(ANDROID_JAR_PATTERN, apiLevel.getLevel());
+ return Paths.get(jar);
+ }
+
+
+ private DesugaredLibrarySpecification readDesugaredLibraryConfiguration(
+ Path desugarConfigurationPath) {
+ return DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification(
+ StringResource.fromFile(desugarConfigurationPath),
+ factory,
+ reporter,
+ false,
+ AndroidApiLevel.B.getLevel());
+ }
+
+ private static DexApplication createApp(Path androidLib, InternalOptions options)
+ throws IOException {
+ AndroidApp.Builder builder = AndroidApp.builder();
+ AndroidApp inputApp = builder.addLibraryFiles(androidLib).build();
+ ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
+ ExecutorService executorService = ThreadUtils.getExecutorService(options);
+ assert !options.ignoreJavaLibraryOverride;
+ options.ignoreJavaLibraryOverride = true;
+ DexApplication app = applicationReader.read(executorService);
+ options.ignoreJavaLibraryOverride = false;
+ return app;
+ }
+
+ abstract AndroidApiLevel run() throws Exception;
+
+ public static void main(String[] args) throws Exception {
+ if (args.length == 3) {
+ new GenerateLintFiles(args[0], args[1], args[2]).run();
+ return;
+ }
+ if (args.length == 4 && args[0].equals("--generate-api-docs")) {
+ new GenerateHtmlDoc(args[1], args[2], args[3]).run();
+ return;
+ }
+ throw new RuntimeException(
+ StringUtils.joinLines(
+ "Invalid invocation.",
+ "Usage: GenerateLineFiles [--generate-api-docs] "
+ + "<desugar configuration> <desugar implementation> <output directory>"));
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java
new file mode 100644
index 0000000..859bc95
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateHtmlDoc.java
@@ -0,0 +1,548 @@
+// Copyright (c) 2023, 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.desugar.desugaredlibrary.lint;
+
+import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
+import com.android.tools.r8.graph.ClassAccessFlags;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexProto;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.FieldAccessFlags;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.ClassAnnotation;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.MethodAnnotation;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.SupportedClass;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.io.PrintStream;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.stream.StreamSupport;
+
+public class GenerateHtmlDoc extends AbstractGenerateFiles {
+
+ private static final String HTML_SPLIT = "<br> ";
+ private static final int MAX_LINE_CHARACTERS = 53;
+ private static final String SUP_1 = "<sup>1</sup>";
+ private static final String SUP_2 = "<sup>2</sup>";
+ private static final String SUP_3 = "<sup>3</sup>";
+ private static final String SUP_4 = "<sup>4</sup>";
+
+ public GenerateHtmlDoc(
+ String desugarConfigurationPath, String desugarImplementationPath, String outputDirectory)
+ throws Exception {
+ super(desugarConfigurationPath, desugarImplementationPath, outputDirectory);
+ }
+
+ private static class StringBuilderWithIndent {
+
+ String NL = System.lineSeparator();
+ StringBuilder builder = new StringBuilder();
+ String indent = "";
+
+ StringBuilderWithIndent() {}
+
+ StringBuilderWithIndent indent(String indent) {
+ this.indent = indent;
+ return this;
+ }
+
+ StringBuilderWithIndent appendLineStart(String lineStart) {
+ builder.append(indent);
+ builder.append(lineStart);
+ return this;
+ }
+
+ StringBuilderWithIndent append(String string) {
+ builder.append(string);
+ return this;
+ }
+
+ StringBuilderWithIndent appendLineEnd(String lineEnd) {
+ builder.append(lineEnd);
+ builder.append(NL);
+ return this;
+ }
+
+ StringBuilderWithIndent appendLine(String line) {
+ builder.append(indent);
+ builder.append(line);
+ builder.append(NL);
+ return this;
+ }
+
+ StringBuilderWithIndent emptyLine() {
+ builder.append(NL);
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return builder.toString();
+ }
+ }
+
+ private abstract static class SourceBuilder<B extends GenerateHtmlDoc.SourceBuilder> {
+
+ protected final DexClass clazz;
+ protected List<DexEncodedField> fields = new ArrayList<>();
+ protected Map<DexEncodedMethod, MethodAnnotation> constructors =
+ new TreeMap<>(Comparator.comparing(DexEncodedMethod::getReference));
+ protected Map<DexEncodedMethod, MethodAnnotation> methods =
+ new TreeMap<>(Comparator.comparing(DexEncodedMethod::getReference));
+
+ String className;
+ String packageName;
+
+ private SourceBuilder(DexClass clazz) {
+ this.clazz = clazz;
+ this.className = clazz.type.toSourceString();
+ int index = this.className.lastIndexOf('.');
+ this.packageName = index > 0 ? this.className.substring(0, index) : "";
+ }
+
+ public abstract B self();
+
+ private B addField(DexEncodedField field) {
+ fields.add(field);
+ return self();
+ }
+
+ private B addMethod(DexEncodedMethod method, MethodAnnotation methodAnnotation) {
+ assert !method.isClassInitializer();
+ if (method.isInitializer()) {
+ constructors.put(method, methodAnnotation);
+ } else {
+ methods.put(method, methodAnnotation);
+ }
+ return self();
+ }
+
+ // If we are in a.b.c, then anything starting with a.b should not be fully qualified.
+ protected String typeInPackageRecursive(String typeName, String packageName) {
+ String rewritten = typeInPackage(typeName, packageName);
+ if (rewritten != null) {
+ return rewritten;
+ }
+ String[] split = packageName.split("\\.");
+ if (split.length > 2) {
+ String prevPackage =
+ packageName.substring(0, packageName.length() - split[split.length - 1].length() - 1);
+ return typeInPackage(typeName, prevPackage);
+ }
+ return null;
+ }
+
+ protected String typeInPackage(String typeName, String packageName) {
+ if (typeName.startsWith(packageName)
+ && typeName.length() > packageName.length()
+ && typeName.charAt(packageName.length()) == '.') {
+ int last = typeName.lastIndexOf('.') + 1;
+ return typeName.substring(last);
+ }
+ return null;
+ }
+
+ protected String typeInPackage(String typeName) {
+ String result = typeInPackageRecursive(typeName, packageName);
+ if (result == null) {
+ result = typeInPackage(typeName, "java.lang");
+ }
+ if (result == null) {
+ result = typeInPackage(typeName, "java.util.function");
+ }
+ if (result == null) {
+ result = typeName;
+ }
+ return result.replace('$', '.');
+ }
+
+ protected String typeInPackage(DexType type) {
+ if (type.isPrimitiveType()) {
+ return type.toSourceString();
+ }
+ return typeInPackage(type.toSourceString());
+ }
+
+ protected String accessFlags(ClassAccessFlags accessFlags) {
+ List<String> flags = new ArrayList<>();
+ if (accessFlags.isPublic()) {
+ flags.add("public");
+ }
+ if (accessFlags.isProtected()) {
+ flags.add("protected");
+ }
+ if (accessFlags.isPrivate()) {
+ assert false;
+ flags.add("private");
+ }
+ if (accessFlags.isPackagePrivate()) {
+ assert false;
+ flags.add("/* package */");
+ }
+ if (accessFlags.isAbstract() && !accessFlags.isInterface()) {
+ flags.add("abstract");
+ }
+ if (accessFlags.isStatic()) {
+ flags.add("static");
+ }
+ if (accessFlags.isFinal()) {
+ flags.add("final");
+ }
+ return String.join(" ", flags);
+ }
+
+ protected String accessFlags(FieldAccessFlags accessFlags) {
+ List<String> flags = new ArrayList<>();
+ if (accessFlags.isPublic()) {
+ flags.add("public");
+ }
+ if (accessFlags.isProtected()) {
+ flags.add("protected");
+ }
+ if (accessFlags.isPrivate()) {
+ assert false;
+ flags.add("private");
+ }
+ if (accessFlags.isPackagePrivate()) {
+ assert false;
+ flags.add("/* package */");
+ }
+ if (accessFlags.isStatic()) {
+ flags.add("static");
+ }
+ if (accessFlags.isFinal()) {
+ flags.add("final");
+ }
+ return String.join(" ", flags);
+ }
+
+ protected String accessFlags(MethodAccessFlags accessFlags) {
+ List<String> flags = new ArrayList<>();
+ if (accessFlags.isPublic()) {
+ flags.add("public");
+ }
+ if (accessFlags.isProtected()) {
+ flags.add("protected");
+ }
+ if (accessFlags.isPrivate()) {
+ assert false;
+ flags.add("private");
+ }
+ if (accessFlags.isPackagePrivate()) {
+ assert false;
+ flags.add("/* package */");
+ }
+ if (accessFlags.isAbstract()) {
+ flags.add("abstract");
+ }
+ if (accessFlags.isStatic()) {
+ flags.add("static");
+ }
+ if (accessFlags.isFinal()) {
+ flags.add("final");
+ }
+ return String.join(" ", flags);
+ }
+
+ public String arguments(DexEncodedMethod method) {
+ DexProto proto = method.getReference().proto;
+ StringBuilder argsBuilder = new StringBuilder();
+ boolean firstArg = true;
+ int argIndex = method.isVirtualMethod() || method.accessFlags.isConstructor() ? 1 : 0;
+ int argNumber = 0;
+ argsBuilder.append("(");
+ for (DexType type : proto.parameters.values) {
+ if (!firstArg) {
+ argsBuilder.append(", ");
+ }
+ if (method.hasCode()) {
+ String name = "p" + argNumber;
+ for (LocalVariableInfo localVariable : method.getCode().asCfCode().getLocalVariables()) {
+ if (localVariable.getIndex() == argIndex) {
+ assert !localVariable.getLocal().name.toString().equals("this");
+ name = localVariable.getLocal().name.toString();
+ }
+ }
+ argsBuilder.append(typeInPackage(type)).append(" ").append(name);
+ } else {
+ argsBuilder.append(typeInPackage(type)).append(" p").append(argNumber);
+ }
+ firstArg = false;
+ argIndex += type.isWideType() ? 2 : 1;
+ argNumber++;
+ }
+ argsBuilder.append(")");
+ return argsBuilder.toString();
+ }
+ }
+
+ private static class HTMLBuilder extends StringBuilderWithIndent {
+
+ private String indent = "";
+
+ private void increaseIndent() {
+ indent += " ";
+ indent(indent);
+ }
+
+ private void decreaseIndent() {
+ indent = indent.substring(0, indent.length() - 2);
+ indent(indent);
+ }
+
+ HTMLBuilder appendTdPackage(String s) {
+ String finalString = format(s, 4);
+ appendLineStart("<td><code><em>" + finalString + "</em></code><br>");
+ if (s.startsWith("java.time")) {
+ append("<a href=\"#java-time-customizations\">See customizations</a><br");
+ } else if (s.startsWith("java.nio")) {
+ append("<a href=\"#java-nio-customizations\">See customizations</a><br");
+ }
+ return this;
+ }
+
+ private String format(String s, int i) {
+ String[] regexpSplit = s.split("\\.");
+ if (regexpSplit.length < i) {
+ return s;
+ }
+ int splitIndex = 0;
+ int mid = i / 2;
+ for (int j = 0; j < mid; j++) {
+ splitIndex += regexpSplit[j].length();
+ }
+ splitIndex += mid;
+ return s.substring(0, splitIndex) + HTML_SPLIT + s.substring(splitIndex);
+ }
+
+ HTMLBuilder appendTdClassName(String s) {
+ String finalString = format(s, 2);
+ appendLineEnd(
+ "<code><br><br><div style=\"font-size:small;font-weight:bold;\"> "
+ + finalString
+ + "</div></code><br><br></td>");
+ return this;
+ }
+
+ HTMLBuilder appendTdP(String s) {
+ appendLine("<td><p>" + s + "</p></td>");
+ return this;
+ }
+
+ HTMLBuilder appendLiCode(String s) {
+ appendLine("<li class=\"java8_table\"><code>" + s + "</code></li>");
+ return this;
+ }
+
+ HTMLBuilder appendMethodLiCode(String s) {
+ if (s.length() < MAX_LINE_CHARACTERS || s.contains("()")) {
+ return appendLiCode(s);
+ }
+ StringBuilder sb = new StringBuilder();
+ String[] split = s.split("\\(");
+ sb.append(split[0]).append('(').append(HTML_SPLIT);
+ if (split[1].length() < MAX_LINE_CHARACTERS - 2) {
+ sb.append(split[1]);
+ return appendLiCode(sb.toString());
+ }
+ String[] secondSplit = split[1].split(",");
+ sb.append(" ");
+ for (int i = 0; i < secondSplit.length; i++) {
+ sb.append(secondSplit[i]);
+ if (i != secondSplit.length - 1) {
+ sb.append(',');
+ sb.append(HTML_SPLIT);
+ }
+ }
+ return appendLiCode(sb.toString());
+ }
+
+ HTMLBuilder start(String tag) {
+ appendLine("<" + tag + ">");
+ increaseIndent();
+ return this;
+ }
+
+ HTMLBuilder end(String tag) {
+ decreaseIndent();
+ appendLine("</" + tag + ">");
+ return this;
+ }
+ }
+
+ public static class HTMLSourceBuilder extends SourceBuilder<HTMLSourceBuilder> {
+
+ private final ClassAnnotation classAnnotation;
+ private boolean parallelStreamMethod = false;
+ private boolean missingFromLatestAndroidJar = false;
+ private boolean unsupportedInMinApiRange = false;
+ private boolean covariantReturnSupported = false;
+
+ public HTMLSourceBuilder(DexClass clazz, ClassAnnotation classAnnotation) {
+ super(clazz);
+ this.classAnnotation = classAnnotation;
+ }
+
+ @Override
+ public HTMLSourceBuilder self() {
+ return this;
+ }
+
+ private String getTextAnnotations(MethodAnnotation annotation) {
+ if (annotation == null) {
+ return "";
+ }
+ StringBuilder stringBuilder = new StringBuilder();
+ if (annotation.parallelStreamMethod) {
+ stringBuilder.append(SUP_1);
+ parallelStreamMethod = true;
+ }
+ if (annotation.missingFromLatestAndroidJar) {
+ stringBuilder.append(SUP_2);
+ missingFromLatestAndroidJar = true;
+ }
+ if (annotation.unsupportedInMinApiRange) {
+ stringBuilder.append(SUP_3);
+ unsupportedInMinApiRange = true;
+ }
+ if (annotation.covariantReturnSupported) {
+ stringBuilder.append(SUP_4);
+ covariantReturnSupported = true;
+ }
+ return stringBuilder.toString();
+ }
+
+ @Override
+ public String toString() {
+ HTMLBuilder builder = new HTMLBuilder();
+ builder.start("tr");
+ if (packageName.length() > 0) {
+ builder.appendTdPackage(packageName);
+ }
+ builder.appendTdClassName(typeInPackage(className));
+ builder
+ .start("td")
+ .start(
+ "ul style=\"list-style-position:inside; list-style-type: none !important;"
+ + " margin-left:0px;padding-left:0px !important;\"");
+ if (!fields.isEmpty()) {
+ for (DexEncodedField field : fields) {
+ builder.appendLiCode(
+ accessFlags(field.accessFlags)
+ + " "
+ + typeInPackage(field.getReference().type)
+ + " "
+ + field.getReference().name);
+ }
+ }
+ if (!constructors.isEmpty()) {
+ for (DexEncodedMethod constructor : constructors.keySet()) {
+ builder.appendMethodLiCode(
+ accessFlags(constructor.accessFlags)
+ + " "
+ + typeInPackage(className)
+ + arguments(constructor)
+ + getTextAnnotations(constructors.get(constructor)));
+ }
+ }
+ if (!methods.isEmpty()) {
+ for (DexEncodedMethod method : methods.keySet()) {
+ builder.appendMethodLiCode(
+ accessFlags(method.accessFlags)
+ + " "
+ + typeInPackage(method.getReference().proto.returnType)
+ + " "
+ + method.getReference().name
+ + arguments(method)
+ + getTextAnnotations(methods.get(method)));
+ }
+ }
+ builder.end("ul").end("td");
+ StringBuilder commentBuilder = new StringBuilder();
+ if (classAnnotation.isFullySupported()) {
+ commentBuilder.append("Fully implemented class.").append(HTML_SPLIT);
+ }
+ if (parallelStreamMethod) {
+ commentBuilder
+ .append(SUP_1)
+ .append("Supported only on devices which API level is 21 or higher.")
+ .append(HTML_SPLIT);
+ }
+ if (missingFromLatestAndroidJar) {
+ commentBuilder
+ .append(SUP_2)
+ .append("Not present in Android ")
+ .append(MAX_TESTED_ANDROID_API_LEVEL)
+ .append(" (May not resolve at compilation).")
+ .append(HTML_SPLIT);
+ }
+ if (unsupportedInMinApiRange) {
+ commentBuilder
+ .append(SUP_3)
+ .append(" Not supported at all minSDK levels.")
+ .append(HTML_SPLIT);
+ }
+ if (covariantReturnSupported) {
+ commentBuilder
+ .append(SUP_4)
+ .append(" Also supported with covariant return type.")
+ .append(HTML_SPLIT);
+ }
+ if (!classAnnotation.getUnsupportedMethods().isEmpty()) {
+ commentBuilder
+ .append("Some methods (")
+ .append(classAnnotation.getUnsupportedMethods().size())
+ .append(") present in Android ")
+ .append(MAX_TESTED_ANDROID_API_LEVEL)
+ .append(" are not supported.");
+ }
+ builder.appendTdP(commentBuilder.toString());
+ builder.end("tr");
+ return builder.toString();
+ }
+ }
+
+ private void generateClassHTML(PrintStream ps, SupportedClass supportedClass) {
+ DexClass clazz = supportedClass.getClazz();
+ SourceBuilder<HTMLSourceBuilder> builder =
+ new HTMLSourceBuilder(clazz, supportedClass.getClassAnnotation());
+ // We need to extend to support fields.
+ StreamSupport.stream(clazz.fields().spliterator(), false)
+ .filter(field -> field.accessFlags.isPublic() || field.accessFlags.isProtected())
+ .sorted(Comparator.comparing(DexEncodedField::toSourceString))
+ .forEach(builder::addField);
+ supportedClass.forEachMethodAndAnnotation(
+ (method, methodAnnotation) -> {
+ if ((method.accessFlags.isPublic() || method.accessFlags.isProtected())
+ && !method.accessFlags.isBridge()) {
+ builder.addMethod(method, methodAnnotation);
+ }
+ });
+ ps.println(builder);
+ }
+
+ @Override
+ AndroidApiLevel run() throws Exception {
+ PrintStream ps = new PrintStream(Files.newOutputStream(outputDirectory.resolve("apis.html")));
+
+ SupportedClasses supportedClasses =
+ new SupportedMethodsGenerator(options)
+ .run(desugaredLibraryImplementation, desugaredLibrarySpecificationPath);
+
+ // Full classes added.
+ supportedClasses.forEachClass(supportedClass -> generateClassHTML(ps, supportedClass));
+ return MAX_TESTED_ANDROID_API_LEVEL;
+ }
+
+ public static void main(String[] args) throws Exception {
+ AbstractGenerateFiles.main(args);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
index 422be3d..b7a1a27 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/GenerateLintFiles.java
@@ -5,58 +5,39 @@
package com.android.tools.r8.ir.desugar.desugaredlibrary.lint;
import com.android.tools.r8.ClassFileConsumer;
-import com.android.tools.r8.StringResource;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.Version;
import com.android.tools.r8.cf.CfVersion;
import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfThrow;
-import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.Marker;
+import com.android.tools.r8.dex.Marker.Backend;
import com.android.tools.r8.dex.Marker.Tool;
import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.CfCode;
-import com.android.tools.r8.graph.CfCode.LocalVariableInfo;
-import com.android.tools.r8.graph.ClassAccessFlags;
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProgramClass.ChecksumSupplier;
-import com.android.tools.r8.graph.DexProto;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DirectMappedDexApplication;
-import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.GenericSignature.ClassSignature;
import com.android.tools.r8.graph.GenericSignature.MethodTypeSignature;
import com.android.tools.r8.graph.LazyLoadedDexApplication;
-import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodCollection.MethodCollectionFactory;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
-import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.MethodAnnotation;
import com.android.tools.r8.jar.CfApplicationWriter;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.AndroidApp;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.FileUtils;
-import com.android.tools.r8.utils.InternalOptions;
-import com.android.tools.r8.utils.Reporter;
-import com.android.tools.r8.utils.StringUtils;
-import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Sets;
import java.io.File;
-import java.io.IOException;
-import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -65,29 +46,10 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.function.BiPredicate;
-import java.util.function.Predicate;
-import java.util.stream.StreamSupport;
-public class GenerateLintFiles {
-
- private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
-
- private final DexItemFactory factory = new DexItemFactory();
- private final Reporter reporter = new Reporter();
- private final InternalOptions options =
- new InternalOptions(factory, reporter).withModifications(options -> options.tool = Tool.L8);
-
- private final MachineDesugaredLibrarySpecification desugaredLibrarySpecification;
- private final Collection<Path> desugaredLibraryImplementation;
- private final Path outputDirectory;
-
- private final Set<DexMethod> parallelMethods = Sets.newIdentityHashSet();
+public class GenerateLintFiles extends AbstractGenerateFiles {
public static GenerateLintFiles createForTesting(
Path specification, Set<Path> implementation, Path outputDirectory) throws Exception {
@@ -97,69 +59,15 @@
public GenerateLintFiles(
String desugarConfigurationPath, String desugarImplementationPath, String outputDirectory)
throws Exception {
- this(
- Paths.get(desugarConfigurationPath),
- ImmutableList.of(Paths.get(desugarImplementationPath)),
- Paths.get(outputDirectory));
+ super(desugarConfigurationPath, desugarImplementationPath, outputDirectory);
}
- private GenerateLintFiles(
+ public GenerateLintFiles(
Path desugarConfigurationPath,
Collection<Path> desugarImplementationPath,
Path outputDirectory)
throws Exception {
- DesugaredLibrarySpecification specification =
- readDesugaredLibraryConfiguration(desugarConfigurationPath);
- Path androidJarPath = getAndroidJarPath(specification.getRequiredCompilationApiLevel());
- DexApplication app = createApp(androidJarPath, options);
- this.desugaredLibrarySpecification = specification.toMachineSpecification(app, Timing.empty());
-
- this.desugaredLibraryImplementation = desugarImplementationPath;
- this.outputDirectory = outputDirectory;
- if (!Files.isDirectory(this.outputDirectory)) {
- throw new Exception("Output directory " + outputDirectory + " is not a directory");
- }
-
- DexType streamType = factory.createType(factory.createString("Ljava/util/stream/Stream;"));
- DexMethod parallelMethod =
- factory.createMethod(
- factory.collectionType,
- factory.createProto(streamType),
- factory.createString("parallelStream"));
- parallelMethods.add(parallelMethod);
- DexType baseStreamType =
- factory.createType(factory.createString("Ljava/util/stream/BaseStream;"));
- for (String typePrefix : new String[] {"Base", "Double", "Int", "Long"}) {
- streamType =
- factory.createType(factory.createString("Ljava/util/stream/" + typePrefix + "Stream;"));
- parallelMethod =
- factory.createMethod(
- streamType, factory.createProto(streamType), factory.createString("parallel"));
- parallelMethods.add(parallelMethod);
- // Also filter out the generated bridges for the covariant return type.
- parallelMethod =
- factory.createMethod(
- streamType, factory.createProto(baseStreamType), factory.createString("parallel"));
- parallelMethods.add(parallelMethod);
- }
- }
-
- private static Path getAndroidJarPath(AndroidApiLevel apiLevel) {
- String jar =
- apiLevel == AndroidApiLevel.MASTER
- ? "third_party/android_jar/lib-master/android.jar"
- : String.format(ANDROID_JAR_PATTERN, apiLevel.getLevel());
- return Paths.get(jar);
- }
-
- private DesugaredLibrarySpecification readDesugaredLibraryConfiguration(
- Path desugarConfigurationPath) {
- return DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification(
- StringResource.fromFile(desugarConfigurationPath),
- factory,
- reporter,
- false,
- AndroidApiLevel.B.getLevel());
+ super(desugarConfigurationPath, desugarImplementationPath, outputDirectory);
}
private CfCode buildEmptyThrowingCfCode(DexMethod method) {
@@ -227,101 +135,6 @@
builder.addProgramClass(programClass);
}
- public static class SupportedMethods {
-
- public final Set<DexClass> classesWithAllMethodsSupported;
- public final Map<DexClass, List<DexEncodedMethod>> supportedMethods;
-
- public SupportedMethods(
- Set<DexClass> classesWithAllMethodsSupported,
- Map<DexClass, List<DexEncodedMethod>> supportedMethods) {
- this.classesWithAllMethodsSupported = classesWithAllMethodsSupported;
- this.supportedMethods = supportedMethods;
- }
- }
-
- private SupportedMethods collectSupportedMethods(
- AndroidApiLevel compilationApiLevel, Predicate<DexEncodedMethod> supported) throws Exception {
-
- // Read the android.jar for the compilation API level. Read it as program instead of library
- // to get the local information for parameter names.
- AndroidApp library =
- AndroidApp.builder().addProgramFiles(getAndroidJarPath(compilationApiLevel)).build();
- DirectMappedDexApplication dexApplication =
- new ApplicationReader(library, options, Timing.empty()).read().toDirect();
-
- AndroidApp implementation =
- AndroidApp.builder().addProgramFiles(desugaredLibraryImplementation).build();
- DirectMappedDexApplication implementationApplication =
- new ApplicationReader(implementation, options, Timing.empty()).read().toDirect();
-
- options.setDesugaredLibrarySpecification(desugaredLibrarySpecification);
- List<DexMethod> backports =
- BackportedMethodRewriter.generateListOfBackportedMethods(
- implementation, options, ThreadUtils.getExecutorService(1));
-
- // Collect all the methods that the library desugar configuration adds support for.
- Set<DexClass> classesWithAllMethodsSupported = Sets.newIdentityHashSet();
- Map<DexClass, List<DexEncodedMethod>> supportedMethods = new LinkedHashMap<>();
- for (DexProgramClass clazz : dexApplication.classes()) {
- if (clazz.accessFlags.isPublic() && desugaredLibrarySpecification.isSupported(clazz.type)) {
- DexProgramClass implementationClass =
- implementationApplication.programDefinitionFor(clazz.getType());
- if (implementationClass == null) {
- throw new Exception("Implementation class not found for " + clazz.toSourceString());
- }
- boolean allMethodsAdded = true;
- for (DexEncodedMethod method : clazz.methods()) {
- if (!method.isPublic()) {
- continue;
- }
- ProgramMethod implementationMethod =
- implementationClass.lookupProgramMethod(method.getReference());
- // Don't include methods which are not implemented by the desugared library.
- if (supported.test(method)
- && (implementationMethod != null || backports.contains(method.getReference()))) {
- supportedMethods.computeIfAbsent(clazz, k -> new ArrayList<>()).add(method);
- } else {
- allMethodsAdded = false;
- }
- }
- if (allMethodsAdded) {
- classesWithAllMethodsSupported.add(clazz);
- }
- }
-
- // All emulated interfaces static and default methods are supported.
- if (desugaredLibrarySpecification.getEmulatedInterfaces().containsKey(clazz.type)) {
- assert clazz.isInterface();
- for (DexEncodedMethod method : clazz.methods()) {
- if (!method.isDefaultMethod() && !method.isStatic()) {
- continue;
- }
- if (supported.test(method)) {
- supportedMethods.computeIfAbsent(clazz, k -> new ArrayList<>()).add(method);
- }
- }
- }
- }
-
- // All retargeted methods are supported.
- desugaredLibrarySpecification.forEachRetargetMethod(
- method -> {
- DexClass clazz = dexApplication.contextIndependentDefinitionFor(method.getHolderType());
- assert clazz != null;
- DexEncodedMethod encodedMethod = clazz.lookupMethod(method);
- if (encodedMethod == null) {
- // Some methods are registered but present higher in the hierarchy, ignore them.
- return;
- }
- if (supported.test(encodedMethod)) {
- supportedMethods.computeIfAbsent(clazz, k -> new ArrayList<>()).add(encodedMethod);
- }
- });
-
- return new SupportedMethods(classesWithAllMethodsSupported, supportedMethods);
- }
-
private String lintBaseFileName(
AndroidApiLevel compilationApiLevel, AndroidApiLevel minApiLevel) {
return "desugared_apis_" + compilationApiLevel.getLevel() + "_" + minApiLevel.getLevel();
@@ -342,33 +155,38 @@
private void writeLintFiles(
AndroidApiLevel compilationApiLevel,
AndroidApiLevel minApiLevel,
- SupportedMethods supportedMethods)
+ SupportedClasses supportedClasses)
throws Exception {
// Build a plain text file with the desugared APIs.
List<String> desugaredApisSignatures = new ArrayList<>();
LazyLoadedDexApplication.Builder builder = DexApplication.builder(options, Timing.empty());
- supportedMethods.supportedMethods.forEach(
- (clazz, methods) -> {
+ supportedClasses.forEachClass(
+ (supportedClass) -> {
String classBinaryName =
- DescriptorUtils.getClassBinaryNameFromDescriptor(clazz.type.descriptor.toString());
- if (!supportedMethods.classesWithAllMethodsSupported.contains(clazz)) {
- for (DexEncodedMethod method : methods) {
- if (method.isInstanceInitializer() || method.isClassInitializer()) {
- // No new constructors are added.
- continue;
- }
- desugaredApisSignatures.add(
- classBinaryName
- + '#'
- + method.getReference().name
- + method.getReference().proto.toDescriptorString());
- }
+ DescriptorUtils.getClassBinaryNameFromDescriptor(
+ supportedClass.getType().descriptor.toString());
+ if (!supportedClass.getClassAnnotation().isFullySupported()) {
+ supportedClass.forEachMethodAndAnnotation(
+ (method, methodAnnotation) -> {
+ if (method.isInstanceInitializer() || method.isClassInitializer()) {
+ // No new constructors are added.
+ return;
+ }
+ if (shouldAddMethodToLint(methodAnnotation, minApiLevel)) {
+ desugaredApisSignatures.add(
+ classBinaryName
+ + '#'
+ + method.getReference().name
+ + method.getReference().proto.toDescriptorString());
+ }
+ });
} else {
desugaredApisSignatures.add(classBinaryName);
}
- addMethodsToHeaderJar(builder, clazz, methods);
+ addMethodsToHeaderJar(
+ builder, supportedClass.getClazz(), supportedClass.getSupportedMethods());
});
// Write a plain text file with the desugared APIs.
@@ -381,7 +199,12 @@
AppView.createForD8(
AppInfo.createInitialAppInfo(
builder.build(), GlobalSyntheticsStrategy.forNonSynthesizing()));
- CfApplicationWriter writer = new CfApplicationWriter(appView, options.getMarker());
+ Marker marker =
+ new Marker(Tool.D8)
+ .setVersion(Version.LABEL)
+ .setCompilationMode(CompilationMode.DEBUG)
+ .setBackend(Backend.CF);
+ CfApplicationWriter writer = new CfApplicationWriter(appView, marker);
ClassFileConsumer consumer =
new ClassFileConsumer.ArchiveConsumer(
lintFile(compilationApiLevel, minApiLevel, FileUtils.JAR_EXTENSION));
@@ -389,491 +212,47 @@
consumer.finished(options.reporter);
}
+ private boolean shouldAddMethodToLint(
+ MethodAnnotation methodAnnotation, AndroidApiLevel minApiLevel) {
+ if (methodAnnotation == null) {
+ return true;
+ }
+ if (methodAnnotation.unsupportedInMinApiRange) {
+ // Do not lint method which are unavailable with some min apis.
+ return false;
+ }
+ if (methodAnnotation.parallelStreamMethod) {
+ return minApiLevel == AndroidApiLevel.L;
+ }
+ assert methodAnnotation.missingFromLatestAndroidJar;
+ // We add missing methods from the latest jar, javac will add the squikles.
+ return true;
+ }
+
private void generateLintFiles(
AndroidApiLevel compilationApiLevel,
- Predicate<AndroidApiLevel> generateForThisMinApiLevel,
- BiPredicate<AndroidApiLevel, DexEncodedMethod> supportedForMinApiLevel)
+ AndroidApiLevel minApiLevel,
+ SupportedClasses supportedMethods)
throws Exception {
System.out.print(" - generating for min API:");
- for (AndroidApiLevel minApiLevel : AndroidApiLevel.values()) {
- if (!generateForThisMinApiLevel.test(minApiLevel)) {
- continue;
- }
-
- System.out.print(" " + minApiLevel);
-
- SupportedMethods supportedMethods =
- collectSupportedMethods(
- compilationApiLevel, (method -> supportedForMinApiLevel.test(minApiLevel, method)));
- writeLintFiles(compilationApiLevel, minApiLevel, supportedMethods);
- }
- System.out.println();
+ System.out.print(" " + minApiLevel);
+ writeLintFiles(compilationApiLevel, minApiLevel, supportedMethods);
}
- private void run() throws Exception {
- // Run over all the API levels that the desugared library can be compiled with.
- for (int apiLevel = AndroidApiLevel.T.getLevel();
- apiLevel >= desugaredLibrarySpecification.getRequiredCompilationApiLevel().getLevel();
- apiLevel--) {
- System.out.println("Generating lint files for compile API " + apiLevel);
- run(apiLevel);
- }
- }
-
- public void run(int apiLevel) throws Exception {
- generateLintFiles(
- AndroidApiLevel.getAndroidApiLevel(apiLevel),
- minApiLevel -> minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B,
- (minApiLevel, method) -> {
- assert minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B;
- if (minApiLevel == AndroidApiLevel.L) {
- return true;
- }
- assert minApiLevel == AndroidApiLevel.B;
- return !parallelMethods.contains(method.getReference());
- });
- }
-
- private static DexApplication createApp(Path androidLib, InternalOptions options)
- throws IOException {
- AndroidApp.Builder builder = AndroidApp.builder();
- AndroidApp inputApp = builder.addLibraryFiles(androidLib).build();
- ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
- ExecutorService executorService = ThreadUtils.getExecutorService(options);
- assert !options.ignoreJavaLibraryOverride;
- options.ignoreJavaLibraryOverride = true;
- DexApplication app = applicationReader.read(executorService);
- options.ignoreJavaLibraryOverride = false;
- return app;
- }
-
- private static class StringBuilderWithIndent {
-
- String NL = System.lineSeparator();
- StringBuilder builder = new StringBuilder();
- String indent = "";
-
- StringBuilderWithIndent() {}
-
- StringBuilderWithIndent indent(String indent) {
- this.indent = indent;
- return this;
- }
-
- StringBuilderWithIndent appendLineStart(String lineStart) {
- builder.append(indent);
- builder.append(lineStart);
- return this;
- }
-
- StringBuilderWithIndent append(String string) {
- builder.append(string);
- return this;
- }
-
- StringBuilderWithIndent appendLineEnd(String lineEnd) {
- builder.append(lineEnd);
- builder.append(NL);
- return this;
- }
-
- StringBuilderWithIndent appendLine(String line) {
- builder.append(indent);
- builder.append(line);
- builder.append(NL);
- return this;
- }
-
- StringBuilderWithIndent emptyLine() {
- builder.append(NL);
- return this;
- }
-
- @Override
- public String toString() {
- return builder.toString();
- }
- }
-
- private abstract static class SourceBuilder<B extends SourceBuilder> {
-
- protected final DexClass clazz;
- protected final boolean newClass;
- protected List<DexEncodedField> fields = new ArrayList<>();
- protected List<DexEncodedMethod> constructors = new ArrayList<>();
- protected List<DexEncodedMethod> methods = new ArrayList<>();
-
- String className;
- String packageName;
-
- private SourceBuilder(DexClass clazz, boolean newClass) {
- this.clazz = clazz;
- this.newClass = newClass;
- this.className = clazz.type.toSourceString();
- int index = this.className.lastIndexOf('.');
- this.packageName = index > 0 ? this.className.substring(0, index) : "";
- }
-
- public abstract B self();
-
- private B addField(DexEncodedField field) {
- fields.add(field);
- return self();
- }
-
- private B addMethod(DexEncodedMethod method) {
- assert !method.isClassInitializer();
- if (method.isInitializer()) {
- constructors.add(method);
- } else {
- methods.add(method);
- }
- return self();
- }
-
- protected String typeInPackage(String typeName, String packageName) {
- if (typeName.startsWith(packageName)
- && typeName.length() > packageName.length()
- && typeName.charAt(packageName.length()) == '.'
- && typeName.indexOf('.', packageName.length() + 1) == -1) {
- return typeName.substring(packageName.length() + 1);
- }
- return null;
- }
-
- protected String typeInPackage(String typeName) {
- String result = typeInPackage(typeName, packageName);
- if (result == null) {
- result = typeInPackage(typeName, "java.lang");
- }
- if (result == null) {
- result = typeName;
- }
- return result.replace('$', '.');
- }
-
- protected String typeInPackage(DexType type) {
- if (type.isPrimitiveType()) {
- return type.toSourceString();
- }
- return typeInPackage(type.toSourceString());
- }
-
- protected String accessFlags(ClassAccessFlags accessFlags) {
- List<String> flags = new ArrayList<>();
- if (accessFlags.isPublic()) {
- flags.add("public");
- }
- if (accessFlags.isProtected()) {
- flags.add("protected");
- }
- if (accessFlags.isPrivate()) {
- assert false;
- flags.add("private");
- }
- if (accessFlags.isPackagePrivate()) {
- assert false;
- flags.add("/* package */");
- }
- if (accessFlags.isAbstract() && !accessFlags.isInterface()) {
- flags.add("abstract");
- }
- if (accessFlags.isStatic()) {
- flags.add("static");
- }
- if (accessFlags.isFinal()) {
- flags.add("final");
- }
- return String.join(" ", flags);
- }
-
- protected String accessFlags(FieldAccessFlags accessFlags) {
- List<String> flags = new ArrayList<>();
- if (accessFlags.isPublic()) {
- flags.add("public");
- }
- if (accessFlags.isProtected()) {
- flags.add("protected");
- }
- if (accessFlags.isPrivate()) {
- assert false;
- flags.add("private");
- }
- if (accessFlags.isPackagePrivate()) {
- assert false;
- flags.add("/* package */");
- }
- if (accessFlags.isStatic()) {
- flags.add("static");
- }
- if (accessFlags.isFinal()) {
- flags.add("final");
- }
- return String.join(" ", flags);
- }
-
- protected String accessFlags(MethodAccessFlags accessFlags) {
- List<String> flags = new ArrayList<>();
- if (accessFlags.isPublic()) {
- flags.add("public");
- }
- if (accessFlags.isProtected()) {
- flags.add("protected");
- }
- if (accessFlags.isPrivate()) {
- assert false;
- flags.add("private");
- }
- if (accessFlags.isPackagePrivate()) {
- assert false;
- flags.add("/* package */");
- }
- if (accessFlags.isAbstract()) {
- flags.add("abstract");
- }
- if (accessFlags.isStatic()) {
- flags.add("static");
- }
- if (accessFlags.isFinal()) {
- flags.add("final");
- }
- return String.join(" ", flags);
- }
-
- public String arguments(DexEncodedMethod method) {
- DexProto proto = method.getReference().proto;
- StringBuilder argsBuilder = new StringBuilder();
- boolean firstArg = true;
- int argIndex = method.isVirtualMethod() || method.accessFlags.isConstructor() ? 1 : 0;
- int argNumber = 0;
- argsBuilder.append("(");
- for (DexType type : proto.parameters.values) {
- if (!firstArg) {
- argsBuilder.append(", ");
- }
- if (method.hasCode()) {
- String name = "p" + argNumber;
- for (LocalVariableInfo localVariable : method.getCode().asCfCode().getLocalVariables()) {
- if (localVariable.getIndex() == argIndex) {
- assert !localVariable.getLocal().name.toString().equals("this");
- name = localVariable.getLocal().name.toString();
- }
- }
- argsBuilder.append(typeInPackage(type)).append(" ").append(name);
- } else {
- argsBuilder.append(typeInPackage(type)).append(" p").append(argNumber);
- }
- firstArg = false;
- argIndex += type.isWideType() ? 2 : 1;
- argNumber++;
- }
- argsBuilder.append(")");
- return argsBuilder.toString();
- }
- }
-
- private static class HTMLBuilder extends StringBuilderWithIndent {
-
- private String indent = "";
-
- private void increaseIndent() {
- indent += " ";
- indent(indent);
- }
-
- private void decreaseIndent() {
- indent = indent.substring(0, indent.length() - 2);
- indent(indent);
- }
-
- HTMLBuilder appendTdPackage(String s) {
- appendLineStart("<td><code><em>" + s + "</em></code><br>");
- if (s.startsWith("java.time")) {
- append("<a href=\"#java-time-customizations\">See customizations</a><br");
- } else if (s.startsWith("java.nio")) {
- append("<a href=\"#java-nio-customizations\">See customizations</a><br");
- }
- return this;
- }
-
- HTMLBuilder appendTdClassName(String s) {
- appendLineEnd(
- "<code><br><br><div style=\"font-size:small;font-weight:bold;\"> "
- + s
- + "</div></code><br><br></td>");
- return this;
- }
-
- HTMLBuilder appendTdP(String s) {
- appendLine("<td><p>" + s + "</p></td>");
- return this;
- }
-
- HTMLBuilder appendLiCode(String s) {
- appendLine("<li class=\"java8_table\"><code>" + s + "</code></li>");
- return this;
- }
-
- HTMLBuilder start(String tag) {
- appendLine("<" + tag + ">");
- increaseIndent();
- return this;
- }
-
- HTMLBuilder end(String tag) {
- decreaseIndent();
- appendLine("</" + tag + ">");
- return this;
- }
- }
-
- public static class HTMLSourceBuilder extends SourceBuilder<HTMLSourceBuilder> {
-
- private final Set<DexMethod> parallelMethods;
-
- public HTMLSourceBuilder(DexClass clazz, boolean newClass, Set<DexMethod> parallelMethods) {
- super(clazz, newClass);
- this.parallelMethods = parallelMethods;
- }
-
- @Override
- public HTMLSourceBuilder self() {
- return this;
- }
-
- @Override
- public String toString() {
- HTMLBuilder builder = new HTMLBuilder();
- builder.start("tr");
- if (packageName.length() > 0) {
- builder.appendTdPackage(packageName);
- }
- builder.appendTdClassName(typeInPackage(className));
- builder
- .start("td")
- .start(
- "ul style=\"list-style-position:inside; list-style-type: none !important;"
- + " margin-left:0px;padding-left:0px !important;\"");
- if (!fields.isEmpty()) {
- assert newClass; // Currently no fields are added to existing classes.
- for (DexEncodedField field : fields) {
- builder.appendLiCode(
- accessFlags(field.accessFlags)
- + " "
- + typeInPackage(field.getReference().type)
- + " "
- + field.getReference().name);
- }
- }
- if (!constructors.isEmpty()) {
- for (DexEncodedMethod constructor : constructors) {
- builder.appendLiCode(
- accessFlags(constructor.accessFlags)
- + " "
- + typeInPackage(className)
- + arguments(constructor));
- }
- }
- List<String> parallelM = new ArrayList<>();
- if (!methods.isEmpty()) {
- for (DexEncodedMethod method : methods) {
- builder.appendLiCode(
- accessFlags(method.accessFlags)
- + " "
- + typeInPackage(method.getReference().proto.returnType)
- + " "
- + method.getReference().name
- + arguments(method));
- if (parallelMethods.contains(method.getReference())) {
- parallelM.add(method.getReference().name.toString());
- }
- }
- }
- builder.end("ul").end("td");
- StringBuilder commentBuilder = new StringBuilder();
- if (newClass) {
- commentBuilder.append("Fully implemented class.");
- } else {
- commentBuilder.append("Additional methods on existing class.");
- }
- if (!parallelM.isEmpty()) {
- commentBuilder.append(newClass ? "" : "<br>");
- if (parallelM.size() == 1) {
- commentBuilder
- .append("The method <code>")
- .append(parallelM.get(0))
- .append("</code> is only supported from API level 21.");
- } else {
- commentBuilder.append("The following methods are only supported from API level 21:<br>");
- for (int i = 0; i < parallelM.size(); i++) {
- commentBuilder.append("<code>").append(parallelM.get(i)).append("</code><br>");
- }
- }
- }
- builder.appendTdP(commentBuilder.toString());
- builder.end("tr");
- return builder.toString();
- }
- }
-
- private void generateClassHTML(
- PrintStream ps,
- DexClass clazz,
- boolean newClass,
- Predicate<DexEncodedField> fieldsFilter,
- Predicate<DexEncodedMethod> methodsFilter) {
- SourceBuilder builder = new HTMLSourceBuilder(clazz, newClass, parallelMethods);
- StreamSupport.stream(clazz.fields().spliterator(), false)
- .filter(fieldsFilter)
- .filter(field -> field.accessFlags.isPublic() || field.accessFlags.isProtected())
- .sorted(Comparator.comparing(DexEncodedField::toSourceString))
- .forEach(builder::addField);
- StreamSupport.stream(clazz.methods().spliterator(), false)
- .filter(methodsFilter)
- .filter(
- method ->
- (method.accessFlags.isPublic() || method.accessFlags.isProtected())
- && !method.accessFlags.isBridge())
- .sorted(Comparator.comparing(DexEncodedMethod::toSourceString))
- .forEach(builder::addMethod);
- ps.println(builder);
- }
-
- private void generateDesugaredLibraryApisDocumetation() throws Exception {
- PrintStream ps = new PrintStream(Files.newOutputStream(outputDirectory.resolve("apis.html")));
- // Full classes added.
- SupportedMethods supportedMethods = collectSupportedMethods(AndroidApiLevel.Q, x -> true);
- supportedMethods.classesWithAllMethodsSupported.stream()
- .sorted(Comparator.comparing(clazz -> clazz.type.toSourceString()))
- .forEach(clazz -> generateClassHTML(ps, clazz, true, field -> true, method -> true));
-
- // Methods added to existing classes.
- supportedMethods.supportedMethods.keySet().stream()
- .filter(clazz -> !supportedMethods.classesWithAllMethodsSupported.contains(clazz))
- .sorted(Comparator.comparing(clazz -> clazz.type.toSourceString()))
- .forEach(
- clazz ->
- generateClassHTML(
- ps,
- clazz,
- false,
- field -> false,
- method -> supportedMethods.supportedMethods.get(clazz).contains(method)));
+ @Override
+ public AndroidApiLevel run() throws Exception {
+ AndroidApiLevel compilationLevel =
+ desugaredLibrarySpecification.getRequiredCompilationApiLevel();
+ SupportedClasses supportedMethods =
+ new SupportedMethodsGenerator(options)
+ .run(desugaredLibraryImplementation, desugaredLibrarySpecificationPath);
+ System.out.println("Generating lint files for compile API " + compilationLevel);
+ generateLintFiles(compilationLevel, AndroidApiLevel.B, supportedMethods);
+ generateLintFiles(compilationLevel, AndroidApiLevel.L, supportedMethods);
+ return compilationLevel;
}
public static void main(String[] args) throws Exception {
- if (args.length == 3) {
- new GenerateLintFiles(args[0], args[1], args[2]).run();
- return;
- }
- if (args.length == 4 && args[0].equals("--generate-api-docs")) {
- new GenerateLintFiles(args[1], args[2], args[3]).generateDesugaredLibraryApisDocumetation();
- return;
- }
- throw new RuntimeException(
- StringUtils.joinLines(
- "Invalid invocation.",
- "Usage: GenerateLineFiles [--generate-api-docs] "
- + "<desugar configuration> <desugar implementation> <output directory>"));
+ AbstractGenerateFiles.main(args);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java
new file mode 100644
index 0000000..3c1cfd6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedClasses.java
@@ -0,0 +1,332 @@
+// Copyright (c) 2023, 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.desugar.desugaredlibrary.lint;
+
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSortedMap;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+public class SupportedClasses {
+ private final Map<DexType, SupportedClass> supportedClasses;
+
+ public void forEachClass(Consumer<SupportedClass> consumer) {
+ supportedClasses.values().forEach(consumer);
+ }
+
+ SupportedClasses(Map<DexType, SupportedClass> supportedClasses) {
+ this.supportedClasses = supportedClasses;
+ }
+
+ public static class SupportedClass {
+
+ private final DexClass clazz;
+ private final ClassAnnotation classAnnotation;
+ private final List<DexEncodedMethod> supportedMethods;
+ private final Map<DexMethod, MethodAnnotation> methodAnnotations;
+
+ private SupportedClass(
+ DexClass clazz,
+ ClassAnnotation classAnnotation,
+ List<DexEncodedMethod> supportedMethods,
+ Map<DexMethod, MethodAnnotation> methodAnnotations) {
+ this.clazz = clazz;
+ this.classAnnotation = classAnnotation;
+ this.supportedMethods = supportedMethods;
+ this.methodAnnotations = methodAnnotations;
+ }
+
+ public DexType getType() {
+ return clazz.type;
+ }
+
+ public DexClass getClazz() {
+ return clazz;
+ }
+
+ public ClassAnnotation getClassAnnotation() {
+ return classAnnotation;
+ }
+
+ public List<DexEncodedMethod> getSupportedMethods() {
+ return supportedMethods;
+ }
+
+ public void forEachMethodAndAnnotation(
+ BiConsumer<DexEncodedMethod, MethodAnnotation> biConsumer) {
+ for (DexEncodedMethod supportedMethod : supportedMethods) {
+ biConsumer.accept(supportedMethod, getMethodAnnotation(supportedMethod.getReference()));
+ }
+ }
+
+ public MethodAnnotation getMethodAnnotation(DexMethod method) {
+ return methodAnnotations.get(method);
+ }
+
+ static Builder builder(DexClass clazz) {
+ return new Builder(clazz);
+ }
+
+ private static class Builder {
+
+ private final DexClass clazz;
+ private ClassAnnotation classAnnotation;
+ private final List<DexEncodedMethod> supportedMethods = new ArrayList<>();
+ private final Map<DexMethod, MethodAnnotation> methodAnnotations = new HashMap<>();
+
+ private Builder(DexClass clazz) {
+ this.clazz = clazz;
+ }
+
+ void forEachMethods(BiConsumer<DexClass, List<DexEncodedMethod>> biConsumer) {
+ biConsumer.accept(clazz, supportedMethods);
+ }
+
+ void forEachMethod(BiConsumer<DexClass, DexEncodedMethod> biConsumer) {
+ for (DexEncodedMethod dexEncodedMethod : supportedMethods) {
+ biConsumer.accept(clazz, dexEncodedMethod);
+ }
+ }
+
+ void addSupportedMethod(DexEncodedMethod method) {
+ assert method.getHolderType() == clazz.type;
+ supportedMethods.add(method);
+ }
+
+ void annotateClass(ClassAnnotation annotation) {
+ assert annotation != null;
+ assert classAnnotation == null;
+ classAnnotation = annotation;
+ }
+
+ void annotateMethod(DexMethod method, MethodAnnotation annotation) {
+ assert method.getHolderType() == clazz.type;
+ MethodAnnotation prev =
+ methodAnnotations.getOrDefault(method, MethodAnnotation.getDefault());
+ methodAnnotations.put(method, annotation.combine(prev));
+ }
+
+ MethodAnnotation getMethodAnnotation(DexMethod method) {
+ return methodAnnotations.get(method);
+ }
+
+ SupportedClass build() {
+ supportedMethods.sort(Comparator.comparing(DexEncodedMethod::getReference));
+ return new SupportedClass(
+ clazz, classAnnotation, ImmutableList.copyOf(supportedMethods), methodAnnotations);
+ }
+ }
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ static class Builder {
+
+ Map<DexType, SupportedClass.Builder> supportedClassBuilders = new IdentityHashMap<>();
+
+ void forEachClassAndMethods(BiConsumer<DexClass, List<DexEncodedMethod>> biConsumer) {
+ supportedClassBuilders
+ .values()
+ .forEach(classBuilder -> classBuilder.forEachMethods(biConsumer));
+ }
+
+ void forEachClassAndMethod(BiConsumer<DexClass, DexEncodedMethod> biConsumer) {
+ supportedClassBuilders
+ .values()
+ .forEach(classBuilder -> classBuilder.forEachMethod(biConsumer));
+ }
+
+ void addSupportedMethod(DexClass holder, DexEncodedMethod method) {
+ SupportedClass.Builder classBuilder =
+ supportedClassBuilders.computeIfAbsent(
+ holder.type, clazz -> SupportedClass.builder(holder));
+ classBuilder.addSupportedMethod(method);
+ }
+
+ void annotateClass(DexType type, ClassAnnotation annotation) {
+ SupportedClass.Builder classBuilder = supportedClassBuilders.get(type);
+ assert classBuilder != null;
+ classBuilder.annotateClass(annotation);
+ }
+
+ void annotateMethod(DexMethod method, MethodAnnotation annotation) {
+ SupportedClass.Builder classBuilder = supportedClassBuilders.get(method.getHolderType());
+ assert classBuilder != null;
+ classBuilder.annotateMethod(method, annotation);
+ }
+
+ void annotateMethodIfPresent(DexMethod method, MethodAnnotation annotation) {
+ SupportedClass.Builder classBuilder = supportedClassBuilders.get(method.getHolderType());
+ if (classBuilder == null) {
+ return;
+ }
+ annotateMethod(method, annotation);
+ }
+
+ MethodAnnotation getMethodAnnotation(DexMethod method) {
+ SupportedClass.Builder classBuilder = supportedClassBuilders.get(method.getHolderType());
+ assert classBuilder != null;
+ return classBuilder.getMethodAnnotation(method);
+ }
+
+ SupportedClasses build() {
+ Map<DexType, SupportedClass> map = new IdentityHashMap<>();
+ supportedClassBuilders.forEach(
+ (type, classBuilder) -> {
+ map.put(type, classBuilder.build());
+ });
+ return new SupportedClasses(ImmutableSortedMap.copyOf(map));
+ }
+ }
+
+ static class ClassAnnotation {
+
+ private final boolean fullySupported;
+ // Methods in latest android.jar but unsupported.
+ private final List<DexMethod> unsupportedMethods;
+
+ public ClassAnnotation(boolean fullySupported, List<DexMethod> unsupportedMethods) {
+ this.fullySupported = fullySupported;
+ unsupportedMethods.sort(Comparator.naturalOrder());
+ this.unsupportedMethods = ImmutableList.copyOf(unsupportedMethods);
+ }
+
+ public boolean isFullySupported() {
+ return fullySupported;
+ }
+
+ public List<DexMethod> getUnsupportedMethods() {
+ return unsupportedMethods;
+ }
+ }
+
+ public static class MethodAnnotation {
+
+ private static final MethodAnnotation COVARIANT_RETURN_SUPPORTED =
+ new MethodAnnotation(false, false, true, false, -1, -1);
+ private static final MethodAnnotation DEFAULT =
+ new MethodAnnotation(false, false, false, false, -1, -1);
+ private static final MethodAnnotation PARALLEL_STREAM_METHOD =
+ new MethodAnnotation(true, false, false, false, -1, -1);
+ private static final MethodAnnotation MISSING_FROM_LATEST_ANDROID_JAR =
+ new MethodAnnotation(false, true, false, false, -1, -1);
+
+ // ParallelStream methods are not supported when the runtime api level is strictly below 21.
+ final boolean parallelStreamMethod;
+ // Methods not in the latest android jar but still fully supported.
+ final boolean missingFromLatestAndroidJar;
+ // Methods not supported in a given min api range.
+ final boolean unsupportedInMinApiRange;
+ final boolean covariantReturnSupported;
+ final int minRange;
+ final int maxRange;
+
+ MethodAnnotation(
+ boolean parallelStreamMethod,
+ boolean missingFromLatestAndroidJar,
+ boolean covariantReturnSupported,
+ boolean unsupportedInMinApiRange,
+ int minRange,
+ int maxRange) {
+ this.parallelStreamMethod = parallelStreamMethod;
+ this.missingFromLatestAndroidJar = missingFromLatestAndroidJar;
+ this.covariantReturnSupported = covariantReturnSupported;
+ this.unsupportedInMinApiRange = unsupportedInMinApiRange;
+ this.minRange = minRange;
+ this.maxRange = maxRange;
+ }
+
+ public static MethodAnnotation getCovariantReturnSupported() {
+ return COVARIANT_RETURN_SUPPORTED;
+ }
+
+ public static MethodAnnotation getDefault() {
+ return DEFAULT;
+ }
+
+ public static MethodAnnotation getParallelStreamMethod() {
+ return PARALLEL_STREAM_METHOD;
+ }
+
+ public static MethodAnnotation getMissingFromLatestAndroidJar() {
+ return MISSING_FROM_LATEST_ANDROID_JAR;
+ }
+
+ public static MethodAnnotation createMissingInMinApi(int api) {
+ return new MethodAnnotation(false, false, false, true, api, api);
+ }
+
+ public boolean isUnsupportedInMinApiRange() {
+ return unsupportedInMinApiRange;
+ }
+
+ public int getMinRange() {
+ return minRange;
+ }
+
+ public int getMaxRange() {
+ return maxRange;
+ }
+
+ public boolean isCovariantReturnSupported() {
+ return covariantReturnSupported;
+ }
+
+ public MethodAnnotation combine(MethodAnnotation other) {
+ if (this == getDefault()) {
+ return other;
+ }
+ if (other == getDefault()) {
+ return this;
+ }
+ int newMin, newMax;
+ if (!unsupportedInMinApiRange && !other.unsupportedInMinApiRange) {
+ newMin = newMax = -1;
+ } else if (!unsupportedInMinApiRange || !other.unsupportedInMinApiRange) {
+ newMin = unsupportedInMinApiRange ? minRange : other.minRange;
+ newMax = unsupportedInMinApiRange ? maxRange : other.maxRange;
+ } else {
+ // Merge ranges if contiguous or throw.
+ if (maxRange == other.minRange - 1) {
+ newMin = minRange;
+ newMax = other.maxRange;
+ } else if (other.maxRange == minRange - 1) {
+ newMin = other.minRange;
+ newMax = maxRange;
+ } else {
+ // 20 is missing, so if maxRange or minRange are 19 the following is 21.
+ if (maxRange == 19 && other.minRange == 21) {
+ newMin = minRange;
+ newMax = other.maxRange;
+ } else if (other.maxRange == 19 && minRange == 21) {
+ newMin = other.minRange;
+ newMax = maxRange;
+ } else {
+ throw new RuntimeException("Cannot merge ranges.");
+ }
+ }
+ }
+ return new MethodAnnotation(
+ parallelStreamMethod || other.parallelStreamMethod,
+ missingFromLatestAndroidJar || other.missingFromLatestAndroidJar,
+ covariantReturnSupported || other.covariantReturnSupported,
+ unsupportedInMinApiRange || other.unsupportedInMinApiRange,
+ newMin,
+ newMax);
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedMethodsGenerator.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedMethodsGenerator.java
new file mode 100644
index 0000000..752262f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/lint/SupportedMethodsGenerator.java
@@ -0,0 +1,372 @@
+// Copyright (c) 2023, 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.desugar.desugaredlibrary.lint;
+
+import static com.android.tools.r8.ir.desugar.desugaredlibrary.lint.AbstractGenerateFiles.MAX_TESTED_ANDROID_API_LEVEL;
+
+import com.android.tools.r8.StringResource;
+import com.android.tools.r8.androidapi.ComputedApiLevel;
+import com.android.tools.r8.dex.ApplicationReader;
+import com.android.tools.r8.dex.Constants;
+import com.android.tools.r8.experimental.startup.StartupOrder;
+import com.android.tools.r8.features.ClassToFeatureSplitMap;
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.DexApplication;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DirectMappedDexApplication;
+import com.android.tools.r8.graph.MethodAccessFlags;
+import com.android.tools.r8.graph.MethodResolutionResult;
+import com.android.tools.r8.ir.desugar.BackportedMethodRewriter;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibraryAmender;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.ClassAnnotation;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses.MethodAnnotation;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.machinespecification.MachineDesugaredLibrarySpecification;
+import com.android.tools.r8.shaking.MainDexInfo;
+import com.android.tools.r8.synthesis.SyntheticItems.GlobalSyntheticsStrategy;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.AndroidApp;
+import com.android.tools.r8.utils.InternalOptions;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+public class SupportedMethodsGenerator {
+
+ private static final String ANDROID_JAR_PATTERN = "third_party/android_jar/lib-v%d/android.jar";
+
+ private final InternalOptions options;
+
+ public SupportedMethodsGenerator(InternalOptions options) {
+ this.options = options;
+ }
+
+ public SupportedClasses run(Collection<Path> desugaredLibraryImplementation, Path specification)
+ throws IOException {
+ SupportedClasses.Builder builder = SupportedClasses.builder();
+ // First analyze everything which is supported when desugaring for api 1.
+ collectSupportedMethodsInB(desugaredLibraryImplementation, specification, builder);
+ // Second annotate all apis which are partially and/or fully supported.
+ AndroidApp library =
+ AndroidApp.builder()
+ .addProgramFiles(getAndroidJarPath(MAX_TESTED_ANDROID_API_LEVEL))
+ .build();
+ DirectMappedDexApplication appForMax =
+ new ApplicationReader(library, options, Timing.empty()).read().toDirect();
+ annotateMethodsNotOnLatestAndroidJar(appForMax, builder);
+ annotateParallelMethods(builder);
+ annotatePartialDesugaringMethods(builder, specification);
+ annotateClasses(builder, appForMax);
+ return builder.build();
+ }
+
+ private void annotateClasses(
+ SupportedClasses.Builder builder, DirectMappedDexApplication appForMax) {
+
+ builder.forEachClassAndMethods(
+ (clazz, methods) -> {
+ DexClass maxClass = appForMax.definitionFor(clazz.type);
+ List<DexMethod> missing = new ArrayList<>();
+ boolean fullySupported = true;
+ for (DexEncodedMethod method : maxClass.methods()) {
+ if (!(method.isPublic() || method.isProtectedMethod())) {
+ continue;
+ }
+ // If the method is in android.jar but not in the desugared library, or annotated, then
+ // the class is not marked as fully supported.
+ if (methods.stream().noneMatch(em -> em.getReference() == method.getReference())) {
+ missing.add(method.getReference());
+ fullySupported = false;
+ }
+ MethodAnnotation methodAnnotation = builder.getMethodAnnotation(method.getReference());
+ if (methodAnnotation != null && !methodAnnotation.isCovariantReturnSupported()) {
+ fullySupported = false;
+ }
+ }
+ builder.annotateClass(clazz.type, new ClassAnnotation(fullySupported, missing));
+ });
+ }
+
+ private void annotatePartialDesugaringMethods(
+ SupportedClasses.Builder builder, Path specification) throws IOException {
+ for (int api = AndroidApiLevel.K.getLevel();
+ api <= MAX_TESTED_ANDROID_API_LEVEL.getLevel();
+ api++) {
+ if (api == 20) {
+ // Missing android.jar.
+ continue;
+ }
+ AndroidApiLevel androidApiLevel = AndroidApiLevel.getAndroidApiLevel(api);
+ AndroidApp library =
+ AndroidApp.builder().addProgramFiles(getAndroidJarPath(androidApiLevel)).build();
+ DirectMappedDexApplication dexApplication =
+ new ApplicationReader(library, options, Timing.empty()).read().toDirect();
+ AppInfoWithClassHierarchy appInfo =
+ AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
+ dexApplication,
+ ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
+ MainDexInfo.none(),
+ GlobalSyntheticsStrategy.forNonSynthesizing(),
+ StartupOrder.empty());
+ MachineDesugaredLibrarySpecification machineSpecification =
+ getMachineSpecification(androidApiLevel, specification);
+
+ options.setMinApiLevel(androidApiLevel);
+ options.resetDesugaredLibrarySpecificationForTesting();
+ options.setDesugaredLibrarySpecification(machineSpecification);
+ List<DexMethod> backports =
+ BackportedMethodRewriter.generateListOfBackportedMethods(
+ library, options, ThreadUtils.getExecutorService(1));
+
+ int finalApi = api;
+ builder.forEachClassAndMethod(
+ (clazz, encodedMethod) -> {
+ DexMethod dexMethod = encodedMethod.getReference();
+ if (machineSpecification.isSupported(dexMethod)
+ || backports.contains(dexMethod)
+ || machineSpecification.getCovariantRetarget().containsKey(dexMethod)) {
+ if (machineSpecification.getCovariantRetarget().containsKey(dexMethod)) {
+ builder.annotateMethod(dexMethod, MethodAnnotation.getCovariantReturnSupported());
+ }
+ return;
+ }
+ if (machineSpecification.getEmulatedInterfaces().containsKey(dexMethod.getHolderType())
+ && encodedMethod.isStatic()) {
+ // Static methods on emulated interfaces are always supported if the emulated
+ // interface is
+ // supported.
+ return;
+ }
+ MethodResolutionResult methodResolutionResult =
+ appInfo.resolveMethod(
+ dexMethod,
+ appInfo
+ .contextIndependentDefinitionFor(dexMethod.getHolderType())
+ .isInterface());
+ if (methodResolutionResult.isFailedResolution()) {
+ builder.annotateMethod(dexMethod, MethodAnnotation.createMissingInMinApi(finalApi));
+ }
+ });
+ }
+ }
+
+ private void annotateParallelMethods(SupportedClasses.Builder builder) {
+ for (DexMethod parallelMethod : getParallelMethods()) {
+ builder.annotateMethodIfPresent(parallelMethod, MethodAnnotation.getParallelStreamMethod());
+ }
+ }
+
+ private void annotateMethodsNotOnLatestAndroidJar(
+ DirectMappedDexApplication appForMax, SupportedClasses.Builder builder) {
+ builder.forEachClassAndMethod(
+ (clazz, method) -> {
+ DexClass dexClass = appForMax.definitionFor(clazz.type);
+ assert dexClass != null;
+ if (dexClass.lookupMethod(method.getReference()) == null) {
+ builder.annotateMethod(
+ method.getReference(), MethodAnnotation.getMissingFromLatestAndroidJar());
+ }
+ });
+ }
+
+ static Path getAndroidJarPath(AndroidApiLevel apiLevel) {
+ String jar =
+ apiLevel == AndroidApiLevel.MASTER
+ ? "third_party/android_jar/lib-master/android.jar"
+ : String.format(ANDROID_JAR_PATTERN, apiLevel.getLevel());
+ return Paths.get(jar);
+ }
+
+ private void collectSupportedMethodsInB(
+ Collection<Path> desugaredLibraryImplementation,
+ Path specification,
+ SupportedClasses.Builder builder)
+ throws IOException {
+
+ AndroidApp implementation =
+ AndroidApp.builder().addProgramFiles(desugaredLibraryImplementation).build();
+ DirectMappedDexApplication implementationApplication =
+ new ApplicationReader(implementation, options, Timing.empty()).read().toDirect();
+
+ AndroidApp library =
+ AndroidApp.builder()
+ .addLibraryFiles(getAndroidJarPath(MAX_TESTED_ANDROID_API_LEVEL))
+ .build();
+ DirectMappedDexApplication amendedAppForMax =
+ new ApplicationReader(library, options, Timing.empty()).read().toDirect();
+
+ MachineDesugaredLibrarySpecification machineSpecification =
+ getMachineSpecification(AndroidApiLevel.B, specification);
+
+ options.setMinApiLevel(AndroidApiLevel.B);
+ options.resetDesugaredLibrarySpecificationForTesting();
+ options.setDesugaredLibrarySpecification(machineSpecification);
+ List<DexMethod> backports =
+ BackportedMethodRewriter.generateListOfBackportedMethods(
+ library, options, ThreadUtils.getExecutorService(1));
+
+ DesugaredLibraryAmender.run(
+ machineSpecification.getAmendLibraryMethods(),
+ machineSpecification.getAmendLibraryFields(),
+ amendedAppForMax,
+ options.reporter,
+ ComputedApiLevel.unknown());
+
+ for (DexProgramClass clazz : implementationApplication.classes()) {
+ // All emulated interfaces static and default methods are supported.
+ if (machineSpecification.getEmulatedInterfaces().containsKey(clazz.type)) {
+ assert clazz.isInterface();
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (!method.isDefaultMethod() && !method.isStatic()) {
+ continue;
+ }
+ if (method.getName().startsWith("lambda$")
+ || method.getName().toString().contains("$deserializeLambda$")) {
+ // We don't care if lambda methods are present or not.
+ continue;
+ }
+ if (method
+ .getReference()
+ .toSourceString()
+ .equals("void java.util.Collection.forEach(java.util.function.Consumer)")) {
+ // This method is present for binary compatibility. Do not mark as supported (Supported
+ // through Iterable#forEach).
+ continue;
+ }
+ builder.addSupportedMethod(clazz, method);
+ }
+ addBackports(clazz, backports, builder, amendedAppForMax);
+ } else {
+ // All methods in maintained or rewritten classes are supported.
+ if ((clazz.accessFlags.isPublic() || clazz.accessFlags.isProtected())
+ && machineSpecification.isContextTypeMaintainedOrRewritten(clazz.type)
+ && amendedAppForMax.definitionFor(clazz.type) != null) {
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (!method.isPublic() && !method.isProtectedMethod()) {
+ continue;
+ }
+ builder.addSupportedMethod(clazz, method);
+ }
+ addBackports(clazz, backports, builder, amendedAppForMax);
+ }
+ }
+ }
+
+ // All retargeted methods are supported.
+ machineSpecification.forEachRetargetMethod(
+ method -> {
+ DexClass dexClass = implementationApplication.definitionFor(method.getHolderType());
+ if (dexClass != null) {
+ DexEncodedMethod dexEncodedMethod = dexClass.lookupMethod(method);
+ if (dexEncodedMethod != null) {
+ builder.addSupportedMethod(dexClass, dexEncodedMethod);
+ return;
+ }
+ }
+ dexClass = amendedAppForMax.definitionFor(method.getHolderType());
+ DexEncodedMethod dexEncodedMethod = dexClass.lookupMethod(method);
+ assert dexEncodedMethod != null;
+ builder.addSupportedMethod(dexClass, dexEncodedMethod);
+ });
+ }
+
+ private void addBackports(
+ DexProgramClass clazz,
+ List<DexMethod> backports,
+ SupportedClasses.Builder builder,
+ DirectMappedDexApplication amendedAppForMax) {
+ for (DexMethod backport : backports) {
+ if (clazz.type == backport.getHolderType()) {
+ DexClass maxClass = amendedAppForMax.definitionFor(clazz.type);
+ DexEncodedMethod dexEncodedMethod = maxClass.lookupMethod(backport);
+ // There is a single backport not in amendedAppForMax, Stream#ofNullable.
+ assert dexEncodedMethod != null
+ || backport
+ .toString()
+ .equals(
+ "java.util.stream.Stream java.util.stream.Stream.ofNullable(java.lang.Object)");
+ if (dexEncodedMethod == null) {
+ dexEncodedMethod =
+ DexEncodedMethod.builder()
+ .setMethod(backport)
+ .setAccessFlags(
+ MethodAccessFlags.fromSharedAccessFlags(
+ Constants.ACC_PUBLIC | Constants.ACC_STATIC, false))
+ .build();
+ }
+ builder.addSupportedMethod(clazz, dexEncodedMethod);
+ }
+ }
+ }
+
+ private MachineDesugaredLibrarySpecification getMachineSpecification(
+ AndroidApiLevel api, Path specification) throws IOException {
+ DesugaredLibrarySpecification librarySpecification =
+ DesugaredLibrarySpecificationParser.parseDesugaredLibrarySpecification(
+ StringResource.fromFile(specification),
+ options.itemFactory,
+ options.reporter,
+ false,
+ api.getLevel());
+ Path androidJarPath = getAndroidJarPath(librarySpecification.getRequiredCompilationApiLevel());
+ DexApplication app = createLoadingApp(androidJarPath, options);
+ return librarySpecification.toMachineSpecification(app, Timing.empty());
+ }
+
+ private DexApplication createLoadingApp(Path androidLib, InternalOptions options)
+ throws IOException {
+ AndroidApp.Builder builder = AndroidApp.builder();
+ AndroidApp inputApp = builder.addLibraryFiles(androidLib).build();
+ ApplicationReader applicationReader = new ApplicationReader(inputApp, options, Timing.empty());
+ ExecutorService executorService = ThreadUtils.getExecutorService(options);
+ assert !options.ignoreJavaLibraryOverride;
+ options.ignoreJavaLibraryOverride = true;
+ DexApplication loadingApp = applicationReader.read(executorService);
+ options.ignoreJavaLibraryOverride = false;
+ return loadingApp;
+ }
+
+ private Set<DexMethod> getParallelMethods() {
+ Set<DexMethod> parallelMethods = Sets.newIdentityHashSet();
+ DexItemFactory factory = options.dexItemFactory();
+ DexType streamType = factory.createType(factory.createString("Ljava/util/stream/Stream;"));
+ DexMethod parallelMethod =
+ factory.createMethod(
+ factory.collectionType,
+ factory.createProto(streamType),
+ factory.createString("parallelStream"));
+ parallelMethods.add(parallelMethod);
+ DexType baseStreamType =
+ factory.createType(factory.createString("Ljava/util/stream/BaseStream;"));
+ for (String typePrefix : new String[] {"Base", "Double", "Int", "Long"}) {
+ streamType =
+ factory.createType(factory.createString("Ljava/util/stream/" + typePrefix + "Stream;"));
+ parallelMethod =
+ factory.createMethod(
+ streamType, factory.createProto(streamType), factory.createString("parallel"));
+ parallelMethods.add(parallelMethod);
+ // Also filter out the generated bridges for the covariant return type.
+ parallelMethod =
+ factory.createMethod(
+ streamType, factory.createProto(baseStreamType), factory.createString("parallel"));
+ parallelMethods.add(parallelMethod);
+ }
+ return parallelMethods;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
index b288d9a..2ac41e4 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/icce/AlwaysThrowingInstructionDesugaring.java
@@ -162,9 +162,8 @@
// pop exception result
// [push fake result for <method>]
UtilityMethodForCodeOptimizations throwMethod =
- methodSynthesizerConsumer.synthesizeMethod(appView, methodProcessingContext);
+ methodSynthesizerConsumer.synthesizeMethod(appView, eventConsumer, methodProcessingContext);
ProgramMethod throwProgramMethod = throwMethod.uncheckedGetMethod();
- eventConsumer.acceptThrowMethod(throwProgramMethod, context);
ArrayList<CfInstruction> replacement = new ArrayList<>();
DexTypeList parameters = invoke.getMethod().getParameters();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
index 0c5ed70..32e2c37 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ClassProcessor.java
@@ -807,15 +807,7 @@
}
for (Wrapper<DexMethod> wrapper : signatures.signatures) {
resolveForwardForSignature(
- clazz,
- wrapper.get(),
- (target, forward) -> {
- if (isLiveMethod(target) && !superInfo.isTargetedByForwards(target)) {
- additionalForwards.add(target);
- addForwardingMethod(target, forward, clazz);
- }
- },
- eventConsumer);
+ clazz, superInfo, additionalForwards, wrapper.get(), eventConsumer);
}
}
@@ -823,8 +815,9 @@
// the 'addForward' call-back is called with the target of the forward.
private void resolveForwardForSignature(
DexClass clazz,
+ ClassInfo superInfo,
+ Builder<DexClassAndMethod> additionalForwards,
DexMethod method,
- BiConsumer<DexClassAndMethod, DexMethod> addForward,
InterfaceProcessingDesugaringEventConsumer eventConsumer) {
AppInfoWithClassHierarchy appInfo = appView.appInfoForDesugaring();
MethodResolutionResult resolutionResult = appInfo.resolveMethodOnLegacy(clazz, method);
@@ -891,23 +884,30 @@
DexClassAndMethod virtualDispatchTarget = lookupMethodTarget.getTarget();
assert virtualDispatchTarget != null;
- // If resolution targets a default interface method, forward it.
- if (virtualDispatchTarget.isDefaultMethod()) {
- addForward.accept(
- virtualDispatchTarget,
- helper
- .ensureDefaultAsMethodOfCompanionClassStub(virtualDispatchTarget, eventConsumer)
- .getReference());
+ if (!isLiveMethod(virtualDispatchTarget)
+ || superInfo.isTargetedByForwards(virtualDispatchTarget)) {
return;
}
- DerivedMethod forwardingMethod =
- helper.computeEmulatedInterfaceForwardingMethod(
- virtualDispatchTarget.getHolder(), virtualDispatchTarget);
- if (forwardingMethod != null) {
- DexMethod concreteForwardingMethod =
- helper.ensureEmulatedInterfaceForwardingMethod(forwardingMethod, eventConsumer);
- addForward.accept(virtualDispatchTarget, concreteForwardingMethod);
+ // If resolution targets a default interface method, forward it.
+ DexMethod forward = null;
+ if (virtualDispatchTarget.isDefaultMethod()) {
+ forward =
+ helper
+ .ensureDefaultAsMethodOfCompanionClassStub(virtualDispatchTarget, eventConsumer)
+ .getReference();
+ } else {
+ DerivedMethod forwardingMethod =
+ helper.computeEmulatedInterfaceForwardingMethod(
+ virtualDispatchTarget.getHolder(), virtualDispatchTarget);
+ if (forwardingMethod != null) {
+ forward = helper.ensureEmulatedInterfaceForwardingMethod(forwardingMethod, eventConsumer);
+ }
+ }
+
+ if (forward != null) {
+ additionalForwards.add(virtualDispatchTarget);
+ addForwardingMethod(virtualDispatchTarget, forward, clazz);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
index afae01d..2140c13 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaring.java
@@ -48,6 +48,7 @@
import com.android.tools.r8.ir.desugar.FreshLocalProvider;
import com.android.tools.r8.ir.desugar.LocalStackAllocator;
import com.android.tools.r8.ir.desugar.ProgramAdditions;
+import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordClassSynthesizerDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.records.RecordRewriterHelper.RecordInvokeDynamic;
import com.android.tools.r8.ir.synthetic.CallObjectInitCfCodeProvider;
@@ -59,7 +60,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.BiFunction;
import org.objectweb.asm.Opcodes;
@@ -103,22 +103,26 @@
CfCode cfCode = method.getDefinition().getCode().asCfCode();
for (CfInstruction instruction : cfCode.getInstructions()) {
if (instruction.isInvokeDynamic() && needsDesugaring(instruction, method)) {
- prepareInvokeDynamicOnRecord(instruction.asInvokeDynamic(), programAdditions, method);
+ prepareInvokeDynamicOnRecord(
+ instruction.asInvokeDynamic(), programAdditions, method, eventConsumer);
}
}
}
private void prepareInvokeDynamicOnRecord(
- CfInvokeDynamic invokeDynamic, ProgramAdditions programAdditions, ProgramMethod context) {
+ CfInvokeDynamic invokeDynamic,
+ ProgramAdditions programAdditions,
+ ProgramMethod context,
+ RecordInstructionDesugaringEventConsumer eventConsumer) {
RecordInvokeDynamic recordInvokeDynamic =
parseInvokeDynamicOnRecord(invokeDynamic, appView, context);
if (recordInvokeDynamic.getMethodName() == factory.toStringMethodName
|| recordInvokeDynamic.getMethodName() == factory.hashCodeMethodName) {
- ensureGetFieldsAsObjects(recordInvokeDynamic, programAdditions);
+ ensureGetFieldsAsObjects(recordInvokeDynamic, programAdditions, context, eventConsumer);
return;
}
if (recordInvokeDynamic.getMethodName() == factory.equalsMethodName) {
- ensureEqualsRecord(recordInvokeDynamic, programAdditions);
+ ensureEqualsRecord(recordInvokeDynamic, programAdditions, context, eventConsumer);
return;
}
throw new Unreachable("Invoke dynamic needs record desugaring but could not be desugared.");
@@ -207,11 +211,19 @@
parseInvokeDynamicOnRecord(invokeDynamic, appView, context);
if (recordInvokeDynamic.getMethodName() == factory.toStringMethodName) {
return desugarInvokeRecordToString(
- recordInvokeDynamic, localStackAllocator, eventConsumer, methodProcessingContext);
+ recordInvokeDynamic,
+ localStackAllocator,
+ eventConsumer,
+ context,
+ methodProcessingContext);
}
if (recordInvokeDynamic.getMethodName() == factory.hashCodeMethodName) {
return desugarInvokeRecordHashCode(
- recordInvokeDynamic, localStackAllocator, eventConsumer, methodProcessingContext);
+ recordInvokeDynamic,
+ localStackAllocator,
+ eventConsumer,
+ context,
+ methodProcessingContext);
}
if (recordInvokeDynamic.getMethodName() == factory.equalsMethodName) {
return desugarInvokeRecordEquals(recordInvokeDynamic);
@@ -251,24 +263,37 @@
}
private DexMethod ensureEqualsRecord(
- RecordInvokeDynamic recordInvokeDynamic, ProgramAdditions programAdditions) {
- DexMethod getFieldsAsObjects = ensureGetFieldsAsObjects(recordInvokeDynamic, programAdditions);
+ RecordInvokeDynamic recordInvokeDynamic,
+ ProgramAdditions programAdditions,
+ ProgramMethod context,
+ RecordInstructionDesugaringEventConsumer eventConsumer) {
+ DexMethod getFieldsAsObjects =
+ ensureGetFieldsAsObjects(recordInvokeDynamic, programAdditions, context, eventConsumer);
DexProgramClass clazz = recordInvokeDynamic.getRecordClass();
DexMethod method = equalsRecordMethod(clazz.type);
assert clazz.lookupProgramMethod(method) == null;
- programAdditions.ensureMethod(
- method, () -> synthesizeEqualsRecordMethod(clazz, getFieldsAsObjects, method));
+ ProgramMethod equalsHelperMethod =
+ programAdditions.ensureMethod(
+ method, () -> synthesizeEqualsRecordMethod(clazz, getFieldsAsObjects, method));
+ eventConsumer.acceptRecordEqualsHelperMethod(equalsHelperMethod, context);
return method;
}
private DexMethod ensureGetFieldsAsObjects(
- RecordInvokeDynamic recordInvokeDynamic, ProgramAdditions programAdditions) {
+ RecordInvokeDynamic recordInvokeDynamic,
+ ProgramAdditions programAdditions,
+ ProgramMethod context,
+ RecordInstructionDesugaringEventConsumer eventConsumer) {
DexProgramClass clazz = recordInvokeDynamic.getRecordClass();
DexMethod method = getFieldsAsObjectsMethod(clazz.type);
assert clazz.lookupProgramMethod(method) == null;
- programAdditions.ensureMethod(
- method,
- () -> synthesizeGetFieldsAsObjectsMethod(clazz, recordInvokeDynamic.getFields(), method));
+ ProgramMethod getFieldsAsObjectsHelperMethod =
+ programAdditions.ensureMethod(
+ method,
+ () ->
+ synthesizeGetFieldsAsObjectsMethod(clazz, recordInvokeDynamic.getFields(), method));
+ eventConsumer.acceptRecordGetFieldsAsObjectsHelperMethod(
+ getFieldsAsObjectsHelperMethod, context);
return method;
}
@@ -306,6 +331,7 @@
RecordInvokeDynamic recordInvokeDynamic,
LocalStackAllocator localStackAllocator,
RecordInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
MethodProcessingContext methodProcessingContext) {
localStackAllocator.allocateLocalStack(1);
DexMethod getFieldsAsObjects = getFieldsAsObjectsMethod(recordInvokeDynamic.getRecordType());
@@ -320,7 +346,7 @@
recordHashCodeHelperProto,
RecordCfMethods::RecordMethods_hashCode,
methodProcessingContext);
- eventConsumer.acceptRecordMethod(programMethod);
+ eventConsumer.acceptRecordHashCodeHelperMethod(programMethod, context);
instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, programMethod.getReference(), false));
return instructions;
}
@@ -335,6 +361,7 @@
RecordInvokeDynamic recordInvokeDynamic,
LocalStackAllocator localStackAllocator,
RecordInstructionDesugaringEventConsumer eventConsumer,
+ ProgramMethod context,
MethodProcessingContext methodProcessingContext) {
localStackAllocator.allocateLocalStack(2);
DexMethod getFieldsAsObjects = getFieldsAsObjectsMethod(recordInvokeDynamic.getRecordType());
@@ -357,7 +384,7 @@
recordToStringHelperProto,
RecordCfMethods::RecordMethods_toString,
methodProcessingContext);
- eventConsumer.acceptRecordMethod(programMethod);
+ eventConsumer.acceptRecordToStringHelperMethod(programMethod, context);
instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, programMethod.getReference(), false));
return instructions;
}
@@ -374,6 +401,21 @@
return false;
}
+ private void ensureRecordClass(
+ RecordInstructionDesugaringEventConsumer eventConsumer, ProgramMethod context) {
+ DexProgramClass recordTagClass =
+ internalEnsureRecordClass(eventConsumer, ImmutableList.of(context));
+ eventConsumer.acceptRecordClassContext(recordTagClass, context);
+ }
+
+ private void ensureRecordClass(
+ RecordClassSynthesizerDesugaringEventConsumer eventConsumer,
+ Collection<DexProgramClass> recordClasses) {
+ DexProgramClass recordTagClass = internalEnsureRecordClass(eventConsumer, recordClasses);
+ recordClasses.forEach(
+ recordClass -> eventConsumer.acceptRecordClassContext(recordTagClass, recordClass));
+ }
+
/**
* If java.lang.Record is referenced from a class' supertype or a program method/field signature,
* then the global synthetic is generated upfront of the compilation to avoid confusing D8/R8.
@@ -382,11 +424,12 @@
* contains "x instance of java.lang.Record" but no record type is present, then the global
* synthetic is generated during instruction desugaring scanning.
*/
- private void ensureRecordClass(
- RecordDesugaringEventConsumer eventConsumer, Collection<ProgramDefinition> contexts) {
+ private DexProgramClass internalEnsureRecordClass(
+ RecordDesugaringEventConsumer eventConsumer,
+ Collection<? extends ProgramDefinition> contexts) {
DexItemFactory factory = appView.dexItemFactory();
checkRecordTagNotPresent(factory);
- appView
+ return appView
.getSyntheticItems()
.ensureGlobalClass(
() -> new MissingGlobalSyntheticsConsumerDiagnostic("Record desugaring"),
@@ -401,11 +444,6 @@
eventConsumer::acceptRecordClass);
}
- private void ensureRecordClass(
- RecordDesugaringEventConsumer eventConsumer, ProgramDefinition context) {
- ensureRecordClass(eventConsumer, ImmutableList.of(context));
- }
-
private void checkRecordTagNotPresent(DexItemFactory factory) {
DexClass r8RecordClass =
appView.appInfo().definitionForWithoutExistenceAssert(factory.recordTagType);
@@ -505,7 +543,7 @@
CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
DexApplicationReadFlags flags = appView.appInfo().app().getFlags();
if (flags.hasReadRecordReferenceFromProgramClass()) {
- List<ProgramDefinition> classes = new ArrayList<>();
+ List<DexProgramClass> classes = new ArrayList<>(flags.getRecordWitnesses().size());
for (DexType recordWitness : flags.getRecordWitnesses()) {
DexClass dexClass = appView.contextIndependentDefinitionFor(recordWitness);
assert dexClass != null;
@@ -520,8 +558,7 @@
public void postProcessingDesugaring(
Collection<DexProgramClass> programClasses,
CfPostProcessingDesugaringEventConsumer eventConsumer,
- ExecutorService executorService)
- throws ExecutionException {
+ ExecutorService executorService) {
for (DexProgramClass clazz : programClasses) {
if (clazz.isRecord()) {
assert clazz.superType == factory.recordType;
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaringEventConsumer.java
index f81b5d6..469e0de 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/records/RecordDesugaringEventConsumer.java
@@ -8,10 +8,23 @@
public interface RecordDesugaringEventConsumer {
- void acceptRecordClass(DexProgramClass recordClass);
+ void acceptRecordClass(DexProgramClass recordTagClass);
+
+ interface RecordClassSynthesizerDesugaringEventConsumer extends RecordDesugaringEventConsumer {
+
+ void acceptRecordClassContext(DexProgramClass recordTagClass, DexProgramClass recordClass);
+ }
interface RecordInstructionDesugaringEventConsumer extends RecordDesugaringEventConsumer {
- void acceptRecordMethod(ProgramMethod method);
+ void acceptRecordClassContext(DexProgramClass recordTagClass, ProgramMethod context);
+
+ void acceptRecordEqualsHelperMethod(ProgramMethod method, ProgramMethod context);
+
+ void acceptRecordGetFieldsAsObjectsHelperMethod(ProgramMethod method, ProgramMethod context);
+
+ void acceptRecordHashCodeHelperMethod(ProgramMethod method, ProgramMethod context);
+
+ void acceptRecordToStringHelperMethod(ProgramMethod method, ProgramMethod context);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index dbd19bd..509d6c1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -1495,7 +1495,7 @@
// Replace the check-cast instruction by throwClassCastExceptionIfNotNull().
UtilityMethodForCodeOptimizations throwClassCastExceptionIfNotNullMethod =
UtilityMethodsForCodeOptimizations.synthesizeThrowClassCastExceptionIfNotNullMethod(
- appView, methodProcessingContext);
+ appView, methodProcessor.getEventConsumer(), methodProcessingContext);
throwClassCastExceptionIfNotNullMethod.optimize(methodProcessor);
InvokeStatic replacement =
InvokeStatic.builder()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
index 92e653d..b7d7b10 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -19,11 +19,12 @@
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.code.ValueIsDeadAnalysis;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Box;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterators;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
@@ -54,11 +55,12 @@
// more dead instructions.
Deque<BasicBlock> worklist = new ArrayDeque<>();
do {
+ ValueIsDeadAnalysis valueIsDeadAnalysis = new ValueIsDeadAnalysis(appView, code);
worklist.addAll(code.topologicallySortedBlocks());
while (!worklist.isEmpty()) {
BasicBlock block = worklist.removeLast();
- removeDeadInstructions(worklist, code, block);
- removeDeadPhis(worklist, code, block);
+ removeDeadInstructions(worklist, code, block, valueIsDeadAnalysis);
+ removeDeadPhis(worklist, block, valueIsDeadAnalysis);
}
} while (codeRewriter.simplifyIf(code).anySimplifications()
|| removeUnneededCatchHandlers(code));
@@ -70,8 +72,9 @@
public boolean verifyNoDeadCode(IRCode code) {
assert !codeRewriter.rewriteMoveResult(code);
assert !removeUnneededCatchHandlers(code);
+ ValueIsDeadAnalysis valueIsDeadAnalysis = new ValueIsDeadAnalysis(appView, code);
for (BasicBlock block : code.blocks) {
- assert !block.hasDeadPhi(appView, code);
+ assert !valueIsDeadAnalysis.hasDeadPhi(block);
for (Instruction instruction : block.getInstructions()) {
// No unused move-result instructions.
assert !instruction.isInvoke()
@@ -79,7 +82,7 @@
|| instruction.outValue().hasAnyUsers();
// No dead instructions.
assert !instruction.canBeDeadCode(appView, code).isDeadIfOutValueIsDead()
- || (instruction.hasOutValue() && !instruction.outValue().isDead(appView, code));
+ || (instruction.hasOutValue() && !valueIsDeadAnalysis.isDead(instruction.outValue()));
}
}
return true;
@@ -108,11 +111,12 @@
}
}
- private void removeDeadPhis(Queue<BasicBlock> worklist, IRCode code, BasicBlock block) {
+ private void removeDeadPhis(
+ Queue<BasicBlock> worklist, BasicBlock block, ValueIsDeadAnalysis valueIsDeadAnalysis) {
Iterator<Phi> phiIt = block.getPhis().iterator();
while (phiIt.hasNext()) {
Phi phi = phiIt.next();
- if (phi.isDead(appView, code)) {
+ if (valueIsDeadAnalysis.isDead(phi)) {
phiIt.remove();
for (Value operand : phi.getOperands()) {
operand.removePhiUser(phi);
@@ -122,7 +126,11 @@
}
}
- private void removeDeadInstructions(Queue<BasicBlock> worklist, IRCode code, BasicBlock block) {
+ private void removeDeadInstructions(
+ Queue<BasicBlock> worklist,
+ IRCode code,
+ BasicBlock block,
+ ValueIsDeadAnalysis valueIsDeadAnalysis) {
InstructionListIterator iterator = block.listIterator(code, block.getInstructions().size());
while (iterator.hasPrevious()) {
Instruction current = iterator.previous();
@@ -165,7 +173,7 @@
if (deadInstructionResult.isMaybeDead()) {
boolean satisfied = true;
for (Value valueRequiredToBeDead : deadInstructionResult.getValuesRequiredToBeDead()) {
- if (!valueRequiredToBeDead.isDead(appView, code)) {
+ if (!valueIsDeadAnalysis.isDead(valueRequiredToBeDead)) {
satisfied = false;
break;
}
@@ -175,7 +183,7 @@
}
}
Value outValue = current.outValue();
- if (outValue != null && !outValue.isDead(appView, code)) {
+ if (outValue != null && !valueIsDeadAnalysis.isDead(outValue)) {
continue;
}
updateWorklist(worklist, current);
@@ -291,21 +299,12 @@
}
@Override
- public boolean isDeadIfInValueIsDead() {
- return true;
- }
-
- @Override
public Iterable<Value> getValuesRequiredToBeDead() {
- return () -> Iterators.singletonIterator(inValueRequiredToBeDead);
+ return IterableUtils.singleton(inValueRequiredToBeDead);
}
};
}
- public boolean isDeadIfInValueIsDead() {
- return false;
- }
-
public boolean isDeadIfOutValueIsDead() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
index d4d8601..e1367b8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizations.java
@@ -23,11 +23,15 @@
public interface MethodSynthesizerConsumer {
UtilityMethodForCodeOptimizations synthesizeMethod(
- AppView<?> appView, MethodProcessingContext methodProcessingContext);
+ AppView<?> appView,
+ UtilityMethodsForCodeOptimizationsEventConsumer eventConsumer,
+ MethodProcessingContext methodProcessingContext);
}
public static UtilityMethodForCodeOptimizations synthesizeToStringIfNotNullMethod(
- AppView<?> appView, MethodProcessingContext methodProcessingContext) {
+ AppView<?> appView,
+ UtilityMethodsForCodeOptimizationsEventConsumer eventConsumer,
+ MethodProcessingContext methodProcessingContext) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexProto proto = dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.objectType);
SyntheticItems syntheticItems = appView.getSyntheticItems();
@@ -44,6 +48,8 @@
.setApiLevelForCode(appView.computedMinApiLevel())
.setCode(method -> getToStringIfNotNullCodeTemplate(method, dexItemFactory))
.setProto(proto));
+ eventConsumer.acceptUtilityToStringIfNotNullMethod(
+ syntheticMethod, methodProcessingContext.getMethodContext());
return new UtilityMethodForCodeOptimizations(syntheticMethod);
}
@@ -54,7 +60,9 @@
}
public static UtilityMethodForCodeOptimizations synthesizeThrowClassCastExceptionIfNotNullMethod(
- AppView<?> appView, MethodProcessingContext methodProcessingContext) {
+ AppView<?> appView,
+ UtilityMethodsForCodeOptimizationsEventConsumer eventConsumer,
+ MethodProcessingContext methodProcessingContext) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexProto proto = dexItemFactory.createProto(dexItemFactory.voidType, dexItemFactory.objectType);
SyntheticItems syntheticItems = appView.getSyntheticItems();
@@ -74,6 +82,8 @@
method ->
getThrowClassCastExceptionIfNotNullCodeTemplate(method, dexItemFactory))
.setProto(proto));
+ eventConsumer.acceptUtilityThrowClassCastExceptionIfNotNullMethod(
+ syntheticMethod, methodProcessingContext.getMethodContext());
return new UtilityMethodForCodeOptimizations(syntheticMethod);
}
@@ -85,7 +95,9 @@
}
public static UtilityMethodForCodeOptimizations synthesizeThrowIllegalAccessErrorMethod(
- AppView<?> appView, MethodProcessingContext methodProcessingContext) {
+ AppView<?> appView,
+ UtilityMethodsForCodeOptimizationsEventConsumer eventConsumer,
+ MethodProcessingContext methodProcessingContext) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexProto proto = dexItemFactory.createProto(dexItemFactory.illegalAccessErrorType);
SyntheticItems syntheticItems = appView.getSyntheticItems();
@@ -103,6 +115,8 @@
.setCode(
method -> getThrowIllegalAccessErrorCodeTemplate(method, dexItemFactory))
.setProto(proto));
+ eventConsumer.acceptUtilityThrowIllegalAccessErrorMethod(
+ syntheticMethod, methodProcessingContext.getMethodContext());
return new UtilityMethodForCodeOptimizations(syntheticMethod);
}
@@ -114,7 +128,9 @@
}
public static UtilityMethodForCodeOptimizations synthesizeThrowIncompatibleClassChangeErrorMethod(
- AppView<?> appView, MethodProcessingContext methodProcessingContext) {
+ AppView<?> appView,
+ UtilityMethodsForCodeOptimizationsEventConsumer eventConsumer,
+ MethodProcessingContext methodProcessingContext) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexProto proto = dexItemFactory.createProto(dexItemFactory.icceType);
SyntheticItems syntheticItems = appView.getSyntheticItems();
@@ -134,6 +150,8 @@
getThrowIncompatibleClassChangeErrorCodeTemplate(
method, dexItemFactory))
.setProto(proto));
+ eventConsumer.acceptUtilityThrowIncompatibleClassChangeErrorMethod(
+ syntheticMethod, methodProcessingContext.getMethodContext());
return new UtilityMethodForCodeOptimizations(syntheticMethod);
}
@@ -145,7 +163,9 @@
}
public static UtilityMethodForCodeOptimizations synthesizeThrowNoSuchMethodErrorMethod(
- AppView<?> appView, MethodProcessingContext methodProcessingContext) {
+ AppView<?> appView,
+ UtilityMethodsForCodeOptimizationsEventConsumer eventConsumer,
+ MethodProcessingContext methodProcessingContext) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexProto proto = dexItemFactory.createProto(dexItemFactory.noSuchMethodErrorType);
SyntheticItems syntheticItems = appView.getSyntheticItems();
@@ -163,6 +183,8 @@
.setCode(
method -> getThrowNoSuchMethodErrorCodeTemplate(method, dexItemFactory))
.setProto(proto));
+ eventConsumer.acceptUtilityThrowNoSuchMethodErrorMethod(
+ syntheticMethod, methodProcessingContext.getMethodContext());
return new UtilityMethodForCodeOptimizations(syntheticMethod);
}
@@ -174,7 +196,9 @@
}
public static UtilityMethodForCodeOptimizations synthesizeThrowRuntimeExceptionWithMessageMethod(
- AppView<?> appView, MethodProcessingContext methodProcessingContext) {
+ AppView<?> appView,
+ UtilityMethodsForCodeOptimizationsEventConsumer eventConsumer,
+ MethodProcessingContext methodProcessingContext) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
DexProto proto =
dexItemFactory.createProto(dexItemFactory.runtimeExceptionType, dexItemFactory.stringType);
@@ -194,6 +218,8 @@
method ->
getThrowRuntimeExceptionWithMessageCodeTemplate(method, dexItemFactory))
.setProto(proto));
+ eventConsumer.acceptUtilityThrowRuntimeExceptionWithMessageMethod(
+ syntheticMethod, methodProcessingContext.getMethodContext());
return new UtilityMethodForCodeOptimizations(syntheticMethod);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizationsEventConsumer.java b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizationsEventConsumer.java
new file mode 100644
index 0000000..90bad5f
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/UtilityMethodsForCodeOptimizationsEventConsumer.java
@@ -0,0 +1,25 @@
+// Copyright (c) 2023, 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.graph.ProgramMethod;
+
+public interface UtilityMethodsForCodeOptimizationsEventConsumer {
+
+ void acceptUtilityToStringIfNotNullMethod(ProgramMethod method, ProgramMethod context);
+
+ void acceptUtilityThrowClassCastExceptionIfNotNullMethod(
+ ProgramMethod method, ProgramMethod context);
+
+ void acceptUtilityThrowIllegalAccessErrorMethod(ProgramMethod method, ProgramMethod context);
+
+ void acceptUtilityThrowIncompatibleClassChangeErrorMethod(
+ ProgramMethod method, ProgramMethod context);
+
+ void acceptUtilityThrowNoSuchMethodErrorMethod(ProgramMethod method, ProgramMethod context);
+
+ void acceptUtilityThrowRuntimeExceptionWithMessageMethod(
+ ProgramMethod method, ProgramMethod context);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InstanceInitializerOutliner.java b/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutliner.java
similarity index 92%
rename from src/main/java/com/android/tools/r8/ir/optimize/InstanceInitializerOutliner.java
rename to src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutliner.java
index cbff832..cd94f57 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InstanceInitializerOutliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutliner.java
@@ -1,8 +1,8 @@
-// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2023, 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;
+package com.android.tools.r8.ir.optimize.api;
import com.android.tools.r8.androidapi.ComputedApiLevel;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
@@ -25,6 +25,7 @@
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.conversion.MethodProcessor;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.synthetic.ForwardMethodBuilder;
import com.android.tools.r8.ir.synthetic.NewInstanceSourceCode;
@@ -58,7 +59,10 @@
}
public void rewriteInstanceInitializers(
- IRCode code, ProgramMethod context, MethodProcessingContext methodProcessingContext) {
+ IRCode code,
+ ProgramMethod context,
+ MethodProcessor methodProcessor,
+ MethodProcessingContext methodProcessingContext) {
// Do not outline from already synthesized methods.
if (context.getDefinition().isD8R8Synthesized()) {
return;
@@ -96,7 +100,10 @@
}
DexEncodedMethod synthesizedInstanceInitializer =
createSynthesizedInstanceInitializer(
- invokeDirect.getInvokedMethod(), apiReferenceLevel, methodProcessingContext);
+ invokeDirect.getInvokedMethod(),
+ apiReferenceLevel,
+ methodProcessor,
+ methodProcessingContext);
List<Value> arguments = instruction.inValues();
InvokeStatic outlinedMethodInvoke =
InvokeStatic.builder()
@@ -134,7 +141,7 @@
assert classApiLevel.isKnownApiLevel();
DexEncodedMethod synthesizedNewInstance =
createSynthesizedNewInstance(
- newInstance.getType(), classApiLevel, methodProcessingContext);
+ newInstance.getType(), classApiLevel, methodProcessor, methodProcessingContext);
InvokeStatic outlinedStaticInit =
InvokeStatic.builder()
.setMethod(synthesizedNewInstance.getReference())
@@ -174,6 +181,7 @@
private DexEncodedMethod createSynthesizedNewInstance(
DexType targetType,
ComputedApiLevel computedApiLevel,
+ MethodProcessor methodProcessor,
MethodProcessingContext methodProcessingContext) {
DexProto proto = appView.dexItemFactory().createProto(factory.voidType);
ProgramMethod method =
@@ -193,6 +201,9 @@
m ->
NewInstanceSourceCode.create(appView, m.getHolderType(), targetType)
.generateCfCode()));
+ methodProcessor
+ .getEventConsumer()
+ .acceptInstanceInitializerOutline(method, methodProcessingContext.getMethodContext());
synchronized (synthesizedMethods) {
synthesizedMethods.add(method);
}
@@ -202,6 +213,7 @@
private DexEncodedMethod createSynthesizedInstanceInitializer(
DexMethod targetMethod,
ComputedApiLevel computedApiLevel,
+ MethodProcessor methodProcessor,
MethodProcessingContext methodProcessingContext) {
DexProto proto =
appView
@@ -226,7 +238,9 @@
.setConstructorTargetWithNewInstance(targetMethod)
.setStaticSource(m)
.build()));
-
+ methodProcessor
+ .getEventConsumer()
+ .acceptInstanceInitializerOutline(method, methodProcessingContext.getMethodContext());
synchronized (synthesizedMethods) {
synthesizedMethods.add(method);
ClassTypeElement exactType =
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutlinerEventConsumer.java b/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutlinerEventConsumer.java
new file mode 100644
index 0000000..7bd1b0e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/api/InstanceInitializerOutlinerEventConsumer.java
@@ -0,0 +1,12 @@
+// Copyright (c) 2023, 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.api;
+
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface InstanceInitializerOutlinerEventConsumer {
+
+ void acceptInstanceInitializerOutline(ProgramMethod method, ProgramMethod context);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerMethodProcessorEventConsumer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerMethodProcessorEventConsumer.java
new file mode 100644
index 0000000..e24fcd3
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerMethodProcessorEventConsumer.java
@@ -0,0 +1,17 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.enums;
+
+import com.android.tools.r8.graph.ProgramMethod;
+
+public interface EnumUnboxerMethodProcessorEventConsumer {
+
+ void acceptEnumUnboxerCheckNotZeroContext(ProgramMethod method, ProgramMethod context);
+
+ void acceptEnumUnboxerLocalUtilityClassMethodContext(ProgramMethod method, ProgramMethod context);
+
+ void acceptEnumUnboxerSharedUtilityClassMethodContext(
+ ProgramMethod method, ProgramMethod context);
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
index d0db72d..c9f6a96 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingRewriter.java
@@ -123,6 +123,7 @@
}
assert code.isConsistentSSABeforeTypesAreCorrect(appView);
ProgramMethod context = code.context();
+ EnumUnboxerMethodProcessorEventConsumer eventConsumer = methodProcessor.getEventConsumer();
Map<Instruction, DexType> convertedEnums = createInitialConvertedEnums(code, prototypeChanges);
Set<Phi> affectedPhis = Sets.newIdentityHashSet();
BasicBlockIterator blocks = code.listIterator();
@@ -186,19 +187,25 @@
if (invokedMethod == factory.enumMembers.ordinalMethod
|| invokedMethod.match(factory.enumMembers.hashCode)) {
replaceEnumInvoke(
- iterator, invoke, getSharedUtilityClass().ensureOrdinalMethod(appView));
+ iterator,
+ invoke,
+ getSharedUtilityClass().ensureOrdinalMethod(appView, context, eventConsumer));
continue;
} else if (invokedMethod.match(factory.enumMembers.equals)) {
replaceEnumInvoke(
- iterator, invoke, getSharedUtilityClass().ensureEqualsMethod(appView));
+ iterator,
+ invoke,
+ getSharedUtilityClass().ensureEqualsMethod(appView, context, eventConsumer));
continue;
} else if (invokedMethod == factory.enumMembers.compareTo
|| invokedMethod == factory.enumMembers.compareToWithObject) {
replaceEnumInvoke(
- iterator, invoke, getSharedUtilityClass().ensureCompareToMethod(appView));
+ iterator,
+ invoke,
+ getSharedUtilityClass().ensureCompareToMethod(appView, context, eventConsumer));
continue;
} else if (invokedMethod == factory.enumMembers.nameMethod) {
- rewriteNameMethod(iterator, invoke, enumType, methodProcessor);
+ rewriteNameMethod(iterator, invoke, enumType, context, eventConsumer);
continue;
} else if (invokedMethod.match(factory.enumMembers.toString)) {
DexMethod lookupMethod = enumUnboxingLens.lookupMethod(invokedMethod);
@@ -206,11 +213,11 @@
// class, which was moved, and the lens code rewriter will rewrite the invoke to
// that method.
if (invoke.isInvokeSuper() || lookupMethod == invokedMethod) {
- rewriteNameMethod(iterator, invoke, enumType, methodProcessor);
+ rewriteNameMethod(iterator, invoke, enumType, context, eventConsumer);
continue;
}
} else if (invokedMethod == factory.objectMembers.getClass) {
- rewriteNullCheck(iterator, invoke);
+ rewriteNullCheck(iterator, invoke, context, eventConsumer);
continue;
}
} else if (invokedMethod == factory.stringBuilderMethods.appendObject
@@ -221,7 +228,8 @@
DexType enumArgType = getEnumClassTypeOrNull(enumArg, convertedEnums);
if (enumArgType != null) {
ProgramMethod stringValueOfMethod =
- getLocalUtilityClass(enumArgType).ensureStringValueOfMethod(appView);
+ getLocalUtilityClass(enumArgType)
+ .ensureStringValueOfMethod(appView, context, eventConsumer);
InvokeStatic toStringInvoke =
InvokeStatic.builder()
.setMethod(stringValueOfMethod)
@@ -258,7 +266,7 @@
convertedEnums,
iterator,
affectedPhis,
- methodProcessor);
+ eventConsumer);
}
if (instruction.isStaticGet()) {
StaticGet staticGet = instruction.asStaticGet();
@@ -283,7 +291,7 @@
// Replace Enum.$VALUES by a call to: int[] SharedUtilityClass.values(int size).
InvokeStatic invoke =
InvokeStatic.builder()
- .setMethod(getSharedUtilityClass().getValuesMethod())
+ .setMethod(getSharedUtilityClass().getValuesMethod(context, eventConsumer))
.setFreshOutValue(appView, code)
.setSingleArgument(sizeValue)
.build();
@@ -312,7 +320,7 @@
DexType holder = instanceGet.getField().holder;
if (unboxedEnumsData.isUnboxedEnum(holder)) {
ProgramMethod fieldMethod =
- ensureInstanceFieldMethod(instanceGet.getField(), methodProcessor);
+ ensureInstanceFieldMethod(instanceGet.getField(), context, eventConsumer);
Value rewrittenOutValue =
code.createValue(
TypeElement.fromDexType(fieldMethod.getReturnType(), maybeNull(), appView));
@@ -363,7 +371,7 @@
Map<Instruction, DexType> convertedEnums,
InstructionListIterator instructionIterator,
Set<Phi> affectedPhis,
- MethodProcessor methodProcessor) {
+ EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
if (singleTarget == null) {
return;
@@ -381,7 +389,8 @@
if (!unboxedEnumsData.isUnboxedEnum(enumType)) {
return;
}
- ProgramMethod valueOfMethod = getLocalUtilityClass(enumType).ensureValueOfMethod(appView);
+ ProgramMethod valueOfMethod =
+ getLocalUtilityClass(enumType).ensureValueOfMethod(appView, context, eventConsumer);
Value outValue = invoke.outValue();
Value rewrittenOutValue = null;
if (outValue != null) {
@@ -406,7 +415,7 @@
Value argument = invoke.getFirstArgument();
DexType enumType = getEnumClassTypeOrNull(argument, convertedEnums);
if (enumType != null) {
- rewriteNullCheck(instructionIterator, invoke);
+ rewriteNullCheck(instructionIterator, invoke, context, eventConsumer);
}
} else if (invokedMethod == factory.objectsMethods.requireNonNullWithMessage) {
assert invoke.arguments().size() == 2;
@@ -416,7 +425,8 @@
replaceEnumInvoke(
instructionIterator,
invoke,
- getSharedUtilityClass().ensureCheckNotZeroWithMessageMethod(appView));
+ getSharedUtilityClass()
+ .ensureCheckNotZeroWithMessageMethod(appView, context, eventConsumer));
}
}
return;
@@ -430,7 +440,8 @@
DexType enumType = getEnumClassTypeOrNull(argument, convertedEnums);
if (enumType != null) {
ProgramMethod stringValueOfMethod =
- getLocalUtilityClass(enumType).ensureStringValueOfMethod(appView);
+ getLocalUtilityClass(enumType)
+ .ensureStringValueOfMethod(appView, context, eventConsumer);
instructionIterator.replaceCurrentInstruction(
new InvokeStatic(
stringValueOfMethod.getReference(), invoke.outValue(), invoke.arguments()));
@@ -481,6 +492,7 @@
.build();
instructionIterator.replaceCurrentInstruction(replacement);
convertedEnums.put(replacement, enumType);
+ eventConsumer.acceptEnumUnboxerCheckNotZeroContext(checkNotZeroMethod, context);
}
} else {
assert false;
@@ -491,9 +503,16 @@
}
}
- public void rewriteNullCheck(InstructionListIterator iterator, InvokeMethod invoke) {
+ public void rewriteNullCheck(
+ InstructionListIterator iterator,
+ InvokeMethod invoke,
+ ProgramMethod context,
+ EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
assert !invoke.hasOutValue() || !invoke.outValue().hasAnyUsers();
- replaceEnumInvoke(iterator, invoke, getSharedUtilityClass().ensureCheckNotZeroMethod(appView));
+ replaceEnumInvoke(
+ iterator,
+ invoke,
+ getSharedUtilityClass().ensureCheckNotZeroMethod(appView, context, eventConsumer));
}
private void removeRedundantValuesArrayCloning(
@@ -520,10 +539,12 @@
InstructionListIterator iterator,
InvokeMethodWithReceiver invoke,
DexType enumType,
- MethodProcessor methodProcessor) {
+ ProgramMethod context,
+ EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
ProgramMethod toStringMethod =
getLocalUtilityClass(enumType)
- .ensureGetInstanceFieldMethod(appView, factory.enumMembers.nameField);
+ .ensureGetInstanceFieldMethod(
+ appView, factory.enumMembers.nameField, context, eventConsumer);
iterator.replaceCurrentInstruction(
new InvokeStatic(toStringMethod.getReference(), invoke.outValue(), invoke.arguments()));
}
@@ -553,13 +574,17 @@
return iterator.insertConstIntInstruction(code, options, 0);
}
- private ProgramMethod ensureInstanceFieldMethod(DexField field, MethodProcessor methodProcessor) {
+ private ProgramMethod ensureInstanceFieldMethod(
+ DexField field,
+ ProgramMethod context,
+ EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
EnumInstanceFieldKnownData enumFieldKnownData =
unboxedEnumsData.getInstanceFieldData(field.holder, field);
if (enumFieldKnownData.isOrdinal()) {
- return getSharedUtilityClass().ensureOrdinalMethod(appView);
+ return getSharedUtilityClass().ensureOrdinalMethod(appView, context, eventConsumer);
}
- return getLocalUtilityClass(field.getHolderType()).ensureGetInstanceFieldMethod(appView, field);
+ return getLocalUtilityClass(field.getHolderType())
+ .ensureGetInstanceFieldMethod(appView, field, context, eventConsumer);
}
private void replaceEnumInvoke(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
index 77100b5..a186580 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingTreeFixer.java
@@ -39,6 +39,7 @@
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.ExtraUnusedNullParameter;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap.EnumData;
import com.android.tools.r8.ir.optimize.enums.classification.CheckNotNullEnumUnboxerMethodClassification;
@@ -148,8 +149,9 @@
IRConverter converter, ExecutorService executorService) throws ExecutionException {
BiMap<DexMethod, DexMethod> checkNotNullToCheckNotZeroMapping = HashBiMap.create();
ProcessorContext processorContext = appView.createProcessorContext();
+ MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty();
OneTimeMethodProcessor.Builder methodProcessorBuilder =
- OneTimeMethodProcessor.builder(processorContext);
+ OneTimeMethodProcessor.builder(eventConsumer, processorContext);
// Only duplicate checkNotNull() methods that are required for enum unboxing.
checkNotNullMethods.removeIf(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java
index 8dd1398..fc83f40 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingUtilityClasses.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.conversion.IRConverter;
+import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap.EnumData;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
@@ -95,8 +96,9 @@
fieldAccessInfoCollectionModifierBuilder.build().modify(appView);
// Create and process the utility methods.
+ MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty();
OneTimeMethodProcessor.Builder methodProcessorBuilder =
- OneTimeMethodProcessor.builder(appView.createProcessorContext());
+ OneTimeMethodProcessor.builder(eventConsumer, appView.createProcessorContext());
utilityClasses.forEach(
utilityClass -> {
utilityClass.ensureMethods(appView);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/LocalEnumUnboxingUtilityClass.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/LocalEnumUnboxingUtilityClass.java
index 6741924..4e7e556 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/LocalEnumUnboxingUtilityClass.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/LocalEnumUnboxingUtilityClass.java
@@ -59,7 +59,16 @@
public ProgramMethod ensureGetInstanceFieldMethod(
AppView<AppInfoWithLiveness> appView,
- DexField field) {
+ DexField field,
+ ProgramMethod context,
+ EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
+ ProgramMethod method = ensureGetInstanceFieldMethod(appView, field);
+ eventConsumer.acceptEnumUnboxerLocalUtilityClassMethodContext(method, context);
+ return method;
+ }
+
+ private ProgramMethod ensureGetInstanceFieldMethod(
+ AppView<AppInfoWithLiveness> appView, DexField field) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
String fieldName = field.getName().toString();
DexString methodName;
@@ -82,7 +91,16 @@
.generateCfCode());
}
- public ProgramMethod ensureStringValueOfMethod(AppView<AppInfoWithLiveness> appView) {
+ public ProgramMethod ensureStringValueOfMethod(
+ AppView<AppInfoWithLiveness> appView,
+ ProgramMethod context,
+ EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
+ ProgramMethod method = ensureStringValueOfMethod(appView);
+ eventConsumer.acceptEnumUnboxerLocalUtilityClassMethodContext(method, context);
+ return method;
+ }
+
+ private ProgramMethod ensureStringValueOfMethod(AppView<AppInfoWithLiveness> appView) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
AbstractValue defaultValue =
appView.abstractValueFactory().createSingleStringValue(dexItemFactory.createString("null"));
@@ -96,7 +114,16 @@
.generateCfCode());
}
- public ProgramMethod ensureValueOfMethod(AppView<AppInfoWithLiveness> appView) {
+ public ProgramMethod ensureValueOfMethod(
+ AppView<AppInfoWithLiveness> appView,
+ ProgramMethod context,
+ EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
+ ProgramMethod method = ensureValueOfMethod(appView);
+ eventConsumer.acceptEnumUnboxerLocalUtilityClassMethodContext(method, context);
+ return method;
+ }
+
+ private ProgramMethod ensureValueOfMethod(AppView<AppInfoWithLiveness> appView) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
return internalEnsureMethod(
appView,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
index c24a972..fb0c4d8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/SharedEnumUnboxingUtilityClass.java
@@ -75,7 +75,16 @@
ensureOrdinalMethod(appView);
}
- public ProgramMethod ensureCheckNotZeroMethod(AppView<AppInfoWithLiveness> appView) {
+ public ProgramMethod ensureCheckNotZeroMethod(
+ AppView<AppInfoWithLiveness> appView,
+ ProgramMethod context,
+ EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
+ ProgramMethod method = ensureCheckNotZeroMethod(appView);
+ eventConsumer.acceptEnumUnboxerSharedUtilityClassMethodContext(method, context);
+ return method;
+ }
+
+ private ProgramMethod ensureCheckNotZeroMethod(AppView<AppInfoWithLiveness> appView) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
return internalEnsureMethod(
appView,
@@ -84,7 +93,16 @@
method -> EnumUnboxingCfMethods.EnumUnboxingMethods_zeroCheck(dexItemFactory, method));
}
- public ProgramMethod ensureCheckNotZeroWithMessageMethod(AppView<AppInfoWithLiveness> appView) {
+ public ProgramMethod ensureCheckNotZeroWithMessageMethod(
+ AppView<AppInfoWithLiveness> appView,
+ ProgramMethod context,
+ EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
+ ProgramMethod method = ensureCheckNotZeroWithMessageMethod(appView);
+ eventConsumer.acceptEnumUnboxerSharedUtilityClassMethodContext(method, context);
+ return method;
+ }
+
+ private ProgramMethod ensureCheckNotZeroWithMessageMethod(AppView<AppInfoWithLiveness> appView) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
return internalEnsureMethod(
appView,
@@ -95,7 +113,16 @@
EnumUnboxingCfMethods.EnumUnboxingMethods_zeroCheckMessage(dexItemFactory, method));
}
- public ProgramMethod ensureCompareToMethod(AppView<AppInfoWithLiveness> appView) {
+ public ProgramMethod ensureCompareToMethod(
+ AppView<AppInfoWithLiveness> appView,
+ ProgramMethod context,
+ EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
+ ProgramMethod method = ensureCompareToMethod(appView);
+ eventConsumer.acceptEnumUnboxerSharedUtilityClassMethodContext(method, context);
+ return method;
+ }
+
+ private ProgramMethod ensureCompareToMethod(AppView<AppInfoWithLiveness> appView) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
return internalEnsureMethod(
appView,
@@ -105,7 +132,16 @@
method -> EnumUnboxingCfMethods.EnumUnboxingMethods_compareTo(dexItemFactory, method));
}
- public ProgramMethod ensureEqualsMethod(AppView<AppInfoWithLiveness> appView) {
+ public ProgramMethod ensureEqualsMethod(
+ AppView<AppInfoWithLiveness> appView,
+ ProgramMethod context,
+ EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
+ ProgramMethod method = ensureEqualsMethod(appView);
+ eventConsumer.acceptEnumUnboxerSharedUtilityClassMethodContext(method, context);
+ return method;
+ }
+
+ private ProgramMethod ensureEqualsMethod(AppView<AppInfoWithLiveness> appView) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
return internalEnsureMethod(
appView,
@@ -115,7 +151,16 @@
method -> EnumUnboxingCfMethods.EnumUnboxingMethods_equals(dexItemFactory, method));
}
- public ProgramMethod ensureOrdinalMethod(AppView<AppInfoWithLiveness> appView) {
+ public ProgramMethod ensureOrdinalMethod(
+ AppView<AppInfoWithLiveness> appView,
+ ProgramMethod context,
+ EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
+ ProgramMethod method = ensureOrdinalMethod(appView);
+ eventConsumer.acceptEnumUnboxerSharedUtilityClassMethodContext(method, context);
+ return method;
+ }
+
+ private ProgramMethod ensureOrdinalMethod(AppView<AppInfoWithLiveness> appView) {
DexItemFactory dexItemFactory = appView.dexItemFactory();
return internalEnsureMethod(
appView,
@@ -156,7 +201,9 @@
return sharedUtilityClass;
}
- public ProgramMethod getValuesMethod() {
+ public ProgramMethod getValuesMethod(
+ ProgramMethod context, EnumUnboxerMethodProcessorEventConsumer eventConsumer) {
+ eventConsumer.acceptEnumUnboxerSharedUtilityClassMethodContext(valuesMethod, context);
return valuesMethod;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
index 05f697c..b00db83 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMemberOptimizer.java
@@ -154,9 +154,7 @@
LibraryMethodModelCollection.State optimizationState =
optimizationStates.computeIfAbsent(
- optimizer,
- libraryMethodModelCollection ->
- libraryMethodModelCollection.createInitialState(methodProcessor));
+ optimizer, LibraryMethodModelCollection::createInitialState);
optimizer.optimize(
code,
blockIterator,
@@ -166,6 +164,7 @@
affectedValues,
blocksToRemove,
optimizationState,
+ methodProcessor,
methodProcessingContext);
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java
index dbc5b0b..fef1563 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/LibraryMethodModelCollection.java
@@ -20,7 +20,7 @@
/** Used to model the behavior of library methods for optimization purposes. */
public interface LibraryMethodModelCollection<T extends State> {
- default T createInitialState(MethodProcessor methodProcessor) {
+ default T createInitialState() {
return null;
}
@@ -43,6 +43,7 @@
Set<Value> affectedValues,
Set<BasicBlock> blocksToRemove,
T state,
+ MethodProcessor methodProcessor,
MethodProcessingContext methodProcessingContext);
@SuppressWarnings("unchecked")
@@ -55,6 +56,7 @@
Set<Value> affectedValues,
Set<BasicBlock> blocksToRemove,
Object state,
+ MethodProcessor methodProcessor,
MethodProcessingContext methodProcessingContext) {
optimize(
code,
@@ -65,6 +67,7 @@
affectedValues,
blocksToRemove,
(T) state,
+ methodProcessor,
methodProcessingContext);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StatelessLibraryMethodModelCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StatelessLibraryMethodModelCollection.java
index 520c27a..9cfdcbd 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/StatelessLibraryMethodModelCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StatelessLibraryMethodModelCollection.java
@@ -20,7 +20,7 @@
implements LibraryMethodModelCollection<State> {
@Override
- public final State createInitialState(MethodProcessor methodProcessor) {
+ public final State createInitialState() {
return null;
}
@@ -43,6 +43,7 @@
Set<Value> affectedValues,
Set<BasicBlock> blocksToRemove,
State state,
+ MethodProcessor methodProcessor,
MethodProcessingContext methodProcessingContext) {
assert state == null;
optimize(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
index 835baa6..456ecc7 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/library/StringBuilderMethodOptimizer.java
@@ -58,8 +58,8 @@
}
@Override
- public State createInitialState(MethodProcessor methodProcessor) {
- return new State(methodProcessor);
+ public State createInitialState() {
+ return new State();
}
@Override
@@ -77,6 +77,7 @@
Set<Value> affectedValues,
Set<BasicBlock> blocksToRemove,
State state,
+ MethodProcessor methodProcessor,
MethodProcessingContext methodProcessingContext) {
if (invoke.isInvokeMethodWithReceiver()) {
InvokeMethodWithReceiver invokeWithReceiver = invoke.asInvokeMethodWithReceiver();
@@ -86,6 +87,7 @@
invokeWithReceiver,
singleTarget,
state,
+ methodProcessor,
methodProcessingContext);
} else if (singleTarget.getReference() == dexItemFactory.stringBuilderMethods.toString) {
optimizeToString(instructionIterator, invokeWithReceiver);
@@ -98,6 +100,7 @@
InvokeMethodWithReceiver invoke,
DexClassAndMethod singleTarget,
State state,
+ MethodProcessor methodProcessor,
MethodProcessingContext methodProcessingContext) {
boolean isStringBuilderUnused = state.isUnusedBuilder(invoke.getReceiver());
if (invoke.hasOutValue() && (options.isGeneratingDex() || isStringBuilderUnused)) {
@@ -107,7 +110,7 @@
}
if (isStringBuilderUnused) {
optimizeAppendOnUnusedStringBuilder(
- instructionIterator, invoke, singleTarget, state, methodProcessingContext);
+ instructionIterator, invoke, singleTarget, methodProcessor, methodProcessingContext);
}
}
@@ -115,7 +118,7 @@
InstructionListIterator instructionIterator,
InvokeMethodWithReceiver invoke,
DexClassAndMethod singleTarget,
- State state,
+ MethodProcessor methodProcessor,
MethodProcessingContext methodProcessingContext) {
assert !invoke.hasOutValue();
DexMethod appendMethod = singleTarget.getReference();
@@ -144,8 +147,8 @@
// Replace the instruction by toStringIfNotNull().
UtilityMethodForCodeOptimizations toStringIfNotNullMethod =
UtilityMethodsForCodeOptimizations.synthesizeToStringIfNotNullMethod(
- appView, methodProcessingContext);
- toStringIfNotNullMethod.optimize(state.methodProcessor);
+ appView, methodProcessor.getEventConsumer(), methodProcessingContext);
+ toStringIfNotNullMethod.optimize(methodProcessor);
InvokeStatic replacement =
InvokeStatic.builder()
.setMethod(toStringIfNotNullMethod.getMethod())
@@ -168,14 +171,8 @@
class State implements LibraryMethodModelCollection.State {
- final MethodProcessor methodProcessor;
-
final Reference2BooleanMap<Value> unusedBuilders = new Reference2BooleanOpenHashMap<>();
- State(MethodProcessor methodProcessor) {
- this.methodProcessor = methodProcessor;
- }
-
boolean isUnusedBuilder(Value value) {
if (!unusedBuilders.containsKey(value)) {
computeIsUnusedBuilder(value);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineCollection.java
index 713322d..a3782b6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineCollection.java
@@ -11,7 +11,7 @@
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.optimize.OutlinerImpl.Outline;
+import com.android.tools.r8.ir.optimize.outliner.OutlinerImpl.Outline;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ListUtils;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineOptimizationEventConsumer.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineOptimizationEventConsumer.java
new file mode 100644
index 0000000..4d8a9ed
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlineOptimizationEventConsumer.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.outliner;
+
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ConcreteArtProfileCollectionAdditions;
+import java.util.Collection;
+
+public interface OutlineOptimizationEventConsumer {
+
+ void acceptOutlineMethod(ProgramMethod method, Collection<ProgramMethod> contexts);
+
+ static OutlineOptimizationEventConsumer create(
+ ArtProfileCollectionAdditions collectionAdditions) {
+ if (collectionAdditions.isNop()) {
+ return empty();
+ }
+ return create(collectionAdditions.asConcrete());
+ }
+
+ static OutlineOptimizationEventConsumer create(
+ ConcreteArtProfileCollectionAdditions collectionAdditions) {
+ return (method, contexts) -> {
+ for (ProgramMethod context : contexts) {
+ collectionAdditions.applyIfContextIsInProfile(
+ context,
+ additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+ }
+ };
+ }
+
+ static EmptyOutlineOptimizationEventConsumer empty() {
+ return EmptyOutlineOptimizationEventConsumer.getInstance();
+ }
+
+ class EmptyOutlineOptimizationEventConsumer implements OutlineOptimizationEventConsumer {
+
+ private static final EmptyOutlineOptimizationEventConsumer INSTANCE =
+ new EmptyOutlineOptimizationEventConsumer();
+
+ private EmptyOutlineOptimizationEventConsumer() {}
+
+ static EmptyOutlineOptimizationEventConsumer getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public void acceptOutlineMethod(ProgramMethod method, Collection<ProgramMethod> contexts) {
+ // Intentionally empty.
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/outliner/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/Outliner.java
index b3ed5f5..f8d0177 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/outliner/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/Outliner.java
@@ -9,7 +9,6 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.conversion.IRConverter;
-import com.android.tools.r8.ir.optimize.OutlinerImpl;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.Timing;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
similarity index 96%
rename from src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
rename to src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
index 4a99fb4..7382a1e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/outliner/OutlinerImpl.java
@@ -1,8 +1,8 @@
-// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2023, 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;
+package com.android.tools.r8.ir.optimize.outliner;
import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
import static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull;
@@ -60,13 +60,15 @@
import com.android.tools.r8.ir.conversion.IRBuilder;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.MethodConversionOptions.MutableMethodConversionOptions;
+import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
import com.android.tools.r8.ir.conversion.SourceCode;
+import com.android.tools.r8.ir.optimize.CodeRewriter;
import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.ir.optimize.InliningConstraints;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
-import com.android.tools.r8.ir.optimize.outliner.OutlineCollection;
-import com.android.tools.r8.ir.optimize.outliner.Outliner;
import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
@@ -131,7 +133,7 @@
/** Result of third step (see {@link OutlinerImpl#buildOutlineMethods()}. */
private final Map<Outline, DexMethod> generatedOutlines = new HashMap<>();
- static final int MAX_IN_SIZE = 5; // Avoid using ranged calls for outlined code.
+ static final int MAX_IN_SIZE = 5; // Avoid using ranged calls for outlined code.
private final AppView<AppInfoWithLiveness> appView;
private final DexItemFactory dexItemFactory;
@@ -223,17 +225,14 @@
private final NumericType numericType;
- private BinOpOutlineInstruction(
- OutlineInstructionType type,
- NumericType numericType) {
+ private BinOpOutlineInstruction(OutlineInstructionType type, NumericType numericType) {
super(type);
this.numericType = numericType;
}
static BinOpOutlineInstruction fromInstruction(Binop instruction) {
return new BinOpOutlineInstruction(
- OutlineInstructionType.fromInstruction(instruction),
- instruction.getNumericType());
+ OutlineInstructionType.fromInstruction(instruction), instruction.getNumericType());
}
@Override
@@ -406,11 +405,7 @@
private final boolean hasReceiver;
private InvokeOutlineInstruction(
- DexMethod method,
- Type type,
- boolean hasOutValue,
- ValueType[] inputTypes,
- DexProto proto) {
+ DexMethod method, Type type, boolean hasOutValue, ValueType[] inputTypes, DexProto proto) {
super(OutlineInstructionType.INVOKE);
hasReceiver = inputTypes.length != method.proto.parameters.values.length;
assert !hasReceiver || inputTypes[0].isObject();
@@ -568,7 +563,7 @@
final List<DexType> argumentTypes;
final List<Integer> argumentMap;
final List<OutlineInstruction> templateInstructions = new ArrayList<>();
- final public DexType returnType;
+ public final DexType returnType;
private DexProto proto;
@@ -765,7 +760,7 @@
// This is the superclass for both collection candidates and actually replacing code.
// TODO(sgjesse): Collect more information in the candidate collection and reuse that for
// replacing.
- abstract private class OutlineSpotter {
+ private abstract class OutlineSpotter {
final ProgramMethod method;
final IRCode irCode;
@@ -955,7 +950,7 @@
}
private DexType argumentTypeFromInvoke(InvokeMethod invoke, int index) {
- boolean withReceiver = invoke.isInvokeMethodWithReceiver() || invoke.isInvokePolymorphic();
+ boolean withReceiver = invoke.isInvokeMethodWithReceiver() || invoke.isInvokePolymorphic();
if (withReceiver && index == 0) {
return invoke.getInvokedMethod().holder;
}
@@ -1355,8 +1350,14 @@
identifyOutlineSites(code);
},
executorService);
- List<ProgramMethod> outlineMethods = buildOutlineMethods();
- converter.optimizeSynthesizedMethods(outlineMethods, executorService);
+ ArtProfileCollectionAdditions artProfileCollectionAdditions =
+ ArtProfileCollectionAdditions.create(appView);
+ List<ProgramMethod> outlineMethods =
+ buildOutlineMethods(
+ OutlineOptimizationEventConsumer.create(artProfileCollectionAdditions));
+ artProfileCollectionAdditions.commit(appView);
+ MethodProcessorEventConsumer eventConsumer = MethodProcessorEventConsumer.empty();
+ converter.optimizeSynthesizedMethods(outlineMethods, eventConsumer, executorService);
feedback.updateVisibleOptimizationInfo();
forEachSelectedOutliningMethod(
converter,
@@ -1503,7 +1504,7 @@
return result;
}
- public List<ProgramMethod> buildOutlineMethods() {
+ public List<ProgramMethod> buildOutlineMethods(OutlineOptimizationEventConsumer eventConsumer) {
ProcessorContext outlineProcessorContext = appView.createProcessorContext();
Map<DexMethod, MethodProcessingContext> methodProcessingContexts = new IdentityHashMap<>();
List<ProgramMethod> outlineMethods = new ArrayList<>();
@@ -1544,6 +1545,7 @@
representative.getDefinition().getClassFileVersion());
}
});
+ eventConsumer.acceptOutlineMethod(outlineMethod, sites);
generatedOutlines.put(outline, outlineMethod.getReference());
outlineMethods.add(outlineMethod);
}
@@ -1660,12 +1662,10 @@
}
@Override
- public void setUp() {
- }
+ public void setUp() {}
@Override
- public void clear() {
- }
+ public void clear() {}
@Override
public void buildPrelude(IRBuilder builder) {
@@ -1715,14 +1715,14 @@
}
@Override
- public void resolveAndBuildSwitch(int value, int fallthroughOffset, int payloadOffset,
- IRBuilder builder) {
+ public void resolveAndBuildSwitch(
+ int value, int fallthroughOffset, int payloadOffset, IRBuilder builder) {
throw new Unreachable("Unexpected call to resolveAndBuildSwitch");
}
@Override
- public void resolveAndBuildNewArrayFilledData(int arrayRef, int payloadOffset,
- IRBuilder builder) {
+ public void resolveAndBuildNewArrayFilledData(
+ int arrayRef, int payloadOffset, IRBuilder builder) {
throw new Unreachable("Unexpected call to resolveAndBuildNewArrayFilledData");
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/MoveLoadUpPeephole.java b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/MoveLoadUpPeephole.java
index 3b93c50..36d883a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/MoveLoadUpPeephole.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/MoveLoadUpPeephole.java
@@ -36,11 +36,20 @@
private int stackHeight = 0;
private Instruction insertPosition = null;
+ // Set a threshold for the number of loads associated with a stored value that we attempt to move
+ // up. For big arrays with straight control flow moving loads up will have bad performance due
+ // to using an iterator to move up and down.
+ private static final int LOAD_USER_THRESHOLD = 20;
+
private final Point firstLoad =
new Point(
(i) -> {
if (PeepholeHelper.withoutLocalInfo(Instruction::isLoad).test(i)) {
- local = i.asLoad().src();
+ Load load = i.asLoad();
+ if (load.getFirstOperand().numberOfAllUsers() > LOAD_USER_THRESHOLD) {
+ return false;
+ }
+ local = load.src();
return true;
}
return false;
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNaming.java b/src/main/java/com/android/tools/r8/naming/ClassNaming.java
index 138e738..5899d87 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNaming.java
@@ -20,6 +20,8 @@
public abstract Builder addMemberEntry(MemberNaming entry);
+ public abstract MemberNaming lookupMemberEntry(Signature signature);
+
public abstract ClassNaming build();
/** This is an optional method, may be implemented as no-op */
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
index cbfb5e3..afc7132 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForMapApplier.java
@@ -80,6 +80,11 @@
}
@Override
+ public MemberNaming lookupMemberEntry(Signature signature) {
+ return null;
+ }
+
+ @Override
public ClassNamingForMapApplier build() {
return new ClassNamingForMapApplier(
renamedName, originalName, position, qualifiedMethodMembers, methodMembers, fieldMembers);
diff --git a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
index 98a08a1..15efe36 100644
--- a/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
+++ b/src/main/java/com/android/tools/r8/naming/ClassNamingForNameMapper.java
@@ -76,6 +76,13 @@
}
@Override
+ public MemberNaming lookupMemberEntry(Signature signature) {
+ return signature.isFieldSignature()
+ ? fieldMembers.get(signature.asFieldSignature())
+ : methodMembers.get(signature.asMethodSignature());
+ }
+
+ @Override
public ClassNamingForNameMapper build() {
Map<String, MappedRangesOfName> map;
@@ -350,11 +357,11 @@
@Override
public MemberNaming lookup(Signature renamedSignature) {
if (renamedSignature.kind() == SignatureKind.METHOD) {
- assert renamedSignature instanceof MethodSignature;
+ assert renamedSignature.isMethodSignature();
return methodMembers.get(renamedSignature);
} else {
assert renamedSignature.kind() == SignatureKind.FIELD;
- assert renamedSignature instanceof FieldSignature;
+ assert renamedSignature.isFieldSignature();
return fieldMembers.get(renamedSignature);
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/MemberNaming.java b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
index 318034f..a26d531 100644
--- a/src/main/java/com/android/tools/r8/naming/MemberNaming.java
+++ b/src/main/java/com/android/tools/r8/naming/MemberNaming.java
@@ -21,6 +21,7 @@
import com.android.tools.r8.references.TypeReference;
import com.android.tools.r8.utils.ArrayUtils;
import com.android.tools.r8.utils.CollectionUtils;
+import com.android.tools.r8.utils.ConsumerUtils;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.Iterables;
@@ -142,11 +143,11 @@
additionalMappingInformation, info, onProhibitedAddition);
}
- void addAllMappingInformationInternal(List<ReferentialMappingInformation> otherInfo) {
- if (additionalMappingInformation == EMPTY_MAPPING_INFORMATION) {
- additionalMappingInformation = new ArrayList<>();
+ public void addAllMappingInformation(List<ReferentialMappingInformation> infos) {
+ Consumer<MappingInformation> emptyConsumer = ConsumerUtils.emptyConsumer();
+ for (ReferentialMappingInformation mappingInformation : infos) {
+ addMappingInformation(mappingInformation, emptyConsumer);
}
- additionalMappingInformation.addAll(otherInfo);
}
public boolean isCompilerSynthesized() {
diff --git a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
index 14a6a22..17157c3 100644
--- a/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
+++ b/src/main/java/com/android/tools/r8/naming/ProguardMapReader.java
@@ -538,14 +538,15 @@
|| lastAddedNaming == null
|| !lastAddedNaming.getRenamedName().equals(renamedName)
|| !lastAddedNaming.getOriginalSignature().equals(originalSignature)) {
- MemberNaming newMemberNaming =
- new MemberNaming(
- originalSignature,
- getResidualSignatureForMemberNaming(
- residualSignature, originalSignature, renamedName),
- new LinePosition(lineNumber));
+ Signature lookupKey =
+ getResidualSignatureForMemberNaming(residualSignature, originalSignature, renamedName);
+ MemberNaming newMemberNaming = classNamingBuilder.lookupMemberEntry(lookupKey);
+ if (newMemberNaming == null) {
+ newMemberNaming =
+ new MemberNaming(originalSignature, lookupKey, new LinePosition(lineNumber));
+ }
if (additionalMappingInformation.isSet()) {
- newMemberNaming.addAllMappingInformationInternal(additionalMappingInformation.get());
+ newMemberNaming.addAllMappingInformation(additionalMappingInformation.get());
}
classNamingBuilder.addMemberEntry(newMemberNaming);
residualSignature.clear();
@@ -553,7 +554,7 @@
return newMemberNaming;
}
if (additionalMappingInformation.isSet()) {
- lastAddedNaming.addAllMappingInformationInternal(additionalMappingInformation.get());
+ lastAddedNaming.addAllMappingInformation(additionalMappingInformation.get());
additionalMappingInformation.clear();
}
residualSignature.clear();
diff --git a/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java b/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java
index 48baf2f..ee7b46e 100644
--- a/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java
+++ b/src/main/java/com/android/tools/r8/naming/mappinginformation/CompilerSynthesizedMappingInformation.java
@@ -51,7 +51,7 @@
@Override
public boolean allowOther(MappingInformation information) {
- return true;
+ return !information.isCompilerSynthesizedMappingInformation();
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index bf1afad..16edb6a 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -36,6 +36,7 @@
import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorGraphLens.Builder;
import com.android.tools.r8.optimize.argumentpropagation.utils.ParameterRemovalUtils;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepFieldInfo;
import com.android.tools.r8.shaking.KeepMethodInfo;
@@ -193,18 +194,23 @@
Timing timing)
throws ExecutionException {
timing.begin("Optimize components");
+ ArtProfileCollectionAdditions artProfileCollectionAdditions =
+ ArtProfileCollectionAdditions.create(appView);
+ ArgumentPropagatorSyntheticEventConsumer eventConsumer =
+ ArgumentPropagatorSyntheticEventConsumer.create(artProfileCollectionAdditions);
ProcessorContext processorContext = appView.createProcessorContext();
Collection<Builder> partialGraphLensBuilders =
ThreadUtils.processItemsWithResults(
stronglyConnectedProgramComponents,
classes ->
- new StronglyConnectedComponentOptimizer(processorContext)
+ new StronglyConnectedComponentOptimizer(eventConsumer, processorContext)
.optimize(
classes,
interfaceDispatchOutsideProgram.getOrDefault(
classes, DexMethodSignatureSet.empty()),
affectedClassConsumer),
executorService);
+ artProfileCollectionAdditions.commit(appView);
timing.end();
// Merge all the partial, disjoint graph lens builders into a single graph lens.
@@ -267,9 +273,12 @@
private final Map<DexMethodSignature, Pair<AllowedPrototypeChanges, DexMethodSignature>>
occupiedMethodSignatures = new HashMap<>();
+ private final ArgumentPropagatorSyntheticEventConsumer eventConsumer;
private final ProcessorContext processorContext;
- public StronglyConnectedComponentOptimizer(ProcessorContext processorContext) {
+ public StronglyConnectedComponentOptimizer(
+ ArgumentPropagatorSyntheticEventConsumer eventConsumer, ProcessorContext processorContext) {
+ this.eventConsumer = eventConsumer;
this.processorContext = processorContext;
}
@@ -876,16 +885,17 @@
}
}
}
- DexType extraArgumentType =
+ DexProgramClass extraArgumentClass =
appView
.getSyntheticItems()
.createClass(
kinds -> kinds.NON_FIXED_INIT_TYPE_ARGUMENT,
processorContext.createMethodProcessingContext(method).createUniqueContext(),
- appView)
- .getType();
+ appView);
+ eventConsumer.acceptInitializerArgumentClass(extraArgumentClass, method);
RewrittenPrototypeDescription finalPrototypeChanges =
- prototypeChanges.withExtraParameters(new ExtraUnusedNullParameter(extraArgumentType));
+ prototypeChanges.withExtraParameters(
+ new ExtraUnusedNullParameter(extraArgumentClass.getType()));
boolean added =
instanceInitializerSignatures.add(
finalPrototypeChanges.rewriteMethod(method, dexItemFactory));
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorSyntheticEventConsumer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorSyntheticEventConsumer.java
new file mode 100644
index 0000000..aae0602
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorSyntheticEventConsumer.java
@@ -0,0 +1,52 @@
+// Copyright (c) 2023, 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.optimize.argumentpropagation;
+
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.profile.art.rewriting.ArtProfileCollectionAdditions;
+import com.android.tools.r8.profile.art.rewriting.ConcreteArtProfileCollectionAdditions;
+
+public interface ArgumentPropagatorSyntheticEventConsumer {
+
+ void acceptInitializerArgumentClass(DexProgramClass clazz, ProgramMethod context);
+
+ static ArgumentPropagatorSyntheticEventConsumer create(
+ ArtProfileCollectionAdditions collectionAdditions) {
+ if (collectionAdditions.isNop()) {
+ return empty();
+ }
+ return create(collectionAdditions.asConcrete());
+ }
+
+ static ArgumentPropagatorSyntheticEventConsumer create(
+ ConcreteArtProfileCollectionAdditions collectionAdditions) {
+ return (clazz, context) ->
+ collectionAdditions.applyIfContextIsInProfile(
+ context, additionsBuilder -> additionsBuilder.addRule(clazz));
+ }
+
+ static ArgumentPropagatorSyntheticEventConsumer empty() {
+ return EmptyArgumentPropagatorSyntheticEventConsumer.getInstance();
+ }
+
+ class EmptyArgumentPropagatorSyntheticEventConsumer
+ implements ArgumentPropagatorSyntheticEventConsumer {
+
+ private static EmptyArgumentPropagatorSyntheticEventConsumer INSTANCE =
+ new EmptyArgumentPropagatorSyntheticEventConsumer();
+
+ private EmptyArgumentPropagatorSyntheticEventConsumer() {}
+
+ static EmptyArgumentPropagatorSyntheticEventConsumer getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public void acceptInitializerArgumentClass(DexProgramClass clazz, ProgramMethod context) {
+ // Intentionally empty.
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
index 2cdb3e0..e7012b8 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCollection.java
@@ -4,11 +4,14 @@
package com.android.tools.r8.profile.art;
+import com.android.tools.r8.graph.AppInfo;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.naming.NamingLens;
+import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.InternalOptions;
import java.util.ArrayList;
import java.util.Collection;
@@ -16,17 +19,15 @@
public abstract class ArtProfileCollection {
- public static ArtProfileCollection createInitialArtProfileCollection(InternalOptions options) {
+ public static ArtProfileCollection createInitialArtProfileCollection(
+ AppInfo appInfo, InternalOptions options) {
ArtProfileOptions artProfileOptions = options.getArtProfileOptions();
Collection<ArtProfileForRewriting> artProfilesForRewriting =
artProfileOptions.getArtProfilesForRewriting();
- if (artProfilesForRewriting.isEmpty()) {
- return empty();
- }
- if (artProfileOptions.isPassthrough()) {
- return passthrough();
- }
- List<ArtProfile> artProfiles = new ArrayList<>(artProfilesForRewriting.size());
+ List<ArtProfile> artProfiles =
+ new ArrayList<>(
+ artProfilesForRewriting.size()
+ + BooleanUtils.intValue(artProfileOptions.isCompletenessCheckForTestingEnabled()));
for (ArtProfileForRewriting artProfileForRewriting :
options.getArtProfileOptions().getArtProfilesForRewriting()) {
ArtProfileProvider artProfileProvider = artProfileForRewriting.getArtProfileProvider();
@@ -35,17 +36,31 @@
artProfileForRewriting.getArtProfileProvider().getArtProfile(artProfileBuilder);
artProfiles.add(artProfileBuilder.build());
}
+ if (artProfileOptions.isCompletenessCheckForTestingEnabled()) {
+ artProfiles.add(createCompleteArtProfile(appInfo));
+ }
+ if (artProfiles.isEmpty()) {
+ return empty();
+ }
return new NonEmptyArtProfileCollection(artProfiles);
}
+ private static ArtProfile createCompleteArtProfile(AppInfo appInfo) {
+ ArtProfile.Builder artProfileBuilder = ArtProfile.builder();
+ for (DexProgramClass clazz : appInfo.classesWithDeterministicOrder()) {
+ artProfileBuilder.addRule(ArtProfileClassRule.builder().setType(clazz.getType()).build());
+ clazz.forEachMethod(
+ method ->
+ artProfileBuilder.addRule(
+ ArtProfileMethodRule.builder().setMethod(method.getReference()).build()));
+ }
+ return artProfileBuilder.build();
+ }
+
public static EmptyArtProfileCollection empty() {
return EmptyArtProfileCollection.getInstance();
}
- public static PassthroughArtProfileCollection passthrough() {
- return PassthroughArtProfileCollection.getInstance();
- }
-
public abstract boolean isNonEmpty();
public abstract NonEmptyArtProfileCollection asNonEmpty();
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileCompletenessChecker.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCompletenessChecker.java
new file mode 100644
index 0000000..21a7635
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileCompletenessChecker.java
@@ -0,0 +1,89 @@
+// Copyright (c) 2023, 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.profile.art;
+
+import static com.android.tools.r8.profile.art.ArtProfileCompletenessChecker.CompletenessExceptions.ALLOW_MISSING_ENUM_UNBOXING_UTILITY_METHODS;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexReference;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramDefinition;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.synthesis.SyntheticItems;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.Sets;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class ArtProfileCompletenessChecker {
+
+ public enum CompletenessExceptions {
+ ALLOW_MISSING_ENUM_UNBOXING_UTILITY_METHODS
+ }
+
+ public static boolean verify(
+ AppView<?> appView, CompletenessExceptions... completenessExceptions) {
+ if (appView.options().getArtProfileOptions().isCompletenessCheckForTestingEnabled()) {
+ ArtProfile completeArtProfile = appView.getArtProfileCollection().asNonEmpty().getLast();
+ assert verifyProfileIsComplete(
+ appView, completeArtProfile, Sets.newHashSet(completenessExceptions));
+ }
+ return true;
+ }
+
+ private static boolean verifyProfileIsComplete(
+ AppView<?> appView,
+ ArtProfile artProfile,
+ Set<CompletenessExceptions> completenessExceptions) {
+ assert !appView.getSyntheticItems().hasPendingSyntheticClasses();
+ List<DexReference> missing = new ArrayList<>();
+ for (DexProgramClass clazz : appView.appInfo().classesWithDeterministicOrder()) {
+ if (appView.horizontallyMergedClasses().hasBeenMergedIntoDifferentType(clazz.getType())
+ || (appView.hasVerticallyMergedClasses()
+ && appView.verticallyMergedClasses().hasBeenMergedIntoSubtype(clazz.getType()))
+ || appView.unboxedEnums().isUnboxedEnum(clazz)) {
+ continue;
+ }
+ if (!artProfile.containsClassRule(clazz.getType())) {
+ recordMissingDefinition(appView, clazz, completenessExceptions, missing);
+ }
+ for (ProgramMethod method : clazz.programMethods()) {
+ if (!artProfile.containsMethodRule(method.getReference())) {
+ recordMissingDefinition(appView, method, completenessExceptions, missing);
+ }
+ }
+ }
+ if (!missing.isEmpty()) {
+ String message =
+ StringUtils.join(System.lineSeparator(), missing, DexReference::toSmaliString);
+ assert false : message;
+ }
+ return true;
+ }
+
+ private static void recordMissingDefinition(
+ AppView<?> appView,
+ ProgramDefinition definition,
+ Set<CompletenessExceptions> completenessExceptions,
+ List<DexReference> missing) {
+ if (completenessExceptions.contains(ALLOW_MISSING_ENUM_UNBOXING_UTILITY_METHODS)) {
+ DexType contextType = definition.getContextType();
+ SyntheticItems syntheticItems = appView.getSyntheticItems();
+ if (syntheticItems.isSynthetic(contextType)) {
+ if (syntheticItems.isSyntheticOfKind(
+ contextType, naming -> naming.ENUM_UNBOXING_CHECK_NOT_ZERO_METHOD)
+ || syntheticItems.isSyntheticOfKind(
+ contextType, naming -> naming.ENUM_UNBOXING_LOCAL_UTILITY_CLASS)
+ || syntheticItems.isSyntheticOfKind(
+ contextType, naming -> naming.ENUM_UNBOXING_SHARED_UTILITY_CLASS)) {
+ return;
+ }
+ }
+ }
+ missing.add(definition.getReference());
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java b/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
index 231a2d6..d7c3f12 100644
--- a/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
+++ b/src/main/java/com/android/tools/r8/profile/art/ArtProfileOptions.java
@@ -4,13 +4,17 @@
package com.android.tools.r8.profile.art;
+import static com.android.tools.r8.utils.SystemPropertyUtils.parseSystemPropertyOrDefault;
+
import java.util.Collection;
import java.util.Collections;
public class ArtProfileOptions {
private Collection<ArtProfileForRewriting> artProfilesForRewriting = Collections.emptyList();
- private boolean passthrough;
+ private boolean enableCompletenessCheckForTesting =
+ parseSystemPropertyOrDefault(
+ "com.android.tools.r8.artprofilerewritingcompletenesscheck", false);
public ArtProfileOptions() {}
@@ -18,17 +22,25 @@
return artProfilesForRewriting;
}
+ public boolean isCompletenessCheckForTestingEnabled() {
+ return enableCompletenessCheckForTesting;
+ }
+
+ public boolean isIncludingApiReferenceStubs() {
+ // We only include API reference stubs in the residual ART profiles for completeness testing.
+ // This is because the API reference stubs should never be used at runtime except for
+ // verification, meaning there should be no need to AOT them.
+ return enableCompletenessCheckForTesting;
+ }
+
public ArtProfileOptions setArtProfilesForRewriting(Collection<ArtProfileForRewriting> inputs) {
this.artProfilesForRewriting = inputs;
return this;
}
- public boolean isPassthrough() {
- return passthrough;
- }
-
- public ArtProfileOptions setPassthrough(boolean passthrough) {
- this.passthrough = passthrough;
+ public ArtProfileOptions setEnableCompletenessCheckForTesting(
+ boolean enableCompletenessCheckForTesting) {
+ this.enableCompletenessCheckForTesting = enableCompletenessCheckForTesting;
return this;
}
}
diff --git a/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java
index b196906..d7f72ef 100644
--- a/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java
+++ b/src/main/java/com/android/tools/r8/profile/art/NonEmptyArtProfileCollection.java
@@ -10,17 +10,19 @@
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.naming.NamingLens;
import com.android.tools.r8.utils.InternalOptions;
-import com.google.common.collect.ImmutableList;
+import com.android.tools.r8.utils.ListUtils;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
+import java.util.List;
import java.util.function.Function;
public class NonEmptyArtProfileCollection extends ArtProfileCollection
implements Iterable<ArtProfile> {
- private final Collection<ArtProfile> artProfiles;
+ private final List<ArtProfile> artProfiles;
- public NonEmptyArtProfileCollection(Collection<ArtProfile> artProfiles) {
+ public NonEmptyArtProfileCollection(List<ArtProfile> artProfiles) {
this.artProfiles = artProfiles;
}
@@ -34,6 +36,10 @@
return this;
}
+ public ArtProfile getLast() {
+ return ListUtils.last(artProfiles);
+ }
+
@Override
public Iterator<ArtProfile> iterator() {
return artProfiles.iterator();
@@ -53,6 +59,14 @@
@Override
public void supplyConsumers(AppView<?> appView) {
+ if (appView.options().getArtProfileOptions().isCompletenessCheckForTestingEnabled()) {
+ assert ArtProfileCompletenessChecker.verify(appView);
+ ListUtils.removeLast(artProfiles);
+ if (artProfiles.isEmpty()) {
+ appView.setArtProfileCollection(ArtProfileCollection.empty());
+ return;
+ }
+ }
NonEmptyArtProfileCollection collection =
appView.getNamingLens().isIdentityLens()
? this
@@ -75,11 +89,10 @@
}
private NonEmptyArtProfileCollection map(Function<ArtProfile, ArtProfile> fn) {
- ImmutableList.Builder<ArtProfile> newArtProfiles =
- ImmutableList.builderWithExpectedSize(artProfiles.size());
+ List<ArtProfile> newArtProfiles = new ArrayList<>(artProfiles.size());
for (ArtProfile artProfile : artProfiles) {
newArtProfiles.add(fn.apply(artProfile));
}
- return new NonEmptyArtProfileCollection(newArtProfiles.build());
+ return new NonEmptyArtProfileCollection(newArtProfiles);
}
}
diff --git a/src/main/java/com/android/tools/r8/profile/art/PassthroughArtProfileCollection.java b/src/main/java/com/android/tools/r8/profile/art/PassthroughArtProfileCollection.java
deleted file mode 100644
index ac92f64..0000000
--- a/src/main/java/com/android/tools/r8/profile/art/PassthroughArtProfileCollection.java
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright (c) 2022, 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.profile.art;
-
-import com.android.tools.r8.TextInputStream;
-import com.android.tools.r8.TextOutputStream;
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.GraphLens;
-import com.android.tools.r8.graph.PrunedItems;
-import com.android.tools.r8.naming.NamingLens;
-import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.MutableArtProfileClassRule;
-import com.android.tools.r8.profile.art.ArtProfileBuilderUtils.MutableArtProfileMethodRule;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.UncheckedIOException;
-import java.util.function.Consumer;
-
-public class PassthroughArtProfileCollection extends ArtProfileCollection {
-
- private static final PassthroughArtProfileCollection INSTANCE =
- new PassthroughArtProfileCollection();
-
- private PassthroughArtProfileCollection() {}
-
- static PassthroughArtProfileCollection getInstance() {
- return INSTANCE;
- }
-
- @Override
- public boolean isNonEmpty() {
- return false;
- }
-
- @Override
- public NonEmptyArtProfileCollection asNonEmpty() {
- return null;
- }
-
- @Override
- public ArtProfileCollection rewrittenWithLens(GraphLens lens) {
- return this;
- }
-
- @Override
- public ArtProfileCollection rewrittenWithLens(NamingLens lens, DexItemFactory dexItemFactory) {
- return this;
- }
-
- @Override
- public void supplyConsumers(AppView<?> appView) {
- for (ArtProfileForRewriting artProfileForRewriting :
- appView.options().getArtProfileOptions().getArtProfilesForRewriting()) {
- ArtProfileProvider artProfileProvider = artProfileForRewriting.getArtProfileProvider();
- ArtProfileConsumer artProfileConsumer =
- EmptyArtProfileConsumer.orEmpty(artProfileForRewriting.getResidualArtProfileConsumer());
- supplyArtProfileConsumer(appView, artProfileConsumer, artProfileProvider);
- artProfileConsumer.finished(appView.reporter());
- }
- }
-
- private void supplyArtProfileConsumer(
- AppView<?> appView,
- ArtProfileConsumer artProfileConsumer,
- ArtProfileProvider artProfileProvider) {
- ArtProfileConsumerSupplier artProfileConsumerSupplier =
- new ArtProfileConsumerSupplier(artProfileConsumer);
- try {
- ArtProfileRuleConsumer ruleConsumer =
- EmptyArtProfileRuleConsumer.orEmpty(artProfileConsumer.getRuleConsumer());
- artProfileProvider.getArtProfile(
- new ArtProfileBuilder() {
-
- @Override
- public ArtProfileBuilder addClassRule(
- Consumer<ArtProfileClassRuleBuilder> classRuleBuilderConsumer) {
- MutableArtProfileClassRule classRule = new MutableArtProfileClassRule();
- classRuleBuilderConsumer.accept(classRule);
- ruleConsumer.acceptClassRule(
- classRule.getClassReference(), classRule.getClassRuleInfo());
- artProfileConsumerSupplier.supply(classRule);
- return this;
- }
-
- @Override
- public ArtProfileBuilder addMethodRule(
- Consumer<ArtProfileMethodRuleBuilder> methodRuleBuilderConsumer) {
- MutableArtProfileMethodRule methodRule = new MutableArtProfileMethodRule();
- methodRuleBuilderConsumer.accept(methodRule);
- ruleConsumer.acceptMethodRule(
- methodRule.getMethodReference(), methodRule.getMethodRuleInfo());
- artProfileConsumerSupplier.supply(methodRule);
- return this;
- }
-
- @Override
- public ArtProfileBuilder addHumanReadableArtProfile(
- TextInputStream textInputStream,
- Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer) {
- HumanReadableArtProfileParser.Builder parserBuilder =
- HumanReadableArtProfileParser.builder()
- .setReporter(appView.reporter())
- .setProfileBuilder(this);
- parserBuilderConsumer.accept(parserBuilder);
- HumanReadableArtProfileParser parser = parserBuilder.build();
- parser.parse(textInputStream, artProfileProvider.getOrigin());
- return this;
- }
- });
- } finally {
- artProfileConsumerSupplier.close();
- }
- }
-
- @Override
- public ArtProfileCollection withoutPrunedItems(PrunedItems prunedItems) {
- return this;
- }
-
- private static class ArtProfileConsumerSupplier {
-
- private final OutputStreamWriter outputStreamWriter;
-
- ArtProfileConsumerSupplier(ArtProfileConsumer artProfileConsumer) {
- TextOutputStream textOutputStream = artProfileConsumer.getHumanReadableArtProfileConsumer();
- this.outputStreamWriter =
- textOutputStream != null
- ? new OutputStreamWriter(
- textOutputStream.getOutputStream(), textOutputStream.getCharset())
- : null;
- ;
- }
-
- void supply(MutableArtProfileClassRule classRule) {
- if (outputStreamWriter != null) {
- try {
- classRule.writeHumanReadableRuleString(outputStreamWriter);
- outputStreamWriter.write('\n');
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
- }
-
- void supply(MutableArtProfileMethodRule methodRule) {
- if (outputStreamWriter != null) {
- try {
- methodRule.writeHumanReadableRuleString(outputStreamWriter);
- outputStreamWriter.write('\n');
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
- }
-
- void close() {
- if (outputStreamWriter != null) {
- try {
- outputStreamWriter.close();
- } catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java
index 988215e..b210019 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileAdditions.java
@@ -4,6 +4,8 @@
package com.android.tools.r8.profile.art.rewriting;
+import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
@@ -12,6 +14,7 @@
import com.android.tools.r8.profile.art.ArtProfile;
import com.android.tools.r8.profile.art.ArtProfileClassRule;
import com.android.tools.r8.profile.art.ArtProfileMethodRule;
+import com.android.tools.r8.profile.art.ArtProfileMethodRuleInfoImpl;
import com.android.tools.r8.profile.art.ArtProfileRule;
import com.google.common.collect.Sets;
import java.util.ArrayList;
@@ -47,6 +50,12 @@
this.artProfile = artProfile;
}
+ void applyIfContextIsInProfile(DexType context, Consumer<ArtProfileAdditions> fn) {
+ if (artProfile.containsClassRule(context)) {
+ fn.accept(this);
+ }
+ }
+
void applyIfContextIsInProfile(
DexMethod context, Consumer<ArtProfileAdditionsBuilder> builderConsumer) {
ArtProfileMethodRule contextMethodRule = artProfile.getMethodRule(context);
@@ -88,7 +97,12 @@
}
}
- private void addClassRule(DexType type) {
+ public ArtProfileAdditions addClassRule(DexClass clazz) {
+ addClassRule(clazz.getType());
+ return this;
+ }
+
+ public void addClassRule(DexType type) {
if (artProfile.containsClassRule(type)) {
return;
}
@@ -99,6 +113,21 @@
private void addMethodRuleFromContext(
DexMethod method, ArtProfileMethodRule contextMethodRule, MethodRuleAdditionConfig config) {
+ addMethodRule(
+ method,
+ methodRuleInfoBuilder ->
+ config.configureMethodRuleInfo(methodRuleInfoBuilder, contextMethodRule));
+ }
+
+ public ArtProfileAdditions addMethodRule(
+ DexClassAndMethod method,
+ Consumer<ArtProfileMethodRuleInfoImpl.Builder> methodRuleInfoBuilderConsumer) {
+ return addMethodRule(method.getReference(), methodRuleInfoBuilderConsumer);
+ }
+
+ public ArtProfileAdditions addMethodRule(
+ DexMethod method,
+ Consumer<ArtProfileMethodRuleInfoImpl.Builder> methodRuleInfoBuilderConsumer) {
// Create profile rule for method.
ArtProfileMethodRule.Builder methodRuleBuilder =
methodRuleAdditions.computeIfAbsent(
@@ -106,10 +135,10 @@
// Setup the rule.
synchronized (methodRuleBuilder) {
- methodRuleBuilder.acceptMethodRuleInfoBuilder(
- methodRuleInfoBuilder ->
- config.configureMethodRuleInfo(methodRuleInfoBuilder, contextMethodRule));
+ methodRuleBuilder.acceptMethodRuleInfoBuilder(methodRuleInfoBuilderConsumer);
}
+
+ return this;
}
void removeMovedMethodRule(ProgramMethod oldMethod, ProgramMethod newMethod) {
@@ -160,8 +189,8 @@
ArtProfileAdditions rewriteMethodReferences(Function<DexMethod, DexMethod> methodFn) {
ArtProfileAdditions rewrittenAdditions = new ArtProfileAdditions(artProfile);
- assert classRuleAdditions.isEmpty();
assert methodRuleRemovals.isEmpty();
+ rewrittenAdditions.classRuleAdditions.putAll(classRuleAdditions);
methodRuleAdditions.forEach(
(method, methodRuleBuilder) -> {
DexMethod newMethod = methodFn.apply(method);
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java
index 858de9d..36d235f 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileCollectionAdditions.java
@@ -43,7 +43,7 @@
return false;
}
- ConcreteArtProfileCollectionAdditions asConcrete() {
+ public ConcreteArtProfileCollectionAdditions asConcrete() {
return null;
}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingApiReferenceStubberEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingApiReferenceStubberEventConsumer.java
new file mode 100644
index 0000000..683bb93
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingApiReferenceStubberEventConsumer.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2023, 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.profile.art.rewriting;
+
+import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
+
+import com.android.tools.r8.androidapi.ApiReferenceStubberEventConsumer;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexLibraryClass;
+import com.android.tools.r8.graph.DexProgramClass;
+
+public class ArtProfileRewritingApiReferenceStubberEventConsumer
+ implements ApiReferenceStubberEventConsumer {
+
+ private final ConcreteArtProfileCollectionAdditions collectionAdditions;
+ private final ApiReferenceStubberEventConsumer parent;
+
+ private ArtProfileRewritingApiReferenceStubberEventConsumer(
+ ConcreteArtProfileCollectionAdditions collectionAdditions,
+ ApiReferenceStubberEventConsumer parent) {
+ this.collectionAdditions = collectionAdditions;
+ this.parent = parent;
+ }
+
+ public static ArtProfileRewritingApiReferenceStubberEventConsumer attach(
+ ConcreteArtProfileCollectionAdditions collectionAdditions,
+ ApiReferenceStubberEventConsumer eventConsumer) {
+ return new ArtProfileRewritingApiReferenceStubberEventConsumer(
+ collectionAdditions, eventConsumer);
+ }
+
+ @Override
+ public void acceptMockedLibraryClass(DexProgramClass mockClass, DexLibraryClass libraryClass) {
+ parent.acceptMockedLibraryClass(mockClass, libraryClass);
+ }
+
+ @Override
+ public void acceptMockedLibraryClassContext(
+ DexProgramClass mockClass, DexLibraryClass libraryClass, DexProgramClass context) {
+ collectionAdditions.applyIfContextIsInProfile(
+ context,
+ additions ->
+ additions
+ .addClassRule(mockClass)
+ .addMethodRule(mockClass.getProgramClassInitializer(), emptyConsumer()));
+ parent.acceptMockedLibraryClassContext(mockClass, libraryClass, context);
+ }
+
+ @Override
+ public void finished(AppView<?> appView) {
+ collectionAdditions.commit(appView);
+ parent.finished(appView);
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.java
new file mode 100644
index 0000000..3cc4631
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer.java
@@ -0,0 +1,90 @@
+// Copyright (c) 2023, 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.profile.art.rewriting;
+
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
+import java.util.Set;
+
+public class ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer
+ extends CfClassSynthesizerDesugaringEventConsumer {
+
+ private final ConcreteArtProfileCollectionAdditions additionsCollection;
+ private final CfClassSynthesizerDesugaringEventConsumer parent;
+
+ private ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer(
+ ConcreteArtProfileCollectionAdditions additionsCollection,
+ CfClassSynthesizerDesugaringEventConsumer parent) {
+ this.additionsCollection = additionsCollection;
+ this.parent = parent;
+ }
+
+ public static CfClassSynthesizerDesugaringEventConsumer attach(
+ ArtProfileCollectionAdditions artProfileCollectionAdditions,
+ CfClassSynthesizerDesugaringEventConsumer eventConsumer) {
+ if (artProfileCollectionAdditions.isNop()) {
+ return eventConsumer;
+ }
+ return new ArtProfileRewritingCfClassSynthesizerDesugaringEventConsumer(
+ artProfileCollectionAdditions.asConcrete(), eventConsumer);
+ }
+
+ @Override
+ public void acceptCollectionConversion(ProgramMethod arrayConversion) {
+ parent.acceptCollectionConversion(arrayConversion);
+ }
+
+ @Override
+ public void acceptWrapperProgramClass(DexProgramClass clazz) {
+ parent.acceptWrapperProgramClass(clazz);
+ }
+
+ @Override
+ public void acceptEnumConversionProgramClass(DexProgramClass clazz) {
+ parent.acceptEnumConversionProgramClass(clazz);
+ }
+
+ @Override
+ public void acceptDesugaredLibraryRetargeterDispatchProgramClass(DexProgramClass clazz) {
+ parent.acceptDesugaredLibraryRetargeterDispatchProgramClass(clazz);
+ }
+
+ @Override
+ public void acceptProgramEmulatedInterface(DexProgramClass clazz) {
+ parent.acceptProgramEmulatedInterface(clazz);
+ }
+
+ @Override
+ public void acceptRecordClass(DexProgramClass recordClass) {
+ parent.acceptRecordClass(recordClass);
+ }
+
+ @Override
+ public void acceptRecordClassContext(
+ DexProgramClass recordTagClass, DexProgramClass recordClass) {
+ additionsCollection.applyIfContextIsInProfile(
+ recordClass, additions -> additions.addClassRule(recordTagClass));
+ ProgramMethod recordTagInstanceInitializer = recordTagClass.getProgramDefaultInitializer();
+ if (recordTagInstanceInitializer != null) {
+ recordClass.forEachProgramInstanceInitializer(
+ recordInstanceInitializer ->
+ additionsCollection.applyIfContextIsInProfile(
+ recordInstanceInitializer,
+ additionsBuilder -> additionsBuilder.addRule(recordTagInstanceInitializer)));
+ }
+ parent.acceptRecordClassContext(recordTagClass, recordClass);
+ }
+
+ @Override
+ public void acceptVarHandleDesugaringClass(DexProgramClass varHandleClass) {
+ parent.acceptVarHandleDesugaringClass(varHandleClass);
+ }
+
+ @Override
+ public Set<DexProgramClass> getSynthesizedClasses() {
+ return parent.getSynthesizedClasses();
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java
index 5b54cd3..811b1f5 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfInstructionDesugaringEventConsumer.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.profile.art.rewriting;
+import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexClasspathClass;
import com.android.tools.r8.graph.DexProgramClass;
@@ -11,6 +12,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.LambdaClass;
+import com.android.tools.r8.ir.desugar.LambdaClass.Target;
import com.android.tools.r8.ir.desugar.constantdynamic.ConstantDynamicClass;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialBridgeInfo;
import java.util.List;
@@ -18,24 +20,28 @@
public class ArtProfileRewritingCfInstructionDesugaringEventConsumer
extends CfInstructionDesugaringEventConsumer {
+ private final AppView<?> appView;
private final ConcreteArtProfileCollectionAdditions additionsCollection;
private final CfInstructionDesugaringEventConsumer parent;
private ArtProfileRewritingCfInstructionDesugaringEventConsumer(
+ AppView<?> appView,
ConcreteArtProfileCollectionAdditions additionsCollection,
CfInstructionDesugaringEventConsumer parent) {
+ this.appView = appView;
this.additionsCollection = additionsCollection;
this.parent = parent;
}
public static CfInstructionDesugaringEventConsumer attach(
+ AppView<?> appView,
ArtProfileCollectionAdditions artProfileCollectionAdditions,
CfInstructionDesugaringEventConsumer eventConsumer) {
if (artProfileCollectionAdditions.isNop()) {
return eventConsumer;
}
return new ArtProfileRewritingCfInstructionDesugaringEventConsumer(
- artProfileCollectionAdditions.asConcrete(), eventConsumer);
+ appView, artProfileCollectionAdditions.asConcrete(), eventConsumer);
}
@Override
@@ -123,7 +129,7 @@
@Override
public void acceptLambdaClass(LambdaClass lambdaClass, ProgramMethod context) {
addLambdaClassAndInstanceInitializersIfSynthesizingContextIsInProfile(lambdaClass, context);
- addLambdaVirtualMethodsIfLambdaImplementationIsInProfile(lambdaClass);
+ addLambdaVirtualMethodsIfLambdaImplementationIsInProfile(lambdaClass, context);
parent.acceptLambdaClass(lambdaClass, context);
}
@@ -141,18 +147,58 @@
});
}
- private void addLambdaVirtualMethodsIfLambdaImplementationIsInProfile(LambdaClass lambdaClass) {
- additionsCollection.applyIfContextIsInProfile(
- lambdaClass.getTarget().getImplementationMethod(),
- additionsBuilder -> {
- lambdaClass
- .getLambdaProgramClass()
- .forEachProgramVirtualMethod(additionsBuilder::addRule);
- if (lambdaClass.getTarget().getCallTarget()
- != lambdaClass.getTarget().getImplementationMethod()) {
- additionsBuilder.addRule(lambdaClass.getTarget().getCallTarget());
- }
- });
+ private void addLambdaVirtualMethodsIfLambdaImplementationIsInProfile(
+ LambdaClass lambdaClass, ProgramMethod context) {
+ if (shouldConservativelyAddLambdaVirtualMethodsIfLambdaInstantiated(lambdaClass, context)) {
+ additionsCollection.applyIfContextIsInProfile(
+ context,
+ additionsBuilder ->
+ lambdaClass
+ .getLambdaProgramClass()
+ .forEachProgramVirtualMethod(additionsBuilder::addRule));
+ } else {
+ Target target = lambdaClass.getTarget();
+ additionsCollection.applyIfContextIsInProfile(
+ target.getImplementationMethod(),
+ additionsBuilder -> {
+ lambdaClass
+ .getLambdaProgramClass()
+ .forEachProgramVirtualMethod(additionsBuilder::addRule);
+ if (target.getCallTarget() != target.getImplementationMethod()) {
+ additionsBuilder.addRule(target.getCallTarget());
+ }
+ });
+ }
+ }
+
+ private boolean shouldConservativelyAddLambdaVirtualMethodsIfLambdaInstantiated(
+ LambdaClass lambdaClass, ProgramMethod context) {
+ Target target = lambdaClass.getTarget();
+ if (target.getInvokeType().isInterface() || target.getInvokeType().isVirtual()) {
+ return true;
+ }
+ if (target.getImplementationMethod().getHolderType() == context.getHolderType()) {
+ // Direct call to the same class. Only add virtual methods if the callee is in the profile.
+ return false;
+ }
+ if (appView.hasClassHierarchy()) {
+ DexClassAndMethod resolutionResult =
+ appView
+ .appInfoWithClassHierarchy()
+ .resolveMethod(target.getImplementationMethod(), target.isInterface())
+ .getResolutionPair();
+ if (resolutionResult == null || resolutionResult.isProgramMethod()) {
+ // Direct call to other method in the app. Only add virtual methods if the callee is in the
+ // profile.
+ return false;
+ }
+ // The profile does not contain non-program items. Conservatively treat the call target as
+ // being executed.
+ return true;
+ } else {
+ // Should not lookup definitions outside the current context.
+ return true;
+ }
}
@Override
@@ -217,8 +263,37 @@
}
@Override
- public void acceptRecordMethod(ProgramMethod method) {
- parent.acceptRecordMethod(method);
+ public void acceptRecordClassContext(DexProgramClass recordTagClass, ProgramMethod context) {
+ parent.acceptRecordClassContext(recordTagClass, context);
+ }
+
+ @Override
+ public void acceptRecordEqualsHelperMethod(ProgramMethod method, ProgramMethod context) {
+ additionsCollection.applyIfContextIsInProfile(
+ context, additionsBuilder -> additionsBuilder.addRule(method));
+ parent.acceptRecordEqualsHelperMethod(method, context);
+ }
+
+ @Override
+ public void acceptRecordGetFieldsAsObjectsHelperMethod(
+ ProgramMethod method, ProgramMethod context) {
+ additionsCollection.applyIfContextIsInProfile(
+ context, additionsBuilder -> additionsBuilder.addRule(method));
+ parent.acceptRecordGetFieldsAsObjectsHelperMethod(method, context);
+ }
+
+ @Override
+ public void acceptRecordHashCodeHelperMethod(ProgramMethod method, ProgramMethod context) {
+ additionsCollection.applyIfContextIsInProfile(
+ context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+ parent.acceptRecordHashCodeHelperMethod(method, context);
+ }
+
+ @Override
+ public void acceptRecordToStringHelperMethod(ProgramMethod method, ProgramMethod context) {
+ additionsCollection.applyIfContextIsInProfile(
+ context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+ parent.acceptRecordToStringHelperMethod(method, context);
}
@Override
@@ -234,11 +309,6 @@
}
@Override
- public void acceptThrowMethod(ProgramMethod method, ProgramMethod context) {
- parent.acceptThrowMethod(method, context);
- }
-
- @Override
public void acceptTwrCloseResourceMethod(ProgramMethod closeMethod, ProgramMethod context) {
additionsCollection.applyIfContextIsInProfile(
context,
@@ -247,6 +317,41 @@
}
@Override
+ public void acceptUtilityToStringIfNotNullMethod(ProgramMethod method, ProgramMethod context) {
+ parent.acceptUtilityToStringIfNotNullMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowClassCastExceptionIfNotNullMethod(
+ ProgramMethod method, ProgramMethod context) {
+ parent.acceptUtilityThrowClassCastExceptionIfNotNullMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowIllegalAccessErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ parent.acceptUtilityThrowIllegalAccessErrorMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowIncompatibleClassChangeErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ parent.acceptUtilityThrowIncompatibleClassChangeErrorMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowNoSuchMethodErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ parent.acceptUtilityThrowNoSuchMethodErrorMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowRuntimeExceptionWithMessageMethod(
+ ProgramMethod method, ProgramMethod context) {
+ parent.acceptUtilityThrowRuntimeExceptionWithMessageMethod(method, context);
+ }
+
+ @Override
public void acceptVarHandleDesugaringClass(DexProgramClass varHandleClass) {
parent.acceptVarHandleDesugaringClass(varHandleClass);
}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.java
index 4309e8b..6843852 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingCfPostProcessingDesugaringEventConsumer.java
@@ -7,11 +7,13 @@
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexClasspathClass;
+import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.desugar.CfPostProcessingDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringSyntheticHelper;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
public class ArtProfileRewritingCfPostProcessingDesugaringEventConsumer
@@ -102,6 +104,11 @@
}
@Override
+ public Set<DexMethod> getNewlyLiveMethods() {
+ return parent.getNewlyLiveMethods();
+ }
+
+ @Override
public void finalizeDesugaring() throws ExecutionException {
parent.finalizeDesugaring();
}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMethodProcessorEventConsumer.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMethodProcessorEventConsumer.java
new file mode 100644
index 0000000..325422e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ArtProfileRewritingMethodProcessorEventConsumer.java
@@ -0,0 +1,112 @@
+// Copyright (c) 2023, 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.profile.art.rewriting;
+
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
+
+public class ArtProfileRewritingMethodProcessorEventConsumer extends MethodProcessorEventConsumer {
+
+ private final ConcreteArtProfileCollectionAdditions additionsCollection;
+ private final MethodProcessorEventConsumer parent;
+
+ private ArtProfileRewritingMethodProcessorEventConsumer(
+ ConcreteArtProfileCollectionAdditions additionsCollection,
+ MethodProcessorEventConsumer parent) {
+ this.additionsCollection = additionsCollection;
+ this.parent = parent;
+ }
+
+ public static MethodProcessorEventConsumer attach(
+ ArtProfileCollectionAdditions artProfileCollectionAdditions,
+ MethodProcessorEventConsumer eventConsumer) {
+ if (artProfileCollectionAdditions.isNop()) {
+ return eventConsumer;
+ }
+ return new ArtProfileRewritingMethodProcessorEventConsumer(
+ artProfileCollectionAdditions.asConcrete(), eventConsumer);
+ }
+
+ @Override
+ public void acceptEnumUnboxerCheckNotZeroContext(ProgramMethod method, ProgramMethod context) {
+ additionsCollection.applyIfContextIsInProfile(
+ context, additionsBuilder -> additionsBuilder.addRule(method));
+ parent.acceptEnumUnboxerCheckNotZeroContext(method, context);
+ }
+
+ @Override
+ public void acceptEnumUnboxerLocalUtilityClassMethodContext(
+ ProgramMethod method, ProgramMethod context) {
+ additionsCollection.applyIfContextIsInProfile(
+ context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+ parent.acceptEnumUnboxerLocalUtilityClassMethodContext(method, context);
+ }
+
+ @Override
+ public void acceptEnumUnboxerSharedUtilityClassMethodContext(
+ ProgramMethod method, ProgramMethod context) {
+ additionsCollection.applyIfContextIsInProfile(
+ context,
+ additionsBuilder -> {
+ additionsBuilder.addRule(method).addRule(method.getHolder());
+ method.getHolder().acceptProgramClassInitializer(additionsBuilder::addRule);
+ });
+ parent.acceptEnumUnboxerSharedUtilityClassMethodContext(method, context);
+ }
+
+ @Override
+ public void acceptInstanceInitializerOutline(ProgramMethod method, ProgramMethod context) {
+ additionsCollection.applyIfContextIsInProfile(
+ context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+ parent.acceptInstanceInitializerOutline(method, context);
+ }
+
+ @Override
+ public void acceptUtilityToStringIfNotNullMethod(ProgramMethod method, ProgramMethod context) {
+ additionsCollection.applyIfContextIsInProfile(
+ context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+ parent.acceptUtilityToStringIfNotNullMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowClassCastExceptionIfNotNullMethod(
+ ProgramMethod method, ProgramMethod context) {
+ additionsCollection.applyIfContextIsInProfile(
+ context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+ parent.acceptUtilityThrowClassCastExceptionIfNotNullMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowIllegalAccessErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ additionsCollection.applyIfContextIsInProfile(
+ context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+ parent.acceptUtilityThrowIllegalAccessErrorMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowIncompatibleClassChangeErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ additionsCollection.applyIfContextIsInProfile(
+ context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+ parent.acceptUtilityThrowIncompatibleClassChangeErrorMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowNoSuchMethodErrorMethod(
+ ProgramMethod method, ProgramMethod context) {
+ additionsCollection.applyIfContextIsInProfile(
+ context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+ parent.acceptUtilityThrowNoSuchMethodErrorMethod(method, context);
+ }
+
+ @Override
+ public void acceptUtilityThrowRuntimeExceptionWithMessageMethod(
+ ProgramMethod method, ProgramMethod context) {
+ additionsCollection.applyIfContextIsInProfile(
+ context, additionsBuilder -> additionsBuilder.addRule(method).addRule(method.getHolder()));
+ parent.acceptUtilityThrowRuntimeExceptionWithMessageMethod(method, context);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java b/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java
index 9c75c21..85c54e1 100644
--- a/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java
+++ b/src/main/java/com/android/tools/r8/profile/art/rewriting/ConcreteArtProfileCollectionAdditions.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.profile.art.rewriting;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexClassAndMethod;
import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.profile.art.ArtProfile;
import com.android.tools.r8.profile.art.ArtProfileCollection;
import com.android.tools.r8.profile.art.NonEmptyArtProfileCollection;
@@ -35,6 +37,17 @@
}
void applyIfContextIsInProfile(
+ DexClass context, Consumer<ArtProfileAdditions> additionsConsumer) {
+ applyIfContextIsInProfile(context.getType(), additionsConsumer);
+ }
+
+ void applyIfContextIsInProfile(DexType type, Consumer<ArtProfileAdditions> additionsConsumer) {
+ for (ArtProfileAdditions artProfileAdditions : additionsCollection) {
+ artProfileAdditions.applyIfContextIsInProfile(type, additionsConsumer);
+ }
+ }
+
+ public void applyIfContextIsInProfile(
DexClassAndMethod context, Consumer<ArtProfileAdditionsBuilder> builderConsumer) {
applyIfContextIsInProfile(context.getReference(), builderConsumer);
}
@@ -48,7 +61,7 @@
}
@Override
- ConcreteArtProfileCollectionAdditions asConcrete() {
+ public ConcreteArtProfileCollectionAdditions asConcrete() {
return this;
}
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 06db3b7..bbdb059 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3946,6 +3946,10 @@
assert old == null || old == clazz;
}
+ public Set<DexMethod> getNewlyLiveMethods() {
+ return liveMethods.keySet();
+ }
+
public void addLiveMethod(ProgramMethod method) {
DexMethod signature = method.getDefinition().getReference();
ProgramMethod old = liveMethods.put(signature, method);
@@ -4579,9 +4583,8 @@
InterfaceMethodProcessorFacade interfaceDesugaring =
desugaring.getInterfaceMethodPostProcessingDesugaringR8(
ExcludeDexResources, liveMethods::contains, interfaceProcessor);
- CfPostProcessingDesugaringCollection.create(appView, interfaceDesugaring)
- .postProcessingDesugaring(
- liveTypes.items, liveMethods::contains, eventConsumer, executorService);
+ CfPostProcessingDesugaringCollection.create(appView, interfaceDesugaring, liveMethods::contains)
+ .postProcessingDesugaring(liveTypes.items, eventConsumer, executorService);
if (syntheticAdditions.isEmpty()) {
return;
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
index 955069f..3eb32b9 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfiguration.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.position.Position;
import com.android.tools.r8.utils.InternalOptions.PackageObfuscationMode;
import com.android.tools.r8.utils.Reporter;
+import com.android.tools.r8.utils.StringUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import java.nio.file.Path;
@@ -671,11 +672,11 @@
StringBuilder builder = new StringBuilder();
if (!keepAttributes.isEmpty()) {
keepAttributes.append(builder);
- builder.append('\n');
+ builder.append(StringUtils.LINE_SEPARATOR);
}
for (ProguardConfigurationRule rule : rules) {
rule.append(builder);
- builder.append('\n');
+ builder.append(StringUtils.LINE_SEPARATOR);
}
return builder.toString();
}
diff --git a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
index bf4029e..d8ae369 100644
--- a/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
+++ b/src/main/java/com/android/tools/r8/synthesis/SyntheticItems.java
@@ -91,7 +91,7 @@
new ContextsForGlobalSyntheticsInSingleOutputMode() {
@Override
public void addGlobalContexts(
- DexType globalType, Collection<ProgramDefinition> contexts) {
+ DexType globalType, Collection<? extends ProgramDefinition> contexts) {
throw new Unreachable("Unexpected attempt to add globals to non-desugaring build.");
}
};
@@ -114,7 +114,7 @@
void forEach(BiConsumer<DexType, Set<DexType>> fn);
- void addGlobalContexts(DexType globalType, Collection<ProgramDefinition> contexts);
+ void addGlobalContexts(DexType globalType, Collection<? extends ProgramDefinition> contexts);
}
private static class ContextsForGlobalSyntheticsInSingleOutputMode
@@ -131,7 +131,8 @@
}
@Override
- public void addGlobalContexts(DexType globalType, Collection<ProgramDefinition> contexts) {
+ public void addGlobalContexts(
+ DexType globalType, Collection<? extends ProgramDefinition> contexts) {
// contexts are ignored in single output modes.
}
}
@@ -152,7 +153,8 @@
}
@Override
- public void addGlobalContexts(DexType globalType, Collection<ProgramDefinition> contexts) {
+ public void addGlobalContexts(
+ DexType globalType, Collection<? extends ProgramDefinition> contexts) {
Set<DexType> contextReferences =
globalContexts.computeIfAbsent(globalType, k -> ConcurrentHashMap.newKeySet());
contexts.forEach(definition -> contextReferences.add(definition.getContextType()));
@@ -976,7 +978,7 @@
Supplier<MissingGlobalSyntheticsConsumerDiagnostic> diagnosticSupplier,
SyntheticKindSelector kindSelector,
DexType globalType,
- Collection<ProgramDefinition> contexts,
+ Collection<? extends ProgramDefinition> contexts,
AppView<?> appView,
Consumer<SyntheticProgramClassBuilder> fn,
Consumer<DexProgramClass> onCreationConsumer) {
@@ -1033,7 +1035,8 @@
pending.definitions.put(definition.getHolder().getType(), definition);
}
- private void addGlobalContexts(DexType globalType, Collection<ProgramDefinition> contexts) {
+ private void addGlobalContexts(
+ DexType globalType, Collection<? extends ProgramDefinition> contexts) {
globalContexts.addGlobalContexts(globalType, contexts);
}
diff --git a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
index 69c9714..5be322e 100644
--- a/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
+++ b/src/main/java/com/android/tools/r8/utils/AndroidApiLevel.java
@@ -43,14 +43,17 @@
S(31),
Sv2(32),
T(33),
- MASTER(34), // API level for master is tentative.
+ U(34),
+ MASTER(35), // API level for master is tentative.
ANDROID_PLATFORM(10000);
// When updating LATEST and a new version goes stable, add a new api-versions.xml to third_party
// and update the version and generated jar in AndroidApiDatabaseBuilderGeneratorTest.
- // TODO(b/204738868): Update API database for Sv2 / T when they are ready.
public static final AndroidApiLevel LATEST = T;
+ // TODO(b/268601605): When adding U to the test matrix, set this to LATEST.
+ public static final AndroidApiLevel API_DATABASE_LEVEL = U;
+
private final int level;
AndroidApiLevel(int level) {
@@ -174,6 +177,8 @@
case 33:
return T;
case 34:
+ return U;
+ case 35:
return MASTER;
case 10000:
return ANDROID_PLATFORM;
diff --git a/src/main/java/com/android/tools/r8/utils/DexVersion.java b/src/main/java/com/android/tools/r8/utils/DexVersion.java
index 443b9e7..d094cf7 100644
--- a/src/main/java/com/android/tools/r8/utils/DexVersion.java
+++ b/src/main/java/com/android/tools/r8/utils/DexVersion.java
@@ -42,6 +42,7 @@
// version.
case ANDROID_PLATFORM:
case MASTER:
+ case U:
case T:
case Sv2:
case S:
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 2c06cad..e7dfeee 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -1012,6 +1012,11 @@
// If non-null, configuration must be passed to the consumer.
public StringConsumer configurationConsumer = null;
+ public void resetDesugaredLibrarySpecificationForTesting() {
+ loadMachineDesugaredLibrarySpecification = null;
+ machineDesugaredLibrarySpecification = MachineDesugaredLibrarySpecification.empty();
+ }
+
public void setDesugaredLibrarySpecification(DesugaredLibrarySpecification specification) {
if (specification.isEmpty()) {
return;
diff --git a/src/main/java/com/android/tools/r8/utils/MapUtils.java b/src/main/java/com/android/tools/r8/utils/MapUtils.java
index 94b9bfe..e0a915f 100644
--- a/src/main/java/com/android/tools/r8/utils/MapUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MapUtils.java
@@ -61,6 +61,11 @@
map.entrySet().removeIf(entry -> predicate.test(entry.getKey(), entry.getValue()));
}
+ public static <K, V> V removeOrDefault(Map<K, V> map, K key, V defaultValue) {
+ V value = map.remove(key);
+ return value != null ? value : defaultValue;
+ }
+
public static String toString(Map<?, ?> map) {
return StringUtils.join(
",", map.entrySet(), entry -> entry.getKey() + ":" + entry.getValue(), BraceType.TUBORG);
diff --git a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
index 251edcc..558ea43 100644
--- a/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/MethodReferenceUtils.java
@@ -17,6 +17,7 @@
import com.android.tools.r8.references.TypeReference;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
@@ -74,6 +75,11 @@
return Reference.method(type, "<init>", Collections.emptyList(), null);
}
+ public static MethodReference instanceConstructor(
+ ClassReference type, TypeReference... formalTypes) {
+ return Reference.method(type, "<init>", Arrays.asList(formalTypes), null);
+ }
+
public static int compare(MethodReference methodReference, ClassReference other) {
return ClassReferenceUtils.compare(other, methodReference) * -1;
}
diff --git a/src/main/java/com/android/tools/r8/utils/SemanticVersion.java b/src/main/java/com/android/tools/r8/utils/SemanticVersion.java
index e8c0960..0bd7b67 100644
--- a/src/main/java/com/android/tools/r8/utils/SemanticVersion.java
+++ b/src/main/java/com/android/tools/r8/utils/SemanticVersion.java
@@ -5,7 +5,11 @@
import java.util.Objects;
-public class SemanticVersion {
+public class SemanticVersion implements Comparable<SemanticVersion> {
+
+ private static SemanticVersion MIN = SemanticVersion.create(0, 0, 0);
+ private static SemanticVersion MAX =
+ SemanticVersion.create(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
public static SemanticVersion parse(String version) {
int majorEnd = version.indexOf('.');
@@ -61,6 +65,14 @@
return new SemanticVersion(major, minor, patch, prerelease);
}
+ public static SemanticVersion min() {
+ return MIN;
+ }
+
+ public static SemanticVersion max() {
+ return MAX;
+ }
+
public int getMajor() {
return major;
}
@@ -104,4 +116,12 @@
public String toString() {
return "" + major + "." + minor + "." + patch + (prerelease != null ? "-" + prerelease : "");
}
+
+ @Override
+ public int compareTo(SemanticVersion other) {
+ if (equals(other)) {
+ return 0;
+ }
+ return isNewerOrEqual(other) ? -1 : 1;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/StringUtils.java b/src/main/java/com/android/tools/r8/utils/StringUtils.java
index a5b9936..efe43d3 100644
--- a/src/main/java/com/android/tools/r8/utils/StringUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/StringUtils.java
@@ -404,4 +404,12 @@
}
return stringToCapitalize.substring(0, 1).toUpperCase() + stringToCapitalize.substring(1);
}
+
+ public static int indexOf(String s, char ch1, char ch2) {
+ int i1 = s.indexOf(ch1);
+ int i2 = s.indexOf(ch2);
+ if (i1 == -1) return i2;
+ if (i2 == -1) return i1;
+ return Math.min(i1, i2);
+ }
}
diff --git a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
index 7f67d3d..b4ec7c9 100644
--- a/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
+++ b/src/main/java/com/android/tools/r8/utils/positions/MappedPositionToClassNameMapperBuilder.java
@@ -210,9 +210,13 @@
OneShotCollectionConsumer<MappingInformation> methodSpecificMappingInformation =
OneShotCollectionConsumer.wrap(new ArrayList<>());
- if (method.getDefinition().isD8R8Synthesized()
- || (!mappedPositions.isEmpty()
- && mappedPositions.get(0).getPosition().isD8R8Synthesized())) {
+ // We only do global synthetic classes when using names from the library. For such classes it
+ // is important that we do not filter out stack frames since they could appear from concrete
+ // classes in the library. Additionally, this is one place where it is helpful for developers
+ // to also get reported synthesized frames since stubbing can change control-flow and
+ // exceptions.
+ if (isD8R8Synthesized(method, mappedPositions)
+ && !appView.getSyntheticItems().isGlobalSyntheticClass(method.getHolder())) {
methodSpecificMappingInformation.add(CompilerSynthesizedMappingInformation.getInstance());
}
@@ -369,6 +373,12 @@
return this;
}
+ private boolean isD8R8Synthesized(ProgramMethod method, List<MappedPosition> mappedPositions) {
+ return method.getDefinition().isD8R8Synthesized()
+ || (!mappedPositions.isEmpty()
+ && mappedPositions.get(0).getPosition().isD8R8Synthesized());
+ }
+
private MethodReference computeMappedMethod(DexMethod current, AppView<?> appView) {
NamingLens namingLens = appView.getNamingLens();
DexMethod renamedMethodSignature =
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index c4cb889..25628d7 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -38,6 +38,7 @@
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
+import com.android.tools.r8.utils.SemanticVersion;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.nio.file.Path;
@@ -819,4 +820,9 @@
builder.addStartupProfileProviders(startupProfileProviders);
return self();
}
+
+ public T setFakeCompilerVersion(SemanticVersion version) {
+ getBuilder().setFakeCompilerVersion(version);
+ return self();
+ }
}
diff --git a/src/test/java/com/android/tools/r8/R8TestCompileResult.java b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
index 679584b..1ca26f0 100644
--- a/src/test/java/com/android/tools/r8/R8TestCompileResult.java
+++ b/src/test/java/com/android/tools/r8/R8TestCompileResult.java
@@ -16,6 +16,7 @@
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.codeinspector.ClassSubject;
@@ -98,7 +99,12 @@
@Override
public CodeInspector inspector() throws IOException {
- return new CodeInspector(app, proguardMap);
+ return inspector(null);
+ }
+
+ public CodeInspector inspector(Consumer<InternalOptions> debugOptionsConsumer)
+ throws IOException {
+ return new CodeInspector(app, proguardMap, debugOptionsConsumer);
}
private CodeInspector featureInspector(Path feature) throws IOException {
diff --git a/src/test/java/com/android/tools/r8/TestParameters.java b/src/test/java/com/android/tools/r8/TestParameters.java
index 1074507..aabdd99 100644
--- a/src/test/java/com/android/tools/r8/TestParameters.java
+++ b/src/test/java/com/android/tools/r8/TestParameters.java
@@ -94,6 +94,17 @@
return false;
}
+ public boolean canUseRecords() {
+ assert isCfRuntime() || isDexRuntime();
+ return isCfRuntime() && asCfRuntime().isNewerThanOrEqual(CfVm.JDK14);
+ }
+
+ public boolean canUseRecordsWhenDesugaring() {
+ assert isCfRuntime() || isDexRuntime();
+ assert apiLevel != null;
+ return false;
+ }
+
// Convenience predicates.
public boolean isDexRuntime() {
return runtime.isDex();
@@ -182,6 +193,15 @@
return this;
}
+ public TestParameters assumeJvmTestParameters() {
+ assertFalse(
+ "No need to use assumeR8TestParameters() when not using api levels for CF",
+ apiLevel == null);
+ assumeCfRuntime();
+ assumeTrue(representativeApiLevelForRuntime);
+ return this;
+ }
+
public TestParameters assumeR8TestParameters() {
assertFalse(
"No need to use assumeR8TestParameters() when not using api levels for CF",
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
index 423850e..b8d3f1f 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGenerator.java
@@ -143,6 +143,7 @@
Map<DexReference, AndroidApiLevel> referenceMap,
Path androidJar) {
Map<DexType, String> missingMemberInformation = new IdentityHashMap<>();
+ DexItemFactory factory = appView.dexItemFactory();
for (DexLibraryClass clazz : appView.app().asDirect().libraryClasses()) {
ParsedApiClass parsedApiClass = lookupMap.get(clazz.getClassReference());
if (parsedApiClass == null) {
@@ -168,7 +169,7 @@
method -> {
if (method.getAccessFlags().isPublic()
&& referenceMap.get(method.getReference()) == null
- && !appView.dexItemFactory().objectMembers.isObjectMember(method.getReference())) {
+ && !factory.objectMembers.isObjectMember(method.getReference())) {
classBuilder.append(" ").append(method).append(" is missing\n");
}
});
@@ -177,22 +178,30 @@
}
}
+ Set<DexType> expectedMissingMembers = new HashSet<>();
// api-versions.xml do not encode all members of StringBuffers and StringBuilders, check that we
// only have missing definitions for those two classes.
- assert missingMemberInformation.size() == 7;
- assert missingMemberInformation.containsKey(appView.dexItemFactory().stringBufferType);
- assert missingMemberInformation.containsKey(appView.dexItemFactory().stringBuilderType);
+ expectedMissingMembers.add(factory.stringBufferType);
+ expectedMissingMembers.add(factory.stringBuilderType);
// TODO(b/231126636): api-versions.xml has missing definitions for the below classes.
- assert missingMemberInformation.containsKey(
- appView.dexItemFactory().createType("Ljava/util/concurrent/ConcurrentHashMap$KeySetView;"));
- assert missingMemberInformation.containsKey(
- appView.dexItemFactory().createType("Ljava/time/chrono/ThaiBuddhistDate;"));
- assert missingMemberInformation.containsKey(
- appView.dexItemFactory().createType("Ljava/time/chrono/HijrahDate;"));
- assert missingMemberInformation.containsKey(
- appView.dexItemFactory().createType("Ljava/time/chrono/JapaneseDate;"));
- assert missingMemberInformation.containsKey(
- appView.dexItemFactory().createType("Ljava/time/chrono/MinguoDate;"));
+ expectedMissingMembers.add(
+ factory.createType("Ljava/util/concurrent/ConcurrentHashMap$KeySetView;"));
+ expectedMissingMembers.add(factory.createType("Ljava/time/chrono/ThaiBuddhistDate;"));
+ expectedMissingMembers.add(factory.createType("Ljava/time/chrono/HijrahDate;"));
+ expectedMissingMembers.add(factory.createType("Ljava/time/chrono/JapaneseDate;"));
+ expectedMissingMembers.add(factory.createType("Ljava/time/chrono/MinguoDate;"));
+ expectedMissingMembers.add(factory.createType("Landroid/nfc/tech/NfcV;"));
+ expectedMissingMembers.add(factory.createType("Landroid/nfc/tech/IsoDep;"));
+ expectedMissingMembers.add(factory.createType("Landroid/nfc/tech/MifareUltralight;"));
+ expectedMissingMembers.add(factory.createType("Landroid/nfc/tech/MifareClassic;"));
+ expectedMissingMembers.add(factory.createType("Landroid/nfc/tech/NdefFormatable;"));
+ expectedMissingMembers.add(factory.createType("Landroid/nfc/tech/NfcA;"));
+ expectedMissingMembers.add(factory.createType("Landroid/nfc/tech/NfcBarcode;"));
+ expectedMissingMembers.add(factory.createType("Landroid/nfc/tech/NfcF;"));
+ expectedMissingMembers.add(factory.createType("Landroid/nfc/tech/NfcB;"));
+ expectedMissingMembers.add(factory.createType("Landroid/nfc/tech/Ndef;"));
+ expectedMissingMembers.add(factory.createType("Landroid/webkit/CookieSyncManager;"));
+ assertEquals(expectedMissingMembers, missingMemberInformation.keySet());
return true;
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
index 29c9709..285d96a 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiHashingDatabaseBuilderGeneratorTest.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.InternalOptions;
@@ -46,7 +47,7 @@
.resolve("new_api_database.ser");
// Update the API_LEVEL below to have the database generated for a new api level.
- private static final AndroidApiLevel API_LEVEL = AndroidApiLevel.LATEST;
+ private static final AndroidApiLevel API_LEVEL = AndroidApiLevel.API_DATABASE_LEVEL;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
@@ -94,18 +95,16 @@
parsedApiClasses.forEach(
apiClass -> {
apiClass.visitFieldReferences(
- ((apiLevel, fieldReferences) -> {
- fieldReferences.forEach(field -> numberOfFields.increment());
- }));
+ ((apiLevel, fieldReferences) ->
+ fieldReferences.forEach(field -> numberOfFields.increment())));
apiClass.visitMethodReferences(
- ((apiLevel, methodReferences) -> {
- methodReferences.forEach(field -> numberOfMethods.increment());
- }));
+ ((AndroidApiLevel apiLevel, List<MethodReference> methodReferences) ->
+ methodReferences.forEach(field -> numberOfMethods.increment())));
});
// These numbers will change when updating api-versions.xml
- assertEquals(5272, parsedApiClasses.size());
- assertEquals(27868, numberOfFields.get());
- assertEquals(42268, numberOfMethods.get());
+ assertEquals(5635, parsedApiClasses.size());
+ assertEquals(29017, numberOfFields.get());
+ assertEquals(44107, numberOfMethods.get());
}
@Test
diff --git a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
index 97f6cd0..9f80832 100644
--- a/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
+++ b/src/test/java/com/android/tools/r8/apimodel/AndroidApiVersionsXmlParser.java
@@ -19,9 +19,11 @@
import java.io.File;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.TreeMap;
import java.util.function.BiConsumer;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -48,11 +50,20 @@
return parsedApiClass;
}
+ private Set<String> getDeletedTypesMissingRemovedAttribute() {
+ Set<String> removedTypeNames = new HashSet<>();
+ if (maxApiLevel.isGreaterThanOrEqualTo(AndroidApiLevel.U)) {
+ removedTypeNames.add("com.android.internal.util.Predicate");
+ }
+ return removedTypeNames;
+ }
+
private void readApiVersionsXmlFile() throws Exception {
CodeInspector inspector = new CodeInspector(ToolHelper.getAndroidJar(maxApiLevel));
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Document document = factory.newDocumentBuilder().parse(apiVersionsXml);
NodeList classes = document.getElementsByTagName("class");
+ Set<String> exemptionList = getDeletedTypesMissingRemovedAttribute();
for (int i = 0; i < classes.getLength(); i++) {
Node node = classes.item(i);
assert node.getNodeType() == Node.ELEMENT_NODE;
@@ -61,9 +72,10 @@
ClassSubject clazz = inspector.clazz(type);
if (!clazz.isPresent()) {
if (!clazz.getOriginalName().startsWith("android.test")
- && !clazz.getOriginalName().startsWith("junit")) {
- assert hasRemoved(node);
- assert getRemoved(node).isLessThanOrEqualTo(maxApiLevel);
+ && !clazz.getOriginalName().startsWith("junit")
+ && node.getAttributes().getNamedItem("module") == null) {
+ assert exemptionList.contains(type) || hasRemoved(node);
+ assert exemptionList.contains(type) || getRemoved(node).isLessThanOrEqualTo(maxApiLevel);
}
continue;
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelMockRetraceTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockRetraceTest.java
new file mode 100644
index 0000000..edd4d27
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelMockRetraceTest.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2023, 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.apimodel;
+
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
+import static com.android.tools.r8.apimodel.ApiModelingTestHelper.verifyThat;
+import static com.android.tools.r8.naming.retrace.StackTrace.containsLine;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+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.naming.retrace.StackTrace.EquivalenceWithoutFileNameAndLineNumber;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+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 ApiModelMockRetraceTest extends TestBase {
+
+ private final AndroidApiLevel mockLevel = AndroidApiLevel.M;
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ private boolean addToBootClasspath() {
+ return parameters.isCfRuntime()
+ || parameters.getRuntime().maxSupportedApiLevel().isGreaterThanOrEqualTo(mockLevel);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ testForR8(parameters.getBackend())
+ .addProgramClasses(Main.class, ProgramClass.class)
+ .addLibraryClasses(LibraryClass.class)
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
+ .addAndroidBuildVersion()
+ .apply(ApiModelingTestHelper::enableStubbingOfClasses)
+ .apply(setMockApiLevelForClass(LibraryClass.class, mockLevel))
+ .addKeepMainRule(Main.class)
+ .addKeepClassRules(ProgramClass.class)
+ .compile()
+ .inspect(this::inspect)
+ .applyIf(addToBootClasspath(), b -> b.addBootClasspathClasses(LibraryClass.class))
+ .run(parameters.getRuntime(), Main.class)
+ .apply(this::checkOutput);
+ }
+
+ private void inspect(CodeInspector inspector) {
+ verifyThat(inspector, parameters, LibraryClass.class).stubbedUntil(mockLevel);
+ }
+
+ private void checkOutput(SingleTestRunResult<?> runResult) {
+ StackTraceLine clinitFrame =
+ StackTraceLine.builder()
+ .setClassName(typeName(LibraryClass.class))
+ .setMethodName("<clinit>")
+ .build();
+ EquivalenceWithoutFileNameAndLineNumber equivalence =
+ EquivalenceWithoutFileNameAndLineNumber.get();
+ runResult.inspectOriginalStackTrace(
+ originalStackTrace ->
+ assertThat(originalStackTrace, containsLine(clinitFrame, equivalence)));
+ if (parameters.isCfRuntime()) {
+ runResult
+ .assertFailureWithErrorThatThrows(ExceptionInInitializerError.class)
+ .assertFailureWithErrorThatThrows(ArithmeticException.class)
+ .inspectStackTrace(
+ stackTrace -> assertThat(stackTrace, containsLine(clinitFrame, equivalence)));
+ } else {
+ runResult
+ .applyIf(
+ addToBootClasspath(),
+ result ->
+ result
+ .assertFailureWithErrorThatThrows(ExceptionInInitializerError.class)
+ .assertFailureWithErrorThatThrows(ArithmeticException.class),
+ result -> result.assertFailureWithErrorThatThrows(NoClassDefFoundError.class))
+ .inspectStackTrace(
+ stackTrace -> assertThat(stackTrace, containsLine(clinitFrame, equivalence)));
+ }
+ }
+
+ // Only present from 23.
+ public static class LibraryClass {
+
+ public static final int VALUE;
+
+ static {
+ VALUE = 2 / (System.currentTimeMillis() == 0 ? 1 : 0);
+ }
+ }
+
+ public static class ProgramClass extends LibraryClass {}
+
+ public static class Main {
+
+ public static void main(String[] args) throws Exception {
+ // Trigger a CL init that will either throw in the program stub or the library CL init.
+ Class.forName(LibraryClass.class.getName());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
index 1ca4309..1a744bb 100644
--- a/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
+++ b/src/test/java/com/android/tools/r8/desugar/backports/TestBackportedNotPresentInAndroidJar.java
@@ -82,6 +82,9 @@
if (apiLevel == AndroidApiLevel.MASTER) {
continue;
}
+ if (apiLevel == AndroidApiLevel.U) {
+ continue;
+ }
if (apiLevel == AndroidApiLevel.T) {
continue;
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
index a54633b..a6a0535 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/ExtractWrapperTypesTest.java
@@ -498,10 +498,10 @@
libraryDesugaringSpecification.getSpecification(),
libraryDesugaringSpecification.getDesugarJdkLibs(),
out);
- desugaredApi.run(targetApi.getLevel());
+ AndroidApiLevel compileApi = desugaredApi.run();
return new CodeInspector(
- out.resolve("compile_api_level_" + targetApi.getLevel())
- .resolve("desugared_apis_" + targetApi.getLevel() + "_" + minApi.getLevel() + ".jar"));
+ out.resolve("compile_api_level_" + compileApi.getLevel())
+ .resolve("desugared_apis_" + compileApi.getLevel() + "_" + minApi.getLevel() + ".jar"));
}
private boolean addType(
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/GuavaMultiSetSpliteratorTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/GuavaMultiSetSpliteratorTest.java
new file mode 100644
index 0000000..c84d49f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/GuavaMultiSetSpliteratorTest.java
@@ -0,0 +1,189 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.D8_L8SHRINK;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification.R8_L8SHRINK;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8Jdk11;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.desugar.desugaredlibrary.test.CompilationSpecification;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMultiset;
+import com.google.common.collect.Multiset;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Assume;
+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 GuavaMultiSetSpliteratorTest extends DesugaredLibraryTestBase {
+
+ private final TestParameters parameters;
+ private final LibraryDesugaringSpecification libraryDesugaringSpecification;
+ private final CompilationSpecification compilationSpecification;
+
+ @Parameters(name = "{0}, spec: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters()
+ .withDexRuntimes()
+ .withApiLevel(AndroidApiLevel.L)
+ .withAllApiLevels()
+ .build(),
+ getJdk8Jdk11(),
+ ImmutableList.of(D8_L8SHRINK, R8_L8SHRINK));
+ }
+
+ public GuavaMultiSetSpliteratorTest(
+ TestParameters parameters,
+ LibraryDesugaringSpecification libraryDesugaringSpecification,
+ CompilationSpecification compilationSpecification) {
+ this.parameters = parameters;
+ this.libraryDesugaringSpecification = libraryDesugaringSpecification;
+ this.compilationSpecification = compilationSpecification;
+ }
+
+ @Test
+ public void testGuava() throws Throwable {
+ if (!compilationSpecification.isProgramShrink()) {
+ // We need multidex for non shrinking build.
+ Assume.assumeTrue(parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.L));
+ }
+ testForDesugaredLibrary(parameters, libraryDesugaringSpecification, compilationSpecification)
+ .addProgramFiles(ToolHelper.DEPS)
+ .addInnerClasses(getClass())
+ .addOptionsModification(opt -> opt.ignoreMissingClasses = true)
+ .allowDiagnosticWarningMessages()
+ .addKeepMainRule(Main.class)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("17744", "NullPointerException");
+ }
+
+ public static class Main {
+
+ public static void main(String[] args) {
+ System.out.println(ImmutableMultiset.of().spliterator().characteristics());
+ try {
+ System.out.println(new MyMultiSet<>().spliterator().characteristics());
+ } catch (Exception e) {
+ System.out.println(e.getClass().getSimpleName());
+ }
+ }
+ }
+
+ public static class MyMultiSet<E> implements Multiset<E> {
+
+ @Override
+ public int size() {
+ return 0;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return false;
+ }
+
+ @Override
+ public int count(@Nullable Object element) {
+ return 0;
+ }
+
+ @Override
+ public int add(@Nullable E element, int occurrences) {
+ return 0;
+ }
+
+ @Override
+ public boolean add(E element) {
+ return false;
+ }
+
+ @Override
+ public int remove(@Nullable Object element, int occurrences) {
+ return 0;
+ }
+
+ @Override
+ public boolean remove(@Nullable Object element) {
+ return false;
+ }
+
+ @Override
+ public int setCount(E element, int count) {
+ return 0;
+ }
+
+ @Override
+ public boolean setCount(E element, int oldCount, int newCount) {
+ return false;
+ }
+
+ @Override
+ public Set<E> elementSet() {
+ return null;
+ }
+
+ @Override
+ public Set<Entry<E>> entrySet() {
+ return null;
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return null;
+ }
+
+ @NotNull
+ @Override
+ public Object[] toArray() {
+ return new Object[0];
+ }
+
+ @NotNull
+ @Override
+ public <T> T[] toArray(@NotNull T[] ts) {
+ return null;
+ }
+
+ @Override
+ public boolean contains(@Nullable Object element) {
+ return false;
+ }
+
+ @Override
+ public boolean containsAll(Collection<?> elements) {
+ return false;
+ }
+
+ @Override
+ public boolean addAll(@NotNull Collection<? extends E> collection) {
+ return false;
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ return false;
+ }
+
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ return false;
+ }
+
+ @Override
+ public void clear() {}
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
index a33e2d3..a29a6d5 100644
--- a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/LintFilesTest.java
@@ -4,9 +4,11 @@
package com.android.tools.r8.desugar.desugaredlibrary;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_LEGACY;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_MINIMAL;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK8;
-import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8AndAll3Jdk11;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -18,11 +20,13 @@
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecification;
import com.android.tools.r8.ir.desugar.desugaredlibrary.DesugaredLibrarySpecificationParser;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.GenerateHtmlDoc;
import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.GenerateLintFiles;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
+import com.google.common.collect.ImmutableList;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -40,9 +44,11 @@
private List<String> lintContents;
- @Parameters(name = "{0}, spec: {1}")
+ @Parameters(name = "{1}")
public static List<Object[]> data() {
- return buildParameters(getTestParameters().withNoneRuntime().build(), getJdk8AndAll3Jdk11());
+ return buildParameters(
+ getTestParameters().withNoneRuntime().build(),
+ ImmutableList.of(JDK8, JDK11_MINIMAL, JDK11, JDK11_PATH, JDK11_LEGACY));
}
public LintFilesTest(
@@ -80,35 +86,21 @@
assertTrue(supportsAllMethodsOf("java/util/Optional"));
assertTrue(supportsAllMethodsOf("java/util/OptionalInt"));
- // No parallel* methods pre L, and all stream methods supported from L.
+ // No parallel* methods pre L, Stream are never fully supported due to takeWhile/dropWhile.
assertEquals(
minApiLevel == AndroidApiLevel.L,
supportsMethodButNotAllMethodsInClass(
"java/util/Collection#parallelStream()Ljava/util/stream/Stream;"));
assertEquals(
- minApiLevel == AndroidApiLevel.L, supportsAllMethodsOf("java/util/stream/DoubleStream"));
- assertFalse(
+ minApiLevel == AndroidApiLevel.L,
supportsMethodButNotAllMethodsInClass(
"java/util/stream/DoubleStream#parallel()Ljava/util/stream/DoubleStream;"));
- assertFalse(
- supportsMethodButNotAllMethodsInClass(
- "java/util/stream/DoubleStream#parallel()Ljava/util/stream/BaseStream;"));
- assertEquals(
- minApiLevel == AndroidApiLevel.B,
+ assertTrue(
supportsMethodButNotAllMethodsInClass(
"java/util/stream/DoubleStream#allMatch(Ljava/util/function/DoublePredicate;)Z"));
+
assertEquals(
- minApiLevel == AndroidApiLevel.L, lintContents.contains("java/util/stream/IntStream"));
- assertFalse(
- supportsMethodButNotAllMethodsInClass(
- "java/util/stream/IntStream#parallel()Ljava/util/stream/IntStream;"));
- assertFalse(
- supportsMethodButNotAllMethodsInClass(
- "java/util/stream/IntStream#parallel()Ljava/util/stream/BaseStream;"));
- assertEquals(
- minApiLevel == AndroidApiLevel.B,
- supportsMethodButNotAllMethodsInClass(
- "java/util/stream/IntStream#allMatch(Ljava/util/function/IntPredicate;)Z"));
+ libraryDesugaringSpecification != JDK8, supportsAllMethodsOf("java/util/concurrent/Flow"));
// Checks specific methods are supported or not in JDK8, all is supported in JDK11.
if (libraryDesugaringSpecification == JDK8) {
@@ -133,7 +125,7 @@
// Maintain type.
assertEquals(
- libraryDesugaringSpecification != JDK8,
+ libraryDesugaringSpecification != JDK8 && libraryDesugaringSpecification != JDK11_LEGACY,
supportsAllMethodsOf("java/io/UncheckedIOException"));
// Retarget method.
@@ -161,7 +153,7 @@
}
@Test
- public void testFileContent() throws Exception {
+ public void testLint() throws Exception {
Path directory = temp.newFolder().toPath();
Path jdkLibJar =
libraryDesugaringSpecification == JDK8
@@ -182,38 +174,36 @@
false,
AndroidApiLevel.B.getLevel());
- for (AndroidApiLevel apiLevel : AndroidApiLevel.values()) {
- if (apiLevel.isGreaterThan(AndroidApiLevel.T)) {
- continue;
- }
- Path compileApiLevelDirectory = directory.resolve("compile_api_level_" + apiLevel.getLevel());
- if (apiLevel.getLevel()
- < desugaredLibrarySpecification.getRequiredCompilationApiLevel().getLevel()) {
- System.out.println("!Checking " + compileApiLevelDirectory);
- continue;
- }
- assertTrue(Files.exists(compileApiLevelDirectory));
- for (AndroidApiLevel minApiLevel : AndroidApiLevel.values()) {
- String desugaredApisBaseName =
- "desugared_apis_" + apiLevel.getLevel() + "_" + minApiLevel.getLevel();
- if (minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B) {
- assertTrue(
- Files.exists(compileApiLevelDirectory.resolve(desugaredApisBaseName + ".txt")));
- assertTrue(
- Files.exists(compileApiLevelDirectory.resolve(desugaredApisBaseName + ".jar")));
- checkFileContent(
- minApiLevel, compileApiLevelDirectory.resolve(desugaredApisBaseName + ".txt"));
- } else {
- assertFalse(
- Files.exists(compileApiLevelDirectory.resolve(desugaredApisBaseName + ".txt")));
- assertFalse(
- Files.exists(compileApiLevelDirectory.resolve(desugaredApisBaseName + ".jar")));
- }
+ AndroidApiLevel requiredCompilationApiLevel =
+ desugaredLibrarySpecification.getRequiredCompilationApiLevel();
+ Path compileApiLevelDirectory =
+ directory.resolve("compile_api_level_" + requiredCompilationApiLevel.getLevel());
+
+ assertTrue(Files.exists(compileApiLevelDirectory));
+ for (AndroidApiLevel minApiLevel : AndroidApiLevel.values()) {
+ String desugaredApisBaseName =
+ "desugared_apis_" + requiredCompilationApiLevel.getLevel() + "_" + minApiLevel.getLevel();
+ if (minApiLevel == AndroidApiLevel.L || minApiLevel == AndroidApiLevel.B) {
+ assertTrue(Files.exists(compileApiLevelDirectory.resolve(desugaredApisBaseName + ".txt")));
+ assertTrue(Files.exists(compileApiLevelDirectory.resolve(desugaredApisBaseName + ".jar")));
+ checkFileContent(
+ minApiLevel, compileApiLevelDirectory.resolve(desugaredApisBaseName + ".txt"));
+ } else {
+ assertFalse(Files.exists(compileApiLevelDirectory.resolve(desugaredApisBaseName + ".txt")));
+ assertFalse(Files.exists(compileApiLevelDirectory.resolve(desugaredApisBaseName + ".jar")));
}
}
+ }
+
+ @Test
+ public void testHTML() throws Exception {
+ Path jdkLibJar =
+ libraryDesugaringSpecification == JDK8
+ ? ToolHelper.DESUGARED_JDK_8_LIB_JAR
+ : LibraryDesugaringSpecification.getTempLibraryJDK11Undesugar();
Path directory2 = temp.newFolder().toPath();
- GenerateLintFiles.main(
+ GenerateHtmlDoc.main(
new String[] {
"--generate-api-docs",
libraryDesugaringSpecification.getSpecification().toString(),
@@ -225,5 +215,8 @@
// check that the doc generation ran without error and looks sane.
assertEquals("<tr>", html.get(0));
assertEquals("</tr>", html.get(html.size() - 2));
+ if (libraryDesugaringSpecification == JDK11 || libraryDesugaringSpecification == JDK11_PATH) {
+ assertEquals(6, html.stream().filter(s -> s.contains("Flow")).count());
+ }
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
new file mode 100644
index 0000000..7240267
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/desugaredlibrary/PartialDesugaringTest.java
@@ -0,0 +1,185 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.desugar.desugaredlibrary;
+
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_MINIMAL;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK11_PATH;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.JDK8;
+import static com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification.getJdk8AndAll3Jdk11;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.desugar.desugaredlibrary.test.LibraryDesugaringSpecification;
+import com.android.tools.r8.graph.DexMethod;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedClasses;
+import com.android.tools.r8.ir.desugar.desugaredlibrary.lint.SupportedMethodsGenerator;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.InternalOptions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/** This test validates that anything supported at API N-1 is supported at API N. */
+@RunWith(Parameterized.class)
+public class PartialDesugaringTest extends DesugaredLibraryTestBase {
+
+ LibraryDesugaringSpecification librarySpecification;
+
+ @Parameters(name = "{0}, spec: {1}")
+ public static List<Object[]> data() {
+ return buildParameters(getTestParameters().withNoneRuntime().build(), getJdk8AndAll3Jdk11());
+ }
+
+ public PartialDesugaringTest(
+ TestParameters parameters, LibraryDesugaringSpecification librarySpecification) {
+ assert parameters.isNoneRuntime();
+ this.librarySpecification = librarySpecification;
+ }
+
+ private static List<AndroidApiLevel> getRelevantApiLevels() {
+ // B is implicit - everything is supported on B.
+ return ImmutableList.of(
+ AndroidApiLevel.K,
+ AndroidApiLevel.L,
+ AndroidApiLevel.M,
+ AndroidApiLevel.N,
+ AndroidApiLevel.O,
+ AndroidApiLevel.P,
+ AndroidApiLevel.Q,
+ AndroidApiLevel.R,
+ AndroidApiLevel.S,
+ AndroidApiLevel.T);
+ }
+
+ // TODO(b/268425188): Fix remaining failures.
+ private static final Set<String> FAILURES_STREAM =
+ ImmutableSet.of(
+ // The takeWhile/dropWhile methods are not yet present on android.jar.
+ "java.util.stream.IntStream"
+ + " java.util.stream.IntStream.dropWhile(java.util.function.IntPredicate)",
+ "java.util.stream.Stream java.util.stream.Stream.takeWhile(java.util.function.Predicate)",
+ "java.util.stream.LongStream"
+ + " java.util.stream.LongStream.dropWhile(java.util.function.LongPredicate)",
+ "java.util.stream.DoubleStream"
+ + " java.util.stream.DoubleStream.takeWhile(java.util.function.DoublePredicate)",
+ "java.util.stream.IntStream"
+ + " java.util.stream.IntStream.takeWhile(java.util.function.IntPredicate)",
+ "java.util.stream.Stream java.util.stream.Stream.dropWhile(java.util.function.Predicate)",
+ "java.util.stream.LongStream"
+ + " java.util.stream.LongStream.takeWhile(java.util.function.LongPredicate)",
+ "java.util.stream.DoubleStream"
+ + " java.util.stream.DoubleStream.dropWhile(java.util.function.DoublePredicate)");
+ private static final Set<String> FAILURES_FILE_STORE =
+ ImmutableSet.of(
+ // FileStore.getBlockSize() was added in 33.
+ "long java.nio.file.FileStore.getBlockSize()");
+ private static final Set<String> FAILURES_SUMMARY_STATISTICS =
+ ImmutableSet.of(
+ "void java.util.LongSummaryStatistics.<init>(long, long, long, long)",
+ "void java.util.IntSummaryStatistics.<init>(long, int, int, long)");
+ // For some reason, in Android T, the other constructor were added but not this one...
+ private static final Set<String> FAILURES_DOUBLE_SUMMARY_STATISTICS =
+ ImmutableSet.of(
+ "void java.util.DoubleSummaryStatistics.<init>(long, double, double, double)");
+ private static final Set<String> FAILURES_TO_ARRAY =
+ ImmutableSet.of(
+ // See b/266401747: Desugaring is disabled.
+ "java.lang.Object[] java.util.Collection.toArray(java.util.function.IntFunction)");
+ private static final Set<String> FAILURES_ERA =
+ ImmutableSet.of(
+ // This fails on Java 8 desugared library due to missing covariant return type.
+ // The method is present on platform from 33 but not in android.jar...
+ "java.time.chrono.IsoEra java.time.LocalDate.getEra()");
+ private static final Set<String> FAILURES_CHRONOLOGY =
+ ImmutableSet.of(
+ "long java.time.chrono.Chronology.epochSecond(int, int, int, int, int, int,"
+ + " java.time.ZoneOffset)",
+ "long java.time.chrono.Chronology.epochSecond(java.time.chrono.Era, int, int, int, int,"
+ + " int, int, java.time.ZoneOffset)",
+ "long java.time.chrono.IsoChronology.epochSecond(int, int, int, int, int, int,"
+ + " java.time.ZoneOffset)");
+ private static final Set<String> FAILURES_DATE_TIME_BUILDER =
+ ImmutableSet.of(
+ "java.time.format.DateTimeFormatterBuilder"
+ + " java.time.format.DateTimeFormatterBuilder.appendGenericZoneText(java.time.format.TextStyle)",
+ "java.time.format.DateTimeFormatterBuilder"
+ + " java.time.format.DateTimeFormatterBuilder.appendGenericZoneText(java.time.format.TextStyle,"
+ + " java.util.Set)");
+
+ @Test
+ public void test() throws Exception {
+ SupportedClasses supportedClasses =
+ new SupportedMethodsGenerator(new InternalOptions())
+ .run(librarySpecification.getDesugarJdkLibs(), librarySpecification.getSpecification());
+
+ for (AndroidApiLevel api : getRelevantApiLevels()) {
+ Set<DexMethod> localFailures = Sets.newIdentityHashSet();
+ supportedClasses.forEachClass(
+ supportedClass ->
+ supportedClass.forEachMethodAndAnnotation(
+ (method, annotation) -> {
+ if (annotation != null && annotation.isUnsupportedInMinApiRange()) {
+ if (api.getLevel() >= annotation.getMinRange()
+ && api.getLevel() <= annotation.getMaxRange()) {
+ localFailures.add(method.getReference());
+ }
+ }
+ }));
+ Set<String> expectedFailures = getExpectedFailures(api);
+ Set<String> apiFailuresString =
+ localFailures.stream().map(DexMethod::toString).collect(Collectors.toSet());
+ if (!expectedFailures.equals(apiFailuresString)) {
+ System.out.println("Failure for api " + api);
+ assertEquals(expectedFailures, apiFailuresString);
+ }
+ }
+ }
+
+ private Set<String> getExpectedFailures(AndroidApiLevel api) {
+ Set<String> expectedFailures = new HashSet<>();
+ boolean jdk11NonMinimal = librarySpecification != JDK8 && librarySpecification != JDK11_MINIMAL;
+ if (jdk11NonMinimal && api.isGreaterThanOrEqualTo(AndroidApiLevel.N)) {
+ expectedFailures.addAll(FAILURES_STREAM);
+ expectedFailures.addAll(FAILURES_DOUBLE_SUMMARY_STATISTICS);
+ if (api.isLessThan(AndroidApiLevel.T)) {
+ expectedFailures.addAll(FAILURES_SUMMARY_STATISTICS);
+ }
+ }
+ if (librarySpecification == JDK11_PATH
+ && api.isGreaterThanOrEqualTo(AndroidApiLevel.O)
+ && api.isLessThan(AndroidApiLevel.T)) {
+ expectedFailures.addAll(FAILURES_FILE_STORE);
+ }
+ if (librarySpecification != JDK11_MINIMAL
+ && api.isGreaterThanOrEqualTo(AndroidApiLevel.N)
+ && api.isLessThan(AndroidApiLevel.T)) {
+ expectedFailures.addAll(FAILURES_TO_ARRAY);
+ }
+ if (librarySpecification == JDK8 && api.isLessThan(AndroidApiLevel.T)) {
+ // Interestingly that was added somehow to JDK8 desugared library at some point...
+ expectedFailures.addAll(FAILURES_TO_ARRAY);
+ }
+ if (jdk11NonMinimal && api.isGreaterThanOrEqualTo(AndroidApiLevel.O)) {
+ expectedFailures.addAll(FAILURES_CHRONOLOGY);
+ expectedFailures.addAll(FAILURES_DATE_TIME_BUILDER);
+ }
+ if (librarySpecification == JDK8 && api.isGreaterThanOrEqualTo(AndroidApiLevel.O)) {
+ expectedFailures.addAll(FAILURES_ERA);
+ }
+ if (jdk11NonMinimal && api.isGreaterThanOrEqualTo(AndroidApiLevel.T)) {
+ // The method is present, but not in android.jar...
+ expectedFailures.addAll(FAILURES_ERA);
+ }
+ return expectedFailures;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java
index de13512..eace966 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/kotlin/SimpleKotlinEnumUnboxingTest.java
@@ -66,12 +66,15 @@
testForR8(parameters.getBackend())
.addProgramFiles(
jars.getForConfiguration(kotlinParameters),
- kotlinParameters.getCompiler().getKotlinStdlibJar())
+ kotlinParameters.getCompiler().getKotlinStdlibJar(),
+ kotlinParameters.getCompiler().getKotlinAnnotationJar())
.addKeepMainRule(PKG + ".MainKt")
.addKeepRules(enumKeepRules.getKeepRules())
.addKeepRuntimeVisibleAnnotations()
.addOptionsModification(opt -> enableEnumOptions(opt, enumValueOptimization))
- .addEnumUnboxingInspector(inspector -> inspector.assertUnboxed(PKG + ".Color"))
+ // TODO(b/268005228): We should be able to unbox.
+ .addEnumUnboxingInspector(
+ inspector -> inspector.assertUnboxedIf(!kotlinParameters.isKotlinDev(), PKG + ".Color"))
.allowDiagnosticMessages()
.setMinApi(parameters.getApiLevel())
.compile()
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 eef3dcf..c2d3a03 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
@@ -48,6 +48,8 @@
public void testR8() throws Exception {
testForR8(Backend.DEX)
.addProgramFiles(outDirectory.resolve("program.jar"))
+ .addOptionsModification(
+ options -> options.getArtProfileOptions().setEnableCompletenessCheckForTesting(true))
.apply(this::configure)
.compile();
}
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index 2e3512d..8c83a79 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -27,6 +27,7 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
import com.android.tools.r8.ir.code.IRCode;
+import com.android.tools.r8.ir.conversion.MethodProcessorEventConsumer;
import com.android.tools.r8.ir.conversion.MethodProcessorWithWave;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
import com.android.tools.r8.origin.Origin;
@@ -220,6 +221,11 @@
}
@Override
+ public MethodProcessorEventConsumer getEventConsumer() {
+ throw new Unreachable();
+ }
+
+ @Override
public boolean shouldApplyCodeRewritings(ProgramMethod method) {
return false;
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlineKeepMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlineKeepMethodTest.java
index 92383ae..3787be5 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlineKeepMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/classinliner/ClassInlineKeepMethodTest.java
@@ -9,19 +9,15 @@
import static org.hamcrest.MatcherAssert.assertThat;
import com.android.tools.r8.CompilationFailedException;
-import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper.DexVm.Version;
import com.android.tools.r8.utils.codeinspector.ClassSubject;
-import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -32,10 +28,41 @@
private final TestParameters parameters;
private static final String EXPECTED_OUTPUT = "Hello world";
- @NoVerticalClassMerging
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withDexRuntime(Version.last()).build();
+ }
+
+ public ClassInlineKeepMethodTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ @Test
+ public void testIsKeptWithName()
+ throws ExecutionException, CompilationFailedException, IOException {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(ClassInlineKeepMethodTest.class)
+ .addKeepMainRule(Keeper.class)
+ .addKeepClassAndMembersRules(ShouldBeKept.class)
+ .run(parameters.getRuntime(), Keeper.class)
+ .assertSuccessWithOutput(EXPECTED_OUTPUT)
+ .inspect(
+ inspector -> {
+ ClassSubject shouldBeKeptClass = inspector.clazz(ShouldBeKept.class);
+ assertThat(shouldBeKeptClass, isPresent());
+ assertThat(
+ shouldBeKeptClass.uniqueMethodWithOriginalName("shouldBeKept"), isPresent());
+ // Verify that we did not inline from the method by checking for a const string.
+ ClassSubject clazz = inspector.clazz(Keeper.class);
+ assertThat(clazz, isPresent());
+ MethodSubject main = clazz.uniqueMethodWithOriginalName("main");
+ assertTrue(
+ main.streamInstructions().noneMatch(i -> i.isConstString(JumboStringMode.ALLOW)));
+ });
+ }
+
public static class ShouldBeKept {
- @NeverInline
public void shouldBeKept() {
System.out.print("Hello world");
}
@@ -48,32 +75,4 @@
}
}
- @Parameterized.Parameters(name = "{0}")
- public static TestParametersCollection data() {
- return getTestParameters().withDexRuntime(Version.last()).build();
- }
-
- public ClassInlineKeepMethodTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
- @Ignore("b/129390240")
- @Test
- public void testIsKeptWithName()
- throws ExecutionException, CompilationFailedException, IOException {
- CodeInspector inspector =
- testForR8(parameters.getBackend())
- .addInnerClasses(ClassInlineKeepMethodTest.class)
- .addKeepMainRule(Keeper.class)
- .addKeepClassAndMembersRules(ShouldBeKept.class)
- .enableInliningAnnotations()
- .enableNoVerticalClassMergingAnnotations()
- .run(parameters.getRuntime(), Keeper.class)
- .assertSuccessWithOutput(EXPECTED_OUTPUT)
- .inspector();
- ClassSubject clazz = inspector.clazz(Keeper.class);
- assertThat(clazz, isPresent());
- MethodSubject main = clazz.uniqueMethodWithOriginalName("main");
- assertTrue(main.streamInstructions().noneMatch(i -> i.isConstString(JumboStringMode.ALLOW)));
- }
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
index 7b07978..36efb33 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteDelegatedPropertyTest.java
@@ -122,19 +122,24 @@
.addProgramFiles(libJars.getForConfiguration(kotlinc, targetVersion))
.compile()
.writeToZip();
+ Path outputPath = temp.newFolder().toPath();
ProcessResult compileResult =
kotlinc(parameters.getRuntime().asCf(), kotlinc, targetVersion)
.addClasspathFiles(outputJar)
.addSourceFiles(
getKotlinFileInTest(DescriptorUtils.getBinaryNameFromJavaType(PKG_APP), "main"))
- .setOutputPath(temp.newFolder().toPath())
+ .setOutputPath(outputPath)
.compileRaw();
- Assert.assertEquals(1, compileResult.exitCode);
if (kotlinParameters.isNewerThan(KOTLINC_1_8_0)) {
- assertThat(
- compileResult.stderr,
- containsString("the feature \"references to synthetic java properties\""));
+ testForJvm()
+ .addRunClasspathFiles(
+ kotlinc.getKotlinStdlibJar(), kotlinc.getKotlinReflectJar(), outputJar)
+ .addClasspath(outputPath)
+ .run(parameters.getRuntime(), PKG_APP + ".MainKt")
+ .assertSuccessWithOutputLines(
+ "foobar", "property oldName (Kotlin reflection is not available)");
} else {
+ Assert.assertEquals(1, compileResult.exitCode);
assertThat(
compileResult.stderr,
containsString(
diff --git a/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java b/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
index 0963963..d591306 100644
--- a/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/optimize/switches/KotlinEnumSwitchTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assert.assertNotEquals;
import com.android.tools.r8.KotlinCompilerTool;
+import com.android.tools.r8.KotlinCompilerTool.KotlinCompilerVersion;
import com.android.tools.r8.KotlinTestBase;
import com.android.tools.r8.KotlinTestParameters;
import com.android.tools.r8.TestParameters;
@@ -61,6 +62,13 @@
})
.setMinApi(parameters.getApiLevel())
.addDontObfuscate()
+ // This will probably start failing when the CL
+ // https://github.com/JetBrains/kotlin/commit/79f6d4b590573e6adccd7e8899d3b15ddb42d185
+ // is propagated to the build for kotlin-reflect.
+ .applyIf(
+ parameters.isDexRuntime()
+ && kotlinParameters.isNewerThan(KotlinCompilerVersion.KOTLINC_1_8_0),
+ b -> b.addDontWarn("java.lang.ClassValue"))
.allowDiagnosticWarningMessages()
.compile()
.assertAllWarningMessagesMatch(equalTo("Resource 'META-INF/MANIFEST.MF' already exists."))
diff --git a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
index 87698d9..0446cd5 100644
--- a/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
+++ b/src/test/java/com/android/tools/r8/naming/retrace/StackTrace.java
@@ -776,4 +776,31 @@
StackTrace stackTrace, StackTraceLine lineToIgnoreLineNumberFor) {
return new StackTraceIgnoreSpecificLineNumberMatcher(stackTrace, lineToIgnoreLineNumberFor);
}
+
+ public static Matcher<StackTrace> containsLine(
+ StackTraceLine expected, StackTraceEquivalence equivalence) {
+ return new TypeSafeMatcher<StackTrace>() {
+
+ private final Equivalence<StackTrace.StackTraceLine> lineEquivalence =
+ equivalence.getLineEquivalence();
+
+ @Override
+ public boolean matchesSafely(StackTrace stackTrace) {
+ for (StackTraceLine actual : stackTrace.getStackTraceLines()) {
+ if (lineEquivalence.equivalent(actual, expected)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description
+ .appendText("stacktrace did not match")
+ .appendText(System.lineSeparator())
+ .appendText(expected.toString());
+ }
+ };
+ }
}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/EnumUnboxingUtilityMethodProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/EnumUnboxingUtilityMethodProfileRewritingTest.java
index 13a8c9d..e9058d4 100644
--- a/src/test/java/com/android/tools/r8/profile/art/completeness/EnumUnboxingUtilityMethodProfileRewritingTest.java
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/EnumUnboxingUtilityMethodProfileRewritingTest.java
@@ -88,18 +88,25 @@
SyntheticItemsTestUtils.syntheticEnumUnboxingSharedUtilityClass(MyEnum.class));
assertThat(enumUnboxingSharedUtilityClassSubject, isPresent());
assertThat(enumUnboxingSharedUtilityClassSubject.clinit(), isPresent());
- assertThat(
- enumUnboxingSharedUtilityClassSubject.uniqueMethodWithOriginalName("ordinal"), isPresent());
- assertThat(
- enumUnboxingSharedUtilityClassSubject.uniqueMethodWithOriginalName("values"), isPresent());
- // TODO(b/265729283): Should also include the above methods from enum unboxing.
+ MethodSubject sharedOrdinalMethodSubject =
+ enumUnboxingSharedUtilityClassSubject.uniqueMethodWithOriginalName("ordinal");
+ assertThat(sharedOrdinalMethodSubject, isPresent());
+
+ MethodSubject sharedValuesMethodSubject =
+ enumUnboxingSharedUtilityClassSubject.uniqueMethodWithOriginalName("values");
+ assertThat(sharedValuesMethodSubject, isPresent());
+
profileInspector
+ .assertContainsClassRule(enumUnboxingSharedUtilityClassSubject)
.assertContainsMethodRules(
mainClassSubject.mainMethod(),
localGreetMethodSubject,
localOtherMethodSubject,
- localValuesMethodSubject)
+ localValuesMethodSubject,
+ enumUnboxingSharedUtilityClassSubject.clinit(),
+ sharedOrdinalMethodSubject,
+ sharedValuesMethodSubject)
.assertContainsNoOtherRules();
}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
new file mode 100644
index 0000000..ee456d0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/LambdaStaticLibraryMethodImplementationProfileRewritingTest.java
@@ -0,0 +1,128 @@
+// Copyright (c) 2023, 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.profile.art.completeness;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.util.Collections;
+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 LambdaStaticLibraryMethodImplementationProfileRewritingTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForD8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addArtProfileForRewriting(getArtProfile())
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectResidualArtProfile(this::inspectD8)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("0");
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ parameters.assumeR8TestParameters();
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addArtProfileForRewriting(getArtProfile())
+ .addOptionsModification(InlinerOptions::disableInlining)
+ .addOptionsModification(
+ options -> options.callSiteOptimizationOptions().setEnableMethodStaticizing(false))
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspectResidualArtProfile(this::inspectR8)
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("0");
+ }
+
+ public ExternalArtProfile getArtProfile() {
+ return ExternalArtProfile.builder()
+ .addMethodRule(MethodReferenceUtils.mainMethod(Main.class))
+ .build();
+ }
+
+ private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector) {
+ inspect(profileInspector, inspector, false);
+ }
+
+ private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
+ inspect(profileInspector, inspector, parameters.isCfRuntime());
+ }
+
+ public void inspect(
+ ArtProfileInspector profileInspector, CodeInspector inspector, boolean canUseLambdas) {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ MethodSubject mainMethodSubject = mainClassSubject.mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+
+ // Check the presence of the lambda class and its methods.
+ ClassSubject lambdaClassSubject =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticLambdaClass(Main.class, 0));
+ assertThat(lambdaClassSubject, notIf(isPresent(), canUseLambdas));
+
+ MethodSubject lambdaInitializerSubject = lambdaClassSubject.uniqueInstanceInitializer();
+ assertThat(lambdaInitializerSubject, notIf(isPresent(), canUseLambdas));
+
+ MethodSubject lambdaMainMethodSubject =
+ lambdaClassSubject.uniqueMethodThatMatches(FoundMethodSubject::isVirtual);
+ assertThat(lambdaMainMethodSubject, notIf(isPresent(), canUseLambdas));
+
+ if (canUseLambdas) {
+ profileInspector.assertContainsMethodRule(mainMethodSubject).assertContainsNoOtherRules();
+ } else {
+ profileInspector
+ .assertContainsClassRules(lambdaClassSubject)
+ .assertContainsMethodRules(
+ mainMethodSubject, lambdaInitializerSubject, lambdaMainMethodSubject)
+ .assertContainsNoOtherRules();
+ }
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ SetSupplier lambda = Collections::emptySet;
+ System.out.println(lambda.get().size());
+ }
+ }
+
+ interface SetSupplier {
+
+ Set<?> get();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
new file mode 100644
index 0000000..0969070
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/profile/art/completeness/RecordProfileRewritingTest.java
@@ -0,0 +1,300 @@
+// Copyright (c) 2023, 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.profile.art.completeness;
+
+import static com.android.tools.r8.ir.desugar.records.RecordDesugaring.EQUALS_RECORD_METHOD_NAME;
+import static com.android.tools.r8.ir.desugar.records.RecordDesugaring.GET_FIELDS_AS_OBJECTS_METHOD_NAME;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.ifThen;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static com.android.tools.r8.utils.codeinspector.Matchers.notIf;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.D8TestCompileResult;
+import com.android.tools.r8.R8TestCompileResult;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.desugar.records.RecordTestUtils;
+import com.android.tools.r8.profile.art.model.ExternalArtProfile;
+import com.android.tools.r8.profile.art.utils.ArtProfileInspector;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.InternalOptions.InlinerOptions;
+import com.android.tools.r8.utils.MethodReferenceUtils;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import java.util.Collections;
+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 RecordProfileRewritingTest extends TestBase {
+
+ private static final String RECORD_NAME = "SimpleRecord";
+ private static final byte[][] PROGRAM_DATA = RecordTestUtils.getProgramData(RECORD_NAME);
+ private static final String EXPECTED_RESULT =
+ StringUtils.lines("Jane Doe", "42", "Jane Doe", "42");
+
+ private static final ClassReference MAIN_REFERENCE =
+ Reference.classFromTypeName(RecordTestUtils.getMainType(RECORD_NAME));
+ private static final ClassReference PERSON_REFERENCE =
+ Reference.classFromTypeName(MAIN_REFERENCE.getTypeName() + "$Person");
+ private static final ClassReference RECORD_REFERENCE =
+ Reference.classFromTypeName("java.lang.Record");
+ private static final ClassReference OBJECT_REFERENCE = Reference.classFromClass(Object.class);
+ private static final ClassReference STRING_REFERENCE = Reference.classFromClass(String.class);
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ @Test
+ public void testReference() throws Exception {
+ parameters.assumeJvmTestParameters();
+ assumeTrue(parameters.canUseRecords());
+ testForJvm()
+ .addProgramClassFileData(PROGRAM_DATA)
+ .run(parameters.getRuntime(), MAIN_REFERENCE.getTypeName())
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ D8TestCompileResult compileResult =
+ testForD8(parameters.getBackend())
+ .addProgramClassFileData(PROGRAM_DATA)
+ .addArtProfileForRewriting(getArtProfile())
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+ compileResult
+ .inspectResidualArtProfile(
+ profileInspector ->
+ compileResult.inspectWithOptions(
+ inspector -> inspectD8(profileInspector, inspector),
+ options -> options.testing.disableRecordApplicationReaderMap = true))
+ .run(parameters.getRuntime(), MAIN_REFERENCE.getTypeName())
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ parameters.assumeR8TestParameters();
+ assumeTrue(parameters.canUseRecords() || parameters.isDexRuntime());
+ R8TestCompileResult compileResult =
+ testForR8(parameters.getBackend())
+ .addProgramClassFileData(PROGRAM_DATA)
+ .addKeepMainRule(MAIN_REFERENCE.getTypeName())
+ .addKeepRules(
+ "-neverpropagatevalue class " + PERSON_REFERENCE.getTypeName() + " { <fields>; }")
+ .addArtProfileForRewriting(getArtProfile())
+ .addOptionsModification(InlinerOptions::disableInlining)
+ .applyIf(
+ parameters.isCfRuntime(),
+ testBuilder ->
+ testBuilder.addLibraryFiles(RecordTestUtils.getJdk15LibraryFiles(temp)))
+ .enableProguardTestOptions()
+ .noHorizontalClassMergingOfSynthetics()
+ .setMinApi(parameters.getApiLevel())
+ .compile();
+ compileResult
+ .inspectResidualArtProfile(
+ profileInspector ->
+ compileResult.inspectWithOptions(
+ inspector -> inspectR8(profileInspector, inspector),
+ options -> options.testing.disableRecordApplicationReaderMap = true))
+ .run(parameters.getRuntime(), MAIN_REFERENCE.getTypeName())
+ .assertSuccessWithOutput(EXPECTED_RESULT);
+ }
+
+ private ExternalArtProfile getArtProfile() {
+ return ExternalArtProfile.builder()
+ .addMethodRule(MethodReferenceUtils.mainMethod(MAIN_REFERENCE))
+ .addClassRule(PERSON_REFERENCE)
+ .addMethodRule(
+ MethodReferenceUtils.instanceConstructor(
+ PERSON_REFERENCE, STRING_REFERENCE, Reference.INT))
+ .addMethodRule(
+ Reference.method(PERSON_REFERENCE, "name", Collections.emptyList(), STRING_REFERENCE))
+ .addMethodRule(
+ Reference.method(PERSON_REFERENCE, "age", Collections.emptyList(), Reference.INT))
+ .addMethodRule(
+ Reference.method(
+ PERSON_REFERENCE, "equals", ImmutableList.of(OBJECT_REFERENCE), Reference.BOOL))
+ .addMethodRule(
+ Reference.method(PERSON_REFERENCE, "hashCode", Collections.emptyList(), Reference.INT))
+ .addMethodRule(
+ Reference.method(
+ PERSON_REFERENCE, "toString", Collections.emptyList(), STRING_REFERENCE))
+ .build();
+ }
+
+ private void inspectD8(ArtProfileInspector profileInspector, CodeInspector inspector) {
+ inspect(
+ profileInspector,
+ inspector,
+ SyntheticItemsTestUtils.syntheticRecordTagClass(),
+ parameters.canUseNestBasedAccessesWhenDesugaring(),
+ parameters.canUseRecordsWhenDesugaring());
+ }
+
+ private void inspectR8(ArtProfileInspector profileInspector, CodeInspector inspector) {
+ inspect(
+ profileInspector,
+ inspector,
+ RECORD_REFERENCE,
+ parameters.canUseNestBasedAccesses(),
+ parameters.canUseRecords());
+ }
+
+ private void inspect(
+ ArtProfileInspector profileInspector,
+ CodeInspector inspector,
+ ClassReference recordClassReference,
+ boolean canUseNestBasedAccesses,
+ boolean canUseRecords) {
+ ClassSubject mainClassSubject = inspector.clazz(MAIN_REFERENCE);
+ assertThat(mainClassSubject, isPresent());
+
+ MethodSubject mainMethodSubject = mainClassSubject.mainMethod();
+ assertThat(mainMethodSubject, isPresent());
+
+ ClassSubject recordTagClassSubject = inspector.clazz(recordClassReference);
+ assertThat(recordTagClassSubject, notIf(isPresent(), canUseRecords));
+ if (!canUseRecords) {
+ assertEquals(1, recordTagClassSubject.allMethods().size());
+ }
+
+ MethodSubject recordTagInstanceInitializerSubject = recordTagClassSubject.init();
+ assertThat(recordTagInstanceInitializerSubject, notIf(isPresent(), canUseRecords));
+
+ ClassSubject personRecordClassSubject = inspector.clazz(PERSON_REFERENCE);
+ assertThat(personRecordClassSubject, isPresent());
+ assertEquals(
+ canUseRecords
+ ? inspector.getTypeSubject(RECORD_REFERENCE.getTypeName())
+ : recordTagClassSubject.asTypeSubject(),
+ personRecordClassSubject.getSuperType());
+ assertEquals(canUseRecords ? 6 : 10, personRecordClassSubject.allMethods().size());
+
+ MethodSubject personInstanceInitializerSubject =
+ personRecordClassSubject.uniqueInstanceInitializer();
+ assertThat(personInstanceInitializerSubject, isPresent());
+
+ // Name getters.
+ MethodSubject nameMethodSubject = personRecordClassSubject.uniqueMethodWithOriginalName("name");
+ assertThat(nameMethodSubject, isPresent());
+
+ MethodSubject nameNestAccessorMethodSubject =
+ personRecordClassSubject.uniqueMethodWithOriginalName(
+ SyntheticItemsTestUtils.syntheticNestInstanceFieldGetter(
+ Reference.field(PERSON_REFERENCE, "name", STRING_REFERENCE))
+ .getMethodName());
+ assertThat(nameNestAccessorMethodSubject, notIf(isPresent(), canUseNestBasedAccesses));
+
+ // Age getters.
+ MethodSubject ageMethodSubject = personRecordClassSubject.uniqueMethodWithOriginalName("age");
+ assertThat(ageMethodSubject, isPresent());
+
+ MethodSubject ageNestAccessorMethodSubject =
+ personRecordClassSubject.uniqueMethodWithOriginalName(
+ SyntheticItemsTestUtils.syntheticNestInstanceFieldGetter(
+ Reference.field(PERSON_REFERENCE, "age", Reference.INT))
+ .getMethodName());
+ assertThat(ageNestAccessorMethodSubject, notIf(isPresent(), canUseNestBasedAccesses));
+
+ // boolean equals(Object)
+ MethodSubject getFieldsAsObjectsMethodSubject =
+ personRecordClassSubject.uniqueMethodWithOriginalName(GET_FIELDS_AS_OBJECTS_METHOD_NAME);
+ assertThat(getFieldsAsObjectsMethodSubject, notIf(isPresent(), canUseRecords));
+
+ MethodSubject equalsHelperMethodSubject =
+ personRecordClassSubject.uniqueMethodWithOriginalName(EQUALS_RECORD_METHOD_NAME);
+ assertThat(equalsHelperMethodSubject, notIf(isPresent(), canUseRecords));
+
+ MethodSubject equalsMethodSubject =
+ personRecordClassSubject.uniqueMethodWithOriginalName("equals");
+ assertThat(equalsMethodSubject, isPresent());
+ assertThat(
+ equalsMethodSubject, ifThen(!canUseRecords, invokesMethod(equalsHelperMethodSubject)));
+
+ // int hashCode()
+ ClassSubject hashCodeHelperClassSubject =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticRecordHelperClass(PERSON_REFERENCE, 0));
+ assertThat(hashCodeHelperClassSubject, notIf(isPresent(), canUseRecords));
+
+ MethodSubject hashCodeHelperMethodSubject = hashCodeHelperClassSubject.uniqueMethod();
+ assertThat(hashCodeHelperMethodSubject, notIf(isPresent(), canUseRecords));
+
+ MethodSubject hashCodeMethodSubject =
+ personRecordClassSubject.uniqueMethodWithOriginalName("hashCode");
+ assertThat(hashCodeMethodSubject, isPresent());
+ assertThat(
+ hashCodeMethodSubject,
+ ifThen(!canUseRecords, invokesMethod(getFieldsAsObjectsMethodSubject)));
+ assertThat(
+ hashCodeMethodSubject, ifThen(!canUseRecords, invokesMethod(hashCodeHelperMethodSubject)));
+
+ // String toString()
+ ClassSubject toStringHelperClassSubject =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticRecordHelperClass(PERSON_REFERENCE, 1));
+ assertThat(toStringHelperClassSubject, notIf(isPresent(), canUseRecords));
+
+ MethodSubject toStringHelperMethodSubject = toStringHelperClassSubject.uniqueMethod();
+ assertThat(toStringHelperMethodSubject, notIf(isPresent(), canUseRecords));
+
+ MethodSubject toStringMethodSubject =
+ personRecordClassSubject.uniqueMethodWithOriginalName("toString");
+ assertThat(toStringMethodSubject, isPresent());
+ assertThat(
+ toStringMethodSubject,
+ ifThen(!canUseRecords, invokesMethod(getFieldsAsObjectsMethodSubject)));
+ assertThat(
+ toStringMethodSubject, ifThen(!canUseRecords, invokesMethod(toStringHelperMethodSubject)));
+
+ profileInspector
+ .assertContainsClassRule(personRecordClassSubject)
+ .assertContainsMethodRules(
+ mainMethodSubject,
+ personInstanceInitializerSubject,
+ nameMethodSubject,
+ ageMethodSubject,
+ equalsMethodSubject,
+ hashCodeMethodSubject,
+ toStringMethodSubject)
+ .applyIf(
+ !canUseNestBasedAccesses,
+ i ->
+ i.assertContainsMethodRules(
+ nameNestAccessorMethodSubject, ageNestAccessorMethodSubject))
+ .applyIf(
+ !canUseRecords,
+ i ->
+ i.assertContainsClassRules(
+ recordTagClassSubject,
+ hashCodeHelperClassSubject,
+ toStringHelperClassSubject)
+ .assertContainsMethodRules(
+ recordTagInstanceInitializerSubject,
+ equalsHelperMethodSubject,
+ getFieldsAsObjectsMethodSubject,
+ hashCodeHelperMethodSubject,
+ toStringHelperMethodSubject))
+ .assertContainsNoOtherRules();
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceSyntheticClassWithNonSyntheticMethodTest.java b/src/test/java/com/android/tools/r8/retrace/RetraceSyntheticClassWithNonSyntheticMethodTest.java
new file mode 100644
index 0000000..9b8a324
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceSyntheticClassWithNonSyntheticMethodTest.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2023, 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.retrace;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.DiagnosticsHandler;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.references.ClassReference;
+import com.android.tools.r8.references.Reference;
+import com.android.tools.r8.utils.StringUtils;
+import java.util.List;
+import java.util.stream.Collectors;
+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 RetraceSyntheticClassWithNonSyntheticMethodTest extends TestBase {
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withNoneRuntime().build();
+ }
+
+ public RetraceSyntheticClassWithNonSyntheticMethodTest(TestParameters parameters) {
+ parameters.assertNoneRuntime();
+ }
+
+ private final ClassReference originalClass = Reference.classFromTypeName("some.Class");
+ private final ClassReference obfuscatedClass = Reference.classFromTypeName("a");
+
+ private final String mapping =
+ StringUtils.lines(
+ "# { id: 'com.android.tools.r8.mapping', version: '1.0' }",
+ originalClass.getTypeName() + " -> " + obfuscatedClass.getTypeName() + ":",
+ " # { id: 'com.android.tools.r8.synthesized' }",
+ " void <clinit>() -> <clinit>");
+
+ @Test
+ public void retraceSyntheticTest() {
+ Retracer retracer =
+ Retracer.createDefault(
+ ProguardMapProducer.fromString(mapping), new DiagnosticsHandler() {});
+ RetraceClassResult retraceClassResult = retracer.retraceClass(obfuscatedClass);
+ List<RetraceClassElement> retracedClasses =
+ retraceClassResult.stream().collect(Collectors.toList());
+ assertEquals(1, retracedClasses.size());
+ RetraceClassElement retraceClassElement = retracedClasses.get(0);
+ assertEquals(originalClass, retraceClassElement.getRetracedClass().getClassReference());
+ assertTrue(retraceClassElement.isCompilerSynthesized());
+ RetraceMethodResult retraceMethodResult = retraceClassResult.lookupMethod("<clinit>");
+ List<RetraceMethodElement> retracedMethods =
+ retraceMethodResult.stream().collect(Collectors.toList());
+ assertEquals(1, retracedMethods.size());
+ RetraceMethodElement retraceMethodElement = retracedMethods.get(0);
+ assertFalse(retraceMethodElement.isCompilerSynthesized());
+ assertTrue(retraceMethodElement.getRetracedMethod().isKnown());
+ assertEquals("<clinit>", retraceMethodElement.getRetracedMethod().getMethodName());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
index 9c48052..6ebcaf7 100644
--- a/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
+++ b/src/test/java/com/android/tools/r8/retrace/RetraceTests.java
@@ -50,6 +50,7 @@
import com.android.tools.r8.retrace.stacktraces.InvalidStackTrace;
import com.android.tools.r8.retrace.stacktraces.MapVersionWarningStackTrace;
import com.android.tools.r8.retrace.stacktraces.MemberFieldOverlapStackTrace;
+import com.android.tools.r8.retrace.stacktraces.MovedSynthetizedInfoStackTraceTest;
import com.android.tools.r8.retrace.stacktraces.MultipleDotsInFileNameStackTrace;
import com.android.tools.r8.retrace.stacktraces.MultipleLinesNoLineNumberStackTrace;
import com.android.tools.r8.retrace.stacktraces.MultipleMapVersionsWarningStackTrace;
@@ -261,6 +262,11 @@
}
@Test
+ public void testMovedSynthetizedInfoStackTraceTest() throws Exception {
+ runRetraceTest(new MovedSynthetizedInfoStackTraceTest());
+ }
+
+ @Test
public void testCircularReferenceStackTrace() throws Exception {
// Proguard retrace (and therefore the default regular expression) will not retrace circular
// reference exceptions.
diff --git a/src/test/java/com/android/tools/r8/retrace/stacktraces/MovedSynthetizedInfoStackTraceTest.java b/src/test/java/com/android/tools/r8/retrace/stacktraces/MovedSynthetizedInfoStackTraceTest.java
new file mode 100644
index 0000000..75724b3
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/retrace/stacktraces/MovedSynthetizedInfoStackTraceTest.java
@@ -0,0 +1,55 @@
+// Copyright (c) 2023, 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.retrace.stacktraces;
+
+import com.android.tools.r8.utils.StringUtils;
+import java.util.Arrays;
+import java.util.List;
+
+public class MovedSynthetizedInfoStackTraceTest implements StackTraceForTest {
+
+ @Override
+ public List<String> obfuscatedStackTrace() {
+ return Arrays.asList(
+ "java.lang.RuntimeException: foobar", "\tat foo.bar.inlinee$synthetic(BaseCommand.java:2)");
+ }
+
+ @Override
+ public String mapping() {
+ return StringUtils.lines(
+ "# { id: 'com.android.tools.r8.mapping', version: '2.2' }",
+ "com.android.tools.r8.BaseCommand$Builder -> foo.bar:",
+ " 1:1:void inlinee(java.util.Collection):0:0 -> inlinee$synthetic",
+ " 1:1:void inlinee$synthetic(java.util.Collection):0:0 -> inlinee$synthetic",
+ " 2:2:void inlinee(java.util.Collection):206:206 -> inlinee$synthetic",
+ " 2:2:void inlinee$synthetic(java.util.Collection):0:0 -> inlinee$synthetic",
+ " # {\"id\":\"com.android.tools.r8.synthesized\"}",
+ " 4:4:void inlinee(java.util.Collection):208:208 -> inlinee$synthetic",
+ " 4:4:void inlinee$synthetic(java.util.Collection):0 -> inlinee$synthetic",
+ " 7:7:void error(origin.Origin,java.lang.Throwable):363:363 -> inlinee$synthetic",
+ " 7:7:void inlinee(java.util.Collection):210 -> inlinee$synthetic",
+ " 7:7:void inlinee$synthetic(java.util.Collection):0:0 -> inlinee$synthetic");
+ }
+
+ @Override
+ public List<String> retracedStackTrace() {
+ return Arrays.asList(
+ "java.lang.RuntimeException: foobar",
+ "\tat com.android.tools.r8.BaseCommand$Builder.inlinee(BaseCommand.java:206)");
+ }
+
+ @Override
+ public List<String> retraceVerboseStackTrace() {
+ return Arrays.asList(
+ "java.lang.RuntimeException: foobar",
+ "\tat com.android.tools.r8.BaseCommand$Builder.void"
+ + " inlinee(java.util.Collection)(BaseCommand.java:206)");
+ }
+
+ @Override
+ public int expectedWarnings() {
+ return 0;
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesR8SpecificTest.java b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesR8SpecificTest.java
new file mode 100644
index 0000000..c3fd851
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesR8SpecificTest.java
@@ -0,0 +1,330 @@
+// Copyright (c) 2023, 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 org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.SemanticVersion;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.ZipUtils.ZipBuilder;
+import com.google.common.collect.ImmutableList;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class LibraryProvidedProguardRulesR8SpecificTest
+ extends LibraryProvidedProguardRulesTestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameter(1)
+ public LibraryType libraryType;
+
+ @Parameter(2)
+ public ProviderType providerType;
+
+ @Parameters(name = "{0}, AAR: {1}, {2}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withNoneRuntime().build(),
+ ImmutableList.of(LibraryType.JAR_WITH_RULES),
+ ProviderType.values());
+ }
+
+ private static final String EXPECTED_A =
+ StringUtils.lines(
+ "-keep class A1 {", " <init>();", "}", "-keep class A2 {", " <init>();", "}");
+
+ private static final String EXPECTED_B =
+ StringUtils.lines(
+ "-keep class B1 {", " <init>();", "}", "-keep class B2 {", " <init>();", "}");
+
+ private static final String EXPECTED_C =
+ StringUtils.lines(
+ "-keep class C1 {", " <init>();", "}", "-keep class C2 {", " <init>();", "}");
+
+ private static final String EXPECTED_D =
+ StringUtils.lines(
+ "-keep class D1 {", " <init>();", "}", "-keep class D2 {", " <init>();", "}");
+
+ private static final String EXPECTED_E =
+ StringUtils.lines(
+ "-keep class E1 {", " <init>();", "}", "-keep class E2 {", " <init>();", "}");
+
+ private static final String EXPECTED_X =
+ StringUtils.lines(
+ "-keep class X1 {", " <init>();", "}", "-keep class X2 {", " <init>();", "}");
+
+ private Path buildLibrary() throws Exception {
+ ZipBuilder jarBuilder =
+ ZipBuilder.builder(temp.newFile(libraryType.isAar() ? "classes.jar" : "test.jar").toPath());
+ if (libraryType.hasRulesInJar()) {
+ jarBuilder.addText("META-INF/com.android.tools/r8/test1.pro", "-keep class A1");
+ jarBuilder.addText("META-INF/com.android.tools/r8/test2.pro", "-keep class A2");
+ jarBuilder.addText("META-INF/com.android.tools/r8-min-4.0.0/test1.pro", "-keep class B1");
+ jarBuilder.addText("META-INF/com.android.tools/r8-min-4.0.0/test2.pro", "-keep class B2");
+ jarBuilder.addText("META-INF/com.android.tools/r8-max-8.1.0/test1.pro", "-keep class C1");
+ jarBuilder.addText("META-INF/com.android.tools/r8-max-8.1.0/test2.pro", "-keep class C2");
+ jarBuilder.addText(
+ "META-INF/com.android.tools/r8-min-5.0.0-max-8.0.0/test1.pro", "-keep class D1");
+ jarBuilder.addText(
+ "META-INF/com.android.tools/r8-min-5.0.0-max-8.0.0/test2.pro", "-keep class D2");
+ jarBuilder.addText("META-INF/com.android.tools/r8-min-10.5.0/test1.pro", "-keep class E1");
+ jarBuilder.addText("META-INF/com.android.tools/r8-min-10.5.0/test2.pro", "-keep class E2");
+ jarBuilder.addText("META-INF/proguard/test1.pro", "-keep class X1");
+ jarBuilder.addText("META-INF/proguard/test2.pro", "-keep class X2");
+ }
+ if (libraryType.isAar()) {
+ // TODO(b/228319861): Also test AARs.
+ fail("Not tested");
+ return null;
+ } else {
+ return jarBuilder.build();
+ }
+ }
+
+ private Path buildLibraryProguardOnlyRules(String directory) throws Exception {
+ ZipBuilder jarBuilder =
+ ZipBuilder.builder(
+ temp.newFolder().toPath().resolve(libraryType.isAar() ? "classes.jar" : "test.jar"));
+ if (libraryType.hasRulesInJar()) {
+ jarBuilder.addText("META-INF/" + directory + "/test1.pro", "-keep class X1");
+ jarBuilder.addText("META-INF/" + directory + "/test2.pro", "-keep class X2");
+ }
+ if (libraryType.isAar()) {
+ // TODO(b/228319861): Also test AARs.
+ fail("Not tested");
+ return null;
+ } else {
+ return jarBuilder.build();
+ }
+ }
+
+ private Path buildLibraryR8VersionAgnosticOnlyRules() throws Exception {
+ ZipBuilder jarBuilder =
+ ZipBuilder.builder(
+ temp.newFolder().toPath().resolve(libraryType.isAar() ? "classes.jar" : "test.jar"));
+ if (libraryType.hasRulesInJar()) {
+ jarBuilder.addText("META-INF/com.android.tools/r8/test1.pro", "-keep class A1");
+ jarBuilder.addText("META-INF/com.android.tools/r8/test2.pro", "-keep class A2");
+ }
+ if (libraryType.isAar()) {
+ // TODO(b/228319861): Also test AARs.
+ fail("Not tested");
+ return null;
+ } else {
+ return jarBuilder.build();
+ }
+ }
+
+ private Path buildLibraryProguardOnlyRules() throws Exception {
+ return buildLibraryProguardOnlyRules("proguard");
+ }
+
+ private void runTest(SemanticVersion compilerVersion, String expected) throws Exception {
+ Path library = buildLibrary();
+ testForR8(Backend.DEX)
+ .applyIf(providerType == ProviderType.API, b -> b.addProgramFiles(library))
+ .applyIf(providerType == ProviderType.INJARS, b -> b.addKeepRules("-injars " + library))
+ .setMinApi(AndroidApiLevel.B)
+ .setFakeCompilerVersion(compilerVersion)
+ .allowUnusedProguardConfigurationRules()
+ .compile()
+ .inspectProguardConfiguration(
+ configuration -> assertEquals(expected, configuration.toString()));
+ }
+
+ @Test
+ public void runTestVersion3() throws Exception {
+ runTest(
+ SemanticVersion.create(3, 0, 0), StringUtils.lines(EXPECTED_A.trim(), EXPECTED_C.trim()));
+ }
+
+ @Test
+ public void runTestVersion4() throws Exception {
+ runTest(
+ SemanticVersion.create(4, 0, 0),
+ StringUtils.lines(EXPECTED_A.trim(), EXPECTED_B.trim(), EXPECTED_C.trim()));
+ }
+
+ @Test
+ public void runTestVersion5() throws Exception {
+ runTest(
+ SemanticVersion.create(5, 0, 0),
+ StringUtils.lines(
+ EXPECTED_A.trim(), EXPECTED_B.trim(), EXPECTED_C.trim(), EXPECTED_D.trim()));
+ }
+
+ @Test
+ public void runTestVersion8() throws Exception {
+ runTest(
+ SemanticVersion.create(8, 0, 0),
+ StringUtils.lines(
+ EXPECTED_A.trim(), EXPECTED_B.trim(), EXPECTED_C.trim(), EXPECTED_D.trim()));
+ }
+
+ @Test
+ public void runTestVersion8_1() throws Exception {
+ runTest(
+ SemanticVersion.create(8, 1, 0),
+ StringUtils.lines(EXPECTED_A.trim(), EXPECTED_B.trim(), EXPECTED_C.trim()));
+ }
+
+ @Test
+ public void runTestVersion8_2() throws Exception {
+ runTest(
+ SemanticVersion.create(8, 2, 0), StringUtils.lines(EXPECTED_A.trim(), EXPECTED_B.trim()));
+ }
+
+ @Test
+ public void runTestVersion10() throws Exception {
+ runTest(
+ SemanticVersion.create(10, 0, 0), StringUtils.lines(EXPECTED_A.trim(), EXPECTED_B.trim()));
+ }
+
+ @Test
+ public void runTestVersion10_5() throws Exception {
+ runTest(
+ SemanticVersion.create(10, 5, 0),
+ StringUtils.lines(EXPECTED_A.trim(), EXPECTED_B.trim(), EXPECTED_E.trim()));
+ }
+
+ @Test
+ public void runTestVersionMainR8VersionSpecificRules() throws Exception {
+ if (!Version.isMainVersion()) {
+ return;
+ }
+ Path library = buildLibrary();
+ testForR8(Backend.DEX)
+ .applyIf(
+ providerType == ProviderType.API,
+ b -> b.addProgramFiles(library).addProgramFiles(library))
+ .applyIf(providerType == ProviderType.INJARS, b -> b.addKeepRules("-injars " + library))
+ .setMinApi(AndroidApiLevel.B)
+ .allowUnusedProguardConfigurationRules()
+ .allowDiagnosticMessages()
+ .compileWithExpectedDiagnostics(
+ diagnostics ->
+ assertEquals(
+ 1,
+ diagnostics.getWarnings().stream()
+ .filter(
+ allOf(
+ diagnosticMessage(containsString("Running R8 version main")),
+ diagnosticMessage(containsString("Using version 8.1.0 for")))
+ ::matches)
+ .count()))
+ .inspectProguardConfiguration(
+ configuration ->
+ assertEquals(
+ StringUtils.lines(EXPECTED_A.trim(), EXPECTED_B.trim(), EXPECTED_C.trim()),
+ configuration.toString()));
+ }
+
+ @Test
+ public void runTestVersionMainR8VersionAgnosticOnlyRules() throws Exception {
+ if (!Version.isMainVersion()) {
+ return;
+ }
+ Path library = buildLibraryR8VersionAgnosticOnlyRules();
+ testForR8(Backend.DEX)
+ .applyIf(providerType == ProviderType.API, b -> b.addProgramFiles(library))
+ .applyIf(providerType == ProviderType.INJARS, b -> b.addKeepRules("-injars " + library))
+ .setMinApi(AndroidApiLevel.B)
+ .allowUnusedProguardConfigurationRules()
+ .compile()
+ .inspectProguardConfiguration(
+ configuration -> assertEquals(EXPECTED_A, configuration.toString()));
+ }
+
+ @Test
+ public void testProguardOnlyRules() throws Exception {
+ Path library = buildLibraryProguardOnlyRules();
+ testForR8(Backend.DEX)
+ .applyIf(providerType == ProviderType.API, b -> b.addProgramFiles(library))
+ .applyIf(providerType == ProviderType.INJARS, b -> b.addKeepRules("-injars " + library))
+ .setMinApi(AndroidApiLevel.B)
+ .setFakeCompilerVersion(SemanticVersion.create(1, 2, 3))
+ .allowUnusedProguardConfigurationRules()
+ .compile()
+ .inspectProguardConfiguration(
+ configuration -> assertEquals(EXPECTED_X, configuration.toString()));
+ }
+
+ @Test
+ public void testProguardOnlyRulesVersionMain() throws Exception {
+ Path library = buildLibraryProguardOnlyRules();
+ testForR8(Backend.DEX)
+ .applyIf(providerType == ProviderType.API, b -> b.addProgramFiles(library))
+ .applyIf(providerType == ProviderType.INJARS, b -> b.addKeepRules("-injars " + library))
+ .setMinApi(AndroidApiLevel.B)
+ .allowUnusedProguardConfigurationRules()
+ .compile()
+ .inspectProguardConfiguration(
+ configuration -> assertEquals(EXPECTED_X, configuration.toString()));
+ }
+
+ @Test
+ public void testUnusedProguardOnlyRules() throws Exception {
+ for (String directory :
+ ImmutableList.of(
+ "proguard-min-6.1.0",
+ "proguard-max-7.0.0",
+ "proguard-min-6.1.0-max-7.0.0",
+ "proguard610",
+ "com.android.tools/proguard",
+ "com.android.tools/proguard-min-6.1.0",
+ "com.android.tools/proguard-max-7.0.0",
+ "com.android.tools/proguard-min-6.1.0-max-7.0.0",
+ "com.android.tools/proguard610")) {
+ Path library = buildLibraryProguardOnlyRules(directory);
+ testForR8(Backend.DEX)
+ .applyIf(providerType == ProviderType.API, b -> b.addProgramFiles(library))
+ .applyIf(providerType == ProviderType.INJARS, b -> b.addKeepRules("-injars " + library))
+ .setMinApi(AndroidApiLevel.B)
+ .setFakeCompilerVersion(SemanticVersion.create(1, 2, 3))
+ .compile()
+ .inspectProguardConfiguration(
+ configuration -> assertEquals("", configuration.toString()));
+ }
+ }
+
+ @Test
+ public void testUnusedProguardOnlyRulesVersionMain() throws Exception {
+ for (String directory :
+ ImmutableList.of(
+ "proguard-min-6.1.0",
+ "proguard-max-7.0.0",
+ "proguard-min-6.1.0-max-7.0.0",
+ "proguard610",
+ "com.android.tools/proguard",
+ "com.android.tools/proguard-min-6.1.0",
+ "com.android.tools/proguard-max-7.0.0",
+ "com.android.tools/proguard-min-6.1.0-max-7.0.0",
+ "com.android.tools/proguard610")) {
+ Path library = buildLibraryProguardOnlyRules(directory);
+ testForR8(Backend.DEX)
+ .applyIf(providerType == ProviderType.API, b -> b.addProgramFiles(library))
+ .applyIf(providerType == ProviderType.INJARS, b -> b.addKeepRules("-injars " + library))
+ .setMinApi(AndroidApiLevel.B)
+ .compile()
+ .inspectProguardConfiguration(
+ configuration -> assertEquals("", configuration.toString()));
+ }
+ }
+}
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 2741404..6472a03 100644
--- a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTest.java
@@ -24,7 +24,6 @@
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.ProgramResourceProvider;
import com.android.tools.r8.ResourceException;
-import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.origin.ArchiveEntryOrigin;
import com.android.tools.r8.origin.Origin;
@@ -47,7 +46,7 @@
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
-public class LibraryProvidedProguardRulesTest extends TestBase {
+public class LibraryProvidedProguardRulesTest extends LibraryProvidedProguardRulesTestBase {
static class A {
private static String buildClassName(String className) {
@@ -66,30 +65,6 @@
static class B {}
- enum LibraryType {
- JAR_WITH_RULES,
- AAR_WITH_RULES,
- AAR_WITH_RULES_ONLY_IN_JAR,
- AAR_WITH_RULES_BOTH_IN_JAR_AND_IN_AAR;
-
- boolean isAar() {
- return this != JAR_WITH_RULES;
- }
-
- boolean hasRulesInJar() {
- return this != AAR_WITH_RULES;
- }
-
- boolean hasRulesInAar() {
- return this == AAR_WITH_RULES || this == AAR_WITH_RULES_BOTH_IN_JAR_AND_IN_AAR;
- }
- }
-
- enum ProviderType {
- API,
- INJARS
- }
-
@Parameter(0)
public TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTestBase.java b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTestBase.java
new file mode 100644
index 0000000..4bca489
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/LibraryProvidedProguardRulesTestBase.java
@@ -0,0 +1,34 @@
+// Copyright (c) 2023, 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.TestBase;
+
+public class LibraryProvidedProguardRulesTestBase extends TestBase {
+
+ enum LibraryType {
+ JAR_WITH_RULES,
+ AAR_WITH_RULES,
+ AAR_WITH_RULES_ONLY_IN_JAR,
+ AAR_WITH_RULES_BOTH_IN_JAR_AND_IN_AAR;
+
+ boolean isAar() {
+ return this != JAR_WITH_RULES;
+ }
+
+ boolean hasRulesInJar() {
+ return this != AAR_WITH_RULES;
+ }
+
+ boolean hasRulesInAar() {
+ return this == AAR_WITH_RULES || this == AAR_WITH_RULES_BOTH_IN_JAR_AND_IN_AAR;
+ }
+ }
+
+ enum ProviderType {
+ API,
+ INJARS
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index d7df00b..478af32 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -13,6 +13,7 @@
import static com.android.tools.r8.synthesis.SyntheticNaming.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
import static org.hamcrest.CoreMatchers.containsString;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.ir.desugar.invokespecial.InvokeSpecialToSelfDesugaring;
import com.android.tools.r8.ir.desugar.itf.InterfaceDesugaringForTesting;
import com.android.tools.r8.references.ClassReference;
@@ -130,6 +131,14 @@
return syntheticClass(classReference, naming.BACKPORT_WITH_FORWARDING, id);
}
+ public static ClassReference syntheticRecordTagClass() {
+ return Reference.classFromDescriptor(DexItemFactory.recordTagDescriptorString);
+ }
+
+ public static ClassReference syntheticRecordHelperClass(ClassReference reference, int id) {
+ return syntheticClass(reference, naming.RECORD_HELPER, id);
+ }
+
public static ClassReference syntheticTwrCloseResourceClass(Class<?> clazz, int id) {
return syntheticClass(clazz, naming.TWR_CLOSE_RESOURCE, id);
}
@@ -155,10 +164,13 @@
}
public static MethodReference syntheticNestInstanceFieldGetter(Field field) {
- FieldReference fieldReference = Reference.fieldFromField(field);
+ return syntheticNestInstanceFieldGetter(Reference.fieldFromField(field));
+ }
+
+ public static MethodReference syntheticNestInstanceFieldGetter(FieldReference fieldReference) {
return Reference.method(
fieldReference.getHolderClass(),
- NEST_ACCESS_FIELD_GET_NAME_PREFIX + field.getName(),
+ NEST_ACCESS_FIELD_GET_NAME_PREFIX + fieldReference.getFieldName(),
Collections.emptyList(),
fieldReference.getFieldType());
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
index ba1898d..7596ee6 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/AbsentClassSubject.java
@@ -130,6 +130,11 @@
}
@Override
+ public TypeSubject getSuperType() {
+ throw new Unreachable("Absent class has no super type");
+ }
+
+ @Override
public boolean isInterface() {
throw new Unreachable("Cannot determine if an absent class is an interface");
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
index 58dbf1d..1aebbdd 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/ClassSubject.java
@@ -187,6 +187,8 @@
@Override
public abstract ClassAccessFlags getAccessFlags();
+ public abstract TypeSubject getSuperType();
+
public abstract boolean isInterface();
public abstract boolean isAbstract();
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
index b973782..81c7400 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeInspector.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.utils.codeinspector;
+import static com.android.tools.r8.utils.ConsumerUtils.emptyConsumer;
+
import com.android.tools.r8.DexIndexedConsumer;
import com.android.tools.r8.StringResource;
import com.android.tools.r8.TestDiagnosticMessagesImpl;
@@ -159,10 +161,15 @@
}
public CodeInspector(AndroidApp app, String proguardMapContent) throws IOException {
+ this(app, proguardMapContent, emptyConsumer());
+ }
+
+ public CodeInspector(
+ AndroidApp app, String proguardMapContent, Consumer<InternalOptions> optionsConsumer)
+ throws IOException {
this(
- new ApplicationReader(app, runOptionsConsumer(null), Timing.empty())
- .read(
- StringResource.fromString(proguardMapContent, Origin.unknown())));
+ new ApplicationReader(app, runOptionsConsumer(optionsConsumer), Timing.empty())
+ .read(StringResource.fromString(proguardMapContent, Origin.unknown())));
}
public CodeInspector(DexApplication application) {
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
index 1f9b372..d711ac1 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/CodeMatchers.java
@@ -4,6 +4,9 @@
package com.android.tools.r8.utils.codeinspector;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.FieldReference;
@@ -12,6 +15,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
@@ -179,14 +183,21 @@
}
public static Matcher<MethodSubject> invokesMethod(MethodSubject targetSubject) {
- if (!targetSubject.isPresent()) {
- throw new IllegalArgumentException();
- }
- return invokesMethod(targetSubject.getFinalReference());
+ return invokesMethod(
+ () -> {
+ assertThat(targetSubject, isPresent());
+ return targetSubject.getFinalReference();
+ });
}
public static Matcher<MethodSubject> invokesMethod(MethodReference targetReference) {
+ return invokesMethod(() -> targetReference);
+ }
+
+ public static Matcher<MethodSubject> invokesMethod(
+ Supplier<MethodReference> targetReferenceSupplier) {
return new TypeSafeMatcher<MethodSubject>() {
+
@Override
protected boolean matchesSafely(MethodSubject subject) {
if (!subject.isPresent()) {
@@ -195,11 +206,13 @@
if (!subject.getMethod().hasCode()) {
return false;
}
+ MethodReference targetReference = targetReferenceSupplier.get();
return subject.streamInstructions().anyMatch(isInvokeWithTarget(targetReference));
}
@Override
public void describeTo(Description description) {
+ MethodReference targetReference = targetReferenceSupplier.get();
description.appendText(
"invokes method `" + MethodReferenceUtils.toSourceString(targetReference) + "`");
}
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
index 738507a..fcfe160 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/FoundClassSubject.java
@@ -388,6 +388,11 @@
}
@Override
+ public TypeSubject getSuperType() {
+ return new TypeSubject(codeInspector, dexClass.getSuperType());
+ }
+
+ @Override
public String getOriginalName() {
if (getNaming() != null) {
return getNaming().originalName;
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
index 4a3d5f9..82eaa9d 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/Matchers.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.utils.codeinspector;
+import static org.hamcrest.CoreMatchers.anything;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -846,6 +847,14 @@
}
}
+ @SuppressWarnings("unchecked")
+ public static <T> Matcher<T> ifThen(boolean condition, Matcher<T> matcher) {
+ if (condition) {
+ return matcher;
+ }
+ return (Matcher<T>) anything();
+ }
+
public static <T> Matcher<T> onlyIf(boolean condition, Matcher<T> matcher) {
return notIf(matcher, !condition);
}
diff --git a/third_party/android_jar/lib-v34.tar.gz.sha1 b/third_party/android_jar/lib-v34.tar.gz.sha1
new file mode 100644
index 0000000..3b7b739
--- /dev/null
+++ b/third_party/android_jar/lib-v34.tar.gz.sha1
@@ -0,0 +1 @@
+ac28074fa7b977e03eb0692248e1edcee36a2c33
\ No newline at end of file
diff --git a/third_party/api_database/api_database.tar.gz.sha1 b/third_party/api_database/api_database.tar.gz.sha1
index 275471b..9883fe5 100644
--- a/third_party/api_database/api_database.tar.gz.sha1
+++ b/third_party/api_database/api_database.tar.gz.sha1
@@ -1 +1 @@
-f081c538df68649432fa8e45ec511d43d5548396
\ No newline at end of file
+29b5c8dfdccf33e7a540d5de29476805a7d5c2f0
\ No newline at end of file
diff --git a/tools/r8_release.py b/tools/r8_release.py
index c799d90..f98e24f 100755
--- a/tools/r8_release.py
+++ b/tools/r8_release.py
@@ -325,6 +325,17 @@
sources.write(re.sub(pattern, replace, line))
+def replace_startswith(prefix, replacement, path):
+ with open(path, "r") as source:
+ lines = source.readlines()
+ with open(path, "w") as source:
+ for line in lines:
+ if line.startswith(prefix):
+ source.write(replacement)
+ else:
+ source.write(line)
+
+
def download_file(version, file, dst):
dir = 'raw' if len(version) != 40 else 'raw/main'
urllib.request.urlretrieve(
@@ -849,6 +860,12 @@
"R8_DEV_BRANCH = '%s.%s" % (str(semver.major), str(semver.minor)),
THIS_FILE_RELATIVE)
+ # Update main version file with the new dev branch.
+ replace_startswith(
+ ' public static final String ACTIVE_DEV_VERSION = ',
+ ' public static final String ACTIVE_DEV_VERSION = "' + branch_version + '.0"',
+ R8_VERSION_FILE)
+
message = \
'Prepare %s for branch %s' % (THIS_FILE_RELATIVE, branch_version)
subprocess.check_call(['git', 'commit', '-a', '-m', message])
diff --git a/tools/test.py b/tools/test.py
index a4e7b6e..e7e2b99 100755
--- a/tools/test.py
+++ b/tools/test.py
@@ -78,6 +78,10 @@
result.add_option('--all-tests', '--all_tests',
help='Run tests in all configurations.',
default=False, action='store_true')
+ result.add_option('--art-profile-rewriting-completeness-check',
+ '--art_profile_rewriting_completeness_check',
+ help='Enable completeness check for ART profile rewriting.',
+ default=False, action='store_true')
result.add_option('--slow-tests', '--slow_tests',
help='Also run slow tests.',
default=False, action='store_true')
@@ -287,6 +291,8 @@
gradle_args.append('-Ponly_internal')
if options.all_tests:
gradle_args.append('-Pall_tests')
+ if options.art_profile_rewriting_completeness_check:
+ gradle_args.append('-Part_profile_rewriting_completeness_check=1')
if options.slow_tests:
gradle_args.append('-Pslow_tests=1')
if options.tool:
diff --git a/tools/utils.py b/tools/utils.py
index c5fb4ce..5cb2cbe 100644
--- a/tools/utils.py
+++ b/tools/utils.py
@@ -181,7 +181,7 @@
assert os.path.exists(build_tools_dir)
return build_tools_dir
else:
- versions = ['30.0.3', '30.0.2', '30.0.1', '30.0.0']
+ versions = ['33.0.1', '32.0.0']
for version in versions:
build_tools_dir = os.path.join(getAndroidHome(), 'build-tools', version)
if os.path.exists(build_tools_dir):