blob: 11d080b84d8a955f5f06e8904b0fd74df1ddfbf5 [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.profile.art;
import static com.android.tools.r8.utils.MapUtils.ignoreKey;
import com.android.tools.r8.TextInputStream;
import com.android.tools.r8.TextOutputStream;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
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.InternalOptions;
import com.android.tools.r8.utils.Reporter;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
public class ArtProfile {
private final List<ArtProfileRule> rules;
ArtProfile(List<ArtProfileRule> rules) {
assert !rules.isEmpty();
this.rules = rules;
}
public static Builder builder(ArtProfileProvider artProfileProvider, InternalOptions options) {
return new Builder(artProfileProvider, options);
}
public ArtProfile rewrittenWithLens(GraphLens lens) {
return transform(
(classRule, builderFactory) -> builderFactory.accept(lens.lookupType(classRule.getType())),
(methodRule, builderFactory) ->
builderFactory
.apply(lens.getRenamedMethodSignature(methodRule.getMethod()))
.acceptMethodRuleInfoBuilder(
methodRuleInfoBuilder ->
methodRuleInfoBuilder.merge(methodRule.getMethodRuleInfo())));
}
public ArtProfile rewrittenWithLens(NamingLens lens, DexItemFactory dexItemFactory) {
assert !lens.isIdentityLens();
return transform(
(classRule, builderFactory) ->
builderFactory.accept(lens.lookupType(classRule.getType(), dexItemFactory)),
(methodRule, builderFactory) ->
builderFactory
.apply(lens.lookupMethod(methodRule.getMethod(), dexItemFactory))
.acceptMethodRuleInfoBuilder(
methodRuleInfoBuilder ->
methodRuleInfoBuilder.merge(methodRule.getMethodRuleInfo())));
}
public ArtProfile withoutPrunedItems(PrunedItems prunedItems) {
return transform(
(classRule, builderFactory) -> {
if (!prunedItems.isRemoved(classRule.getType())) {
builderFactory.accept(classRule.getType());
}
},
(methodRule, builderFactory) -> {
if (!prunedItems.isRemoved(methodRule.getMethod())) {
builderFactory
.apply(methodRule.getMethod())
.acceptMethodRuleInfoBuilder(
methodRuleInfoBuilder ->
methodRuleInfoBuilder.merge(methodRule.getMethodRuleInfo()));
}
});
}
private ArtProfile transform(
BiConsumer<ArtProfileClassRule, Consumer<DexType>> classTransformation,
BiConsumer<ArtProfileMethodRule, Function<DexMethod, ArtProfileMethodRule.Builder>>
methodTransformation) {
Map<DexReference, ArtProfileRule.Builder> ruleBuilders = new LinkedHashMap<>();
for (ArtProfileRule rule : rules) {
if (rule.isClassRule()) {
// Supply a factory method for creating a builder. If the current rule should be included in
// the rewritten profile, the caller should call the provided builder factory method to
// create a class rule builder. If two rules are mapped to the same reference, the same rule
// builder is reused so that the two rules are merged into a single rule (with their flags
// merged).
classTransformation.accept(
rule.asClassRule(),
newType ->
ruleBuilders
.computeIfAbsent(
newType, ignoreKey(() -> ArtProfileClassRule.builder().setType(newType)))
.asClassRuleBuilder());
} else {
// As above.
assert rule.isMethodRule();
methodTransformation.accept(
rule.asMethodRule(),
newMethod ->
ruleBuilders
.computeIfAbsent(
newMethod,
ignoreKey(() -> ArtProfileMethodRule.builder().setMethod(newMethod)))
.asMethodRuleBuilder());
}
}
ImmutableList.Builder<ArtProfileRule> newRules =
ImmutableList.builderWithExpectedSize(ruleBuilders.size());
ruleBuilders.values().forEach(ruleBuilder -> newRules.add(ruleBuilder.build()));
return new ArtProfile(newRules.build());
}
public void supplyConsumer(ArtProfileConsumer consumer, Reporter reporter) {
if (consumer != null) {
TextOutputStream textOutputStream = consumer.getHumanReadableArtProfileConsumer();
if (textOutputStream != null) {
supplyHumanReadableArtProfileConsumer(textOutputStream);
}
ArtProfileRuleConsumer ruleConsumer = consumer.getRuleConsumer();
if (ruleConsumer != null) {
supplyRuleConsumer(ruleConsumer);
}
consumer.finished(reporter);
}
}
private void supplyHumanReadableArtProfileConsumer(TextOutputStream textOutputStream) {
try {
try (OutputStreamWriter outputStreamWriter =
new OutputStreamWriter(
textOutputStream.getOutputStream(), textOutputStream.getCharset())) {
for (ArtProfileRule rule : rules) {
rule.writeHumanReadableRuleString(outputStreamWriter);
outputStreamWriter.write('\n');
}
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private void supplyRuleConsumer(ArtProfileRuleConsumer ruleConsumer) {
for (ArtProfileRule rule : rules) {
rule.accept(
classRule ->
ruleConsumer.acceptClassRule(
classRule.getClassReference(), classRule.getClassRuleInfo()),
methodRule ->
ruleConsumer.acceptMethodRule(
methodRule.getMethodReference(), methodRule.getMethodRuleInfo()));
}
}
public static class Builder implements ArtProfileBuilder {
private final ArtProfileProvider artProfileProvider;
private final DexItemFactory dexItemFactory;
private Reporter reporter;
private final List<ArtProfileRule> rules = new ArrayList<>();
Builder(ArtProfileProvider artProfileProvider, InternalOptions options) {
this.artProfileProvider = artProfileProvider;
this.dexItemFactory = options.dexItemFactory();
this.reporter = options.reporter;
}
@Override
public ArtProfileBuilder addClassRule(
Consumer<ArtProfileClassRuleBuilder> classRuleBuilderConsumer) {
ArtProfileClassRule.Builder classRuleBuilder = ArtProfileClassRule.builder(dexItemFactory);
classRuleBuilderConsumer.accept(classRuleBuilder);
rules.add(classRuleBuilder.build());
return this;
}
@Override
public ArtProfileBuilder addMethodRule(
Consumer<ArtProfileMethodRuleBuilder> methodRuleBuilderConsumer) {
ArtProfileMethodRule.Builder methodRuleBuilder = ArtProfileMethodRule.builder(dexItemFactory);
methodRuleBuilderConsumer.accept(methodRuleBuilder);
rules.add(methodRuleBuilder.build());
return this;
}
@Override
public ArtProfileBuilder addHumanReadableArtProfile(
TextInputStream textInputStream,
Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer) {
HumanReadableArtProfileParser.Builder parserBuilder =
HumanReadableArtProfileParser.builder().setReporter(reporter).setProfileBuilder(this);
parserBuilderConsumer.accept(parserBuilder);
HumanReadableArtProfileParser parser = parserBuilder.build();
parser.parse(textInputStream, artProfileProvider.getOrigin());
return this;
}
public ArtProfile build() {
return new ArtProfile(rules);
}
}
}