blob: 3e4f660e2bfe3eea343e7880dafe725831c35b66 [file] [log] [blame]
// 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.experimental.startup;
import com.android.tools.r8.TextInputStream;
import com.android.tools.r8.experimental.startup.profile.NonEmptyStartupProfile;
import com.android.tools.r8.experimental.startup.profile.StartupProfileClassRule;
import com.android.tools.r8.experimental.startup.profile.StartupProfileMethodRule;
import com.android.tools.r8.experimental.startup.profile.StartupProfileRule;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexApplication;
import com.android.tools.r8.graph.DexDefinitionSupplier;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.GraphLens;
import com.android.tools.r8.graph.PrunedItems;
import com.android.tools.r8.profile.AbstractProfile;
import com.android.tools.r8.profile.AbstractProfileRule;
import com.android.tools.r8.profile.art.ArtProfileBuilderUtils;
import com.android.tools.r8.profile.art.HumanReadableArtProfileParser;
import com.android.tools.r8.profile.art.HumanReadableArtProfileParserBuilder;
import com.android.tools.r8.startup.StartupClassBuilder;
import com.android.tools.r8.startup.StartupMethodBuilder;
import com.android.tools.r8.startup.StartupProfileBuilder;
import com.android.tools.r8.startup.StartupProfileProvider;
import com.android.tools.r8.startup.diagnostic.MissingStartupProfileItemsDiagnostic;
import com.android.tools.r8.synthesis.SyntheticItems;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.function.Consumer;
public abstract class StartupProfile
implements AbstractProfile<StartupProfileClassRule, StartupProfileMethodRule> {
protected StartupProfile() {}
public static Builder builder() {
return new Builder();
}
public static Builder builder(
InternalOptions options,
MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder,
StartupProfileProvider startupProfileProvider) {
return new Builder(options, missingItemsDiagnosticBuilder, startupProfileProvider);
}
public static StartupProfile createInitialStartupOrder(
InternalOptions options, DexDefinitionSupplier definitions) {
StartupProfile startupProfile = StartupProfile.parseStartupProfile(options, definitions);
if (startupProfile == null || startupProfile.isEmpty()) {
return empty();
}
StartupProfile.Builder builder = StartupProfile.builder();
for (StartupProfileRule startupItem : startupProfile.getRules()) {
builder.addStartupItem(startupItem);
}
return builder.build();
}
public static StartupProfile createInitialStartupOrderForD8(AppView<?> appView) {
return createInitialStartupOrder(appView.options(), appView);
}
public static StartupProfile createInitialStartupOrderForR8(DexApplication application) {
return createInitialStartupOrder(application.options, application);
}
public static StartupProfile empty() {
return new EmptyStartupProfile();
}
public static StartupProfile merge(Collection<StartupProfile> startupProfiles) {
Builder builder = builder();
for (StartupProfile startupProfile : startupProfiles) {
startupProfile.getRules().forEach(builder::addStartupItem);
}
return builder.build();
}
/**
* Parses the supplied startup configuration, if any. The startup configuration is a list of class
* and method descriptors.
*
* <p>Example:
*
* <pre>
* Landroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
* Landroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
* Landroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
* Landroidx/compose/runtime/CompositionImpl;->applyChanges()V
* Landroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I
* Landroidx/compose/runtime/ComposerImpl;
* </pre>
*/
public static StartupProfile parseStartupProfile(
InternalOptions options, DexDefinitionSupplier definitions) {
if (!options.getStartupOptions().hasStartupProfileProviders()) {
return null;
}
Collection<StartupProfileProvider> startupProfileProviders =
options.getStartupOptions().getStartupProfileProviders();
List<StartupProfile> startupProfiles = new ArrayList<>(startupProfileProviders.size());
for (StartupProfileProvider startupProfileProvider : startupProfileProviders) {
MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder =
new MissingStartupProfileItemsDiagnostic.Builder(definitions)
.setOrigin(startupProfileProvider.getOrigin());
StartupProfile.Builder startupProfileBuilder =
StartupProfile.builder(options, missingItemsDiagnosticBuilder, startupProfileProvider);
startupProfileProvider.getStartupProfile(startupProfileBuilder);
startupProfiles.add(startupProfileBuilder.build());
if (missingItemsDiagnosticBuilder.hasMissingStartupItems()) {
options.reporter.warning(missingItemsDiagnosticBuilder.build());
}
}
return StartupProfile.merge(startupProfiles);
}
public abstract Collection<StartupProfileRule> getRules();
public abstract boolean isEmpty();
public abstract StartupProfile rewrittenWithLens(GraphLens graphLens);
public abstract StartupProfile toStartupOrderForWriting(AppView<?> appView);
public abstract StartupProfile withoutPrunedItems(
PrunedItems prunedItems, SyntheticItems syntheticItems);
public static class Builder
implements AbstractProfile.Builder<
StartupProfileClassRule, StartupProfileMethodRule, StartupProfile, Builder>,
StartupProfileBuilder {
private final DexItemFactory dexItemFactory;
private final MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder;
private Reporter reporter;
private final StartupProfileProvider startupProfileProvider;
private final LinkedHashMap<DexReference, StartupProfileRule> startupItems =
new LinkedHashMap<>();
Builder() {
this.dexItemFactory = null;
this.missingItemsDiagnosticBuilder = null;
this.reporter = null;
this.startupProfileProvider = null;
}
Builder(
InternalOptions options,
MissingStartupProfileItemsDiagnostic.Builder missingItemsDiagnosticBuilder,
StartupProfileProvider startupProfileProvider) {
this.dexItemFactory = options.dexItemFactory();
this.missingItemsDiagnosticBuilder = missingItemsDiagnosticBuilder;
this.reporter = options.reporter;
this.startupProfileProvider = startupProfileProvider;
}
@Override
public Builder addRule(AbstractProfileRule rule) {
return addStartupItem(rule.asStartupProfileRule());
}
@Override
public Builder addClassRule(StartupProfileClassRule classRule) {
return addStartupItem(classRule);
}
@Override
public Builder addMethodRule(StartupProfileMethodRule methodRule) {
return addStartupItem(methodRule);
}
@Override
public Builder addStartupClass(Consumer<StartupClassBuilder> startupClassBuilderConsumer) {
StartupProfileClassRule.Builder startupClassBuilder =
StartupProfileClassRule.builder(dexItemFactory);
startupClassBuilderConsumer.accept(startupClassBuilder);
StartupProfileClassRule startupClass = startupClassBuilder.build();
if (missingItemsDiagnosticBuilder.registerStartupClass(startupClass)) {
return this;
}
return addStartupItem(startupClass);
}
@Override
public Builder addStartupMethod(Consumer<StartupMethodBuilder> startupMethodBuilderConsumer) {
StartupProfileMethodRule.Builder startupMethodBuilder =
StartupProfileMethodRule.builder(dexItemFactory);
startupMethodBuilderConsumer.accept(startupMethodBuilder);
StartupProfileMethodRule startupMethod = startupMethodBuilder.build();
if (missingItemsDiagnosticBuilder.registerStartupMethod(startupMethod)) {
return this;
}
return addStartupItem(startupMethod);
}
@Override
public StartupProfileBuilder addHumanReadableArtProfile(
TextInputStream textInputStream,
Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer) {
HumanReadableArtProfileParser.Builder parserBuilder =
HumanReadableArtProfileParser.builder()
.setReporter(reporter)
.setProfileBuilder(
ArtProfileBuilderUtils.createBuilderForArtProfileToStartupProfileConversion(
this));
parserBuilderConsumer.accept(parserBuilder);
HumanReadableArtProfileParser parser = parserBuilder.build();
parser.parse(textInputStream, startupProfileProvider.getOrigin());
return this;
}
public Builder addStartupItem(StartupProfileRule startupItem) {
startupItems.put(startupItem.getReference(), startupItem);
return this;
}
public Builder apply(Consumer<Builder> consumer) {
consumer.accept(this);
return this;
}
public Builder setIgnoreWarnings() {
return setReporter(null);
}
public Builder setReporter(Reporter reporter) {
this.reporter = reporter;
return this;
}
@Override
public StartupProfile build() {
if (startupItems.isEmpty()) {
return empty();
}
return new NonEmptyStartupProfile(startupItems);
}
}
}