blob: 973142312bd7347b38f0ffd7b3853e440ae89d3c [file] [log] [blame]
// Copyright (c) 2021, 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.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.ExceptionDiagnostic;
import com.android.tools.r8.utils.FileUtils;
import com.android.tools.r8.utils.Reporter;
import com.android.tools.r8.utils.StringDiagnostic;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class StartupConfiguration {
private final List<StartupClass<DexType, DexMethod>> startupClasses;
public StartupConfiguration(List<StartupClass<DexType, DexMethod>> startupClasses) {
this.startupClasses = startupClasses;
}
public static Builder builder() {
return new Builder();
}
/**
* 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 StartupConfiguration createStartupConfiguration(
DexItemFactory dexItemFactory, Reporter reporter) {
String propertyValue = System.getProperty("com.android.tools.r8.startup.config");
return propertyValue != null
? createStartupConfigurationFromFile(dexItemFactory, reporter, Paths.get(propertyValue))
: null;
}
public static StartupConfiguration createStartupConfigurationFromFile(
DexItemFactory dexItemFactory, Reporter reporter, Path path) {
reporter.warning("Use of startupconfig is experimental");
List<String> startupDescriptors;
try {
startupDescriptors = FileUtils.readAllLines(path);
} catch (IOException e) {
throw reporter.fatalError(new ExceptionDiagnostic(e));
}
if (startupDescriptors.isEmpty()) {
return null;
}
return createStartupConfigurationFromLines(dexItemFactory, reporter, startupDescriptors);
}
public static StartupConfiguration createStartupConfigurationFromLines(
DexItemFactory dexItemFactory, Reporter reporter, List<String> startupDescriptors) {
List<StartupClass<DexType, DexMethod>> startupClasses = new ArrayList<>();
StartupConfigurationParser.createDexParser(dexItemFactory)
.parseLines(
startupDescriptors,
startupClasses::add,
// TODO(b/238173796): Startup methods should be added as startup methods.
startupMethod ->
startupClasses.add(
StartupClass.dexBuilder()
.setClassReference(startupMethod.getReference().getHolderType())
.setFlags(startupMethod.getFlags())
.build()),
error ->
reporter.warning(
new StringDiagnostic(
"Invalid descriptor for startup class or method: " + error)));
return new StartupConfiguration(startupClasses);
}
public boolean hasStartupClasses() {
return !startupClasses.isEmpty();
}
public List<StartupClass<DexType, DexMethod>> getStartupClasses() {
return startupClasses;
}
public static class Builder {
private final ImmutableList.Builder<StartupClass<DexType, DexMethod>> startupClassesBuilder =
ImmutableList.builder();
public Builder addStartupItem(StartupItem<DexType, DexMethod, ?> startupItem) {
if (startupItem.isStartupClass()) {
return addStartupClass(startupItem.asStartupClass());
} else {
assert startupItem.isStartupMethod();
return addStartupMethod(startupItem.asStartupMethod());
}
}
public Builder addStartupClass(StartupClass<DexType, DexMethod> startupClass) {
this.startupClassesBuilder.add(startupClass);
return this;
}
public Builder addStartupMethod(StartupMethod<DexType, DexMethod> startupMethod) {
// TODO(b/238173796): Startup methods should be added as startup methods.
return addStartupClass(
StartupClass.dexBuilder()
.setFlags(startupMethod.getFlags())
.setClassReference(startupMethod.getReference().getHolderType())
.build());
}
public Builder apply(Consumer<Builder> consumer) {
consumer.accept(this);
return this;
}
public StartupConfiguration build() {
return new StartupConfiguration(startupClassesBuilder.build());
}
}
}