blob: 77d0005a15b8cc579d689620e7c6f20e66650fa8 [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.synthesis.SyntheticNaming.COMPANION_CLASS_SUFFIX;
import static com.android.tools.r8.synthesis.SyntheticNaming.EXTERNAL_SYNTHETIC_CLASS_SEPARATOR;
import com.android.tools.r8.TextInputStream;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.references.ClassReference;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.startup.StartupProfileBuilder;
import com.android.tools.r8.utils.MethodReferenceUtils;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.function.Consumer;
public class ArtProfileBuilderUtils {
public interface SyntheticToSyntheticContextGeneralization {
ClassReference getSyntheticContextReference(ClassReference classReference);
/**
* When a startup profile is given to D8, the program input should be dex and the startup
* profile should have been generated by launching the dex that is given on input. Therefore,
* synthetic items in the ART profile should be present in the program input to D8 with the
* exact same synthetic names as in the ART profile. This means that there is no need to
* generalize synthetic items to their synthetic context.
*/
static SyntheticToSyntheticContextGeneralization createForD8() {
return classReference -> null;
}
/**
* When a startup profile is given to R8, the program input is class files and the startup
* profile should have been generated by dexing the program input (in release and intermediate
* mode) and then launching the resulting app. The synthetic items in the resulting ART profile
* do not exist in the program input to R8 (and the same synthetics may receive different names
* in the R8 compilation). Therefore, synthetic items in the ART profile are generalized into
* matching all synthetics from their synthetic context.
*/
static SyntheticToSyntheticContextGeneralization createForR8() {
return classReference -> {
// TODO(b/243777722): Move this logic into synthetic items and extend the mapping from
// synthetic classes to their synthetic context to all synthetic kinds.
String classDescriptor = classReference.getDescriptor();
for (int i = 1; i < classDescriptor.length() - 1; i++) {
if (classDescriptor.charAt(i) != '$') {
continue;
}
if (classDescriptor.regionMatches(
i, COMPANION_CLASS_SUFFIX, 0, COMPANION_CLASS_SUFFIX.length())
|| classDescriptor.regionMatches(
i,
EXTERNAL_SYNTHETIC_CLASS_SEPARATOR,
0,
EXTERNAL_SYNTHETIC_CLASS_SEPARATOR.length())) {
return Reference.classFromDescriptor(classDescriptor.substring(0, i) + ";");
}
}
return null;
};
}
}
/**
* Helper for creating an {@link ArtProfileBuilder} that performs callbacks on the given {@param
* startupProfileBuilder}.
*/
public static ArtProfileBuilder createBuilderForArtProfileToStartupProfileConversion(
StartupProfileBuilder startupProfileBuilder,
SyntheticToSyntheticContextGeneralization syntheticToSyntheticContextGeneralization) {
return new ArtProfileBuilder() {
@Override
public ArtProfileBuilder addClassRule(
Consumer<ArtProfileClassRuleBuilder> classRuleBuilderConsumer) {
MutableArtProfileClassRule classRule = new MutableArtProfileClassRule();
classRuleBuilderConsumer.accept(classRule);
ClassReference syntheticContextReference =
syntheticToSyntheticContextGeneralization.getSyntheticContextReference(
classRule.getClassReference());
if (syntheticContextReference == null) {
startupProfileBuilder.addStartupClass(
startupClassBuilder ->
startupClassBuilder.setClassReference(classRule.getClassReference()));
} else {
startupProfileBuilder.addSyntheticStartupMethod(
syntheticStartupMethodBuilder ->
syntheticStartupMethodBuilder.setSyntheticContextReference(
syntheticContextReference));
}
return this;
}
@Override
public ArtProfileBuilder addMethodRule(
Consumer<ArtProfileMethodRuleBuilder> methodRuleBuilderConsumer) {
MutableArtProfileMethodRule methodRule = new MutableArtProfileMethodRule();
methodRuleBuilderConsumer.accept(methodRule);
ClassReference syntheticContextReference =
syntheticToSyntheticContextGeneralization.getSyntheticContextReference(
methodRule.getMethodReference().getHolderClass());
if (syntheticContextReference == null) {
startupProfileBuilder.addStartupMethod(
startupMethodBuilder ->
startupMethodBuilder.setMethodReference(methodRule.getMethodReference()));
} else {
startupProfileBuilder.addSyntheticStartupMethod(
syntheticStartupMethodBuilder ->
syntheticStartupMethodBuilder.setSyntheticContextReference(
syntheticContextReference));
}
return this;
}
@Override
public ArtProfileBuilder addHumanReadableArtProfile(
TextInputStream textInputStream,
Consumer<HumanReadableArtProfileParserBuilder> parserBuilderConsumer) {
// The ART profile parser never calls addHumanReadableArtProfile().
throw new Unreachable();
}
};
}
static class MutableArtProfileClassRule implements ArtProfileClassRuleBuilder {
private ClassReference classReference;
MutableArtProfileClassRule() {}
public ClassReference getClassReference() {
return classReference;
}
@Override
public ArtProfileClassRuleBuilder setClassReference(ClassReference classReference) {
this.classReference = classReference;
return this;
}
public ArtProfileClassRuleInfo getClassRuleInfo() {
return ArtProfileClassRuleInfoImpl.empty();
}
public void writeHumanReadableRuleString(OutputStreamWriter writer) throws IOException {
writer.write(classReference.getDescriptor());
}
}
static class MutableArtProfileMethodRule implements ArtProfileMethodRuleBuilder {
private MethodReference methodReference;
private ArtProfileMethodRuleInfoImpl methodRuleInfo = ArtProfileMethodRuleInfoImpl.empty();
MutableArtProfileMethodRule() {}
public MethodReference getMethodReference() {
return methodReference;
}
public ArtProfileMethodRuleInfo getMethodRuleInfo() {
return methodRuleInfo;
}
@Override
public ArtProfileMethodRuleBuilder setMethodReference(MethodReference methodReference) {
this.methodReference = methodReference;
return this;
}
@Override
public ArtProfileMethodRuleBuilder setMethodRuleInfo(
Consumer<ArtProfileMethodRuleInfoBuilder> methodRuleInfoBuilderConsumer) {
ArtProfileMethodRuleInfoImpl.Builder methodRuleInfoBuilder =
ArtProfileMethodRuleInfoImpl.builder();
methodRuleInfoBuilderConsumer.accept(methodRuleInfoBuilder);
methodRuleInfo = methodRuleInfoBuilder.build();
return this;
}
public void writeHumanReadableRuleString(OutputStreamWriter writer) throws IOException {
methodRuleInfo.writeHumanReadableFlags(writer);
writer.write(MethodReferenceUtils.toSmaliString(methodReference));
}
}
}