blob: 0f9ad9c28b0dca37851649051dedd53bc952de45 [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.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.DescriptorUtils;
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 java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
public class StartupConfiguration {
private final List<DexType> startupClasses;
private final List<DexMethod> startupMethods;
public StartupConfiguration(List<DexType> startupClasses, List<DexMethod> startupMethods) {
this.startupClasses = startupClasses;
this.startupMethods = startupMethods;
}
/**
* 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.startupclassdescriptors");
if (propertyValue == null) {
return null;
}
List<String> startupDescriptors;
try {
startupDescriptors = FileUtils.readAllLines(Paths.get(propertyValue));
} catch (IOException e) {
throw reporter.fatalError(new ExceptionDiagnostic(e));
}
if (startupDescriptors.isEmpty()) {
return null;
}
List<DexType> startupClasses = new ArrayList<>();
List<DexMethod> startupMethods = new ArrayList<>();
for (String startupDescriptor : startupDescriptors) {
if (startupDescriptor.isEmpty()) {
continue;
}
int methodNameStartIndex = getMethodNameStartIndex(startupDescriptor);
if (methodNameStartIndex >= 0) {
DexMethod startupMethod =
parseStartupMethodDescriptor(startupDescriptor, methodNameStartIndex, dexItemFactory);
if (startupMethod != null) {
startupClasses.add(startupMethod.getHolderType());
startupMethods.add(startupMethod);
} else {
reporter.warning(
new StringDiagnostic("Invalid descriptor for startup method: " + startupDescriptor));
}
} else {
DexType startupClass = parseStartupClassDescriptor(startupDescriptor, dexItemFactory);
if (startupClass != null) {
startupClasses.add(startupClass);
} else {
reporter.warning(
new StringDiagnostic("Invalid descriptor for startup class: " + startupDescriptor));
}
}
}
return new StartupConfiguration(startupClasses, startupMethods);
}
private static int getMethodNameStartIndex(String startupDescriptor) {
int arrowIndex = startupDescriptor.indexOf("->");
return arrowIndex >= 0 ? arrowIndex + 2 : arrowIndex;
}
private static DexType parseStartupClassDescriptor(
String startupClassDescriptor, DexItemFactory dexItemFactory) {
if (DescriptorUtils.isClassDescriptor(startupClassDescriptor)) {
return dexItemFactory.createType(startupClassDescriptor);
} else {
return null;
}
}
private static DexMethod parseStartupMethodDescriptor(
String startupMethodDescriptor, int methodNameStartIndex, DexItemFactory dexItemFactory) {
String classDescriptor = startupMethodDescriptor.substring(0, methodNameStartIndex - 2);
DexType classType = parseStartupClassDescriptor(classDescriptor, dexItemFactory);
if (classType == null) {
return null;
}
String protoWithNameDescriptor = startupMethodDescriptor.substring(methodNameStartIndex);
int methodNameEndIndex = protoWithNameDescriptor.indexOf('(');
if (methodNameEndIndex <= 1) {
return null;
}
String methodName = protoWithNameDescriptor.substring(methodNameEndIndex);
String protoDescriptor = protoWithNameDescriptor.substring(methodNameEndIndex);
DexProto proto = parseStartupMethodProto(protoDescriptor, dexItemFactory);
return dexItemFactory.createMethod(classType, proto, methodName);
}
private static DexProto parseStartupMethodProto(
String protoDescriptor, DexItemFactory dexItemFactory) {
List<DexType> parameterTypes = new ArrayList<>();
for (String parameterTypeDescriptor :
DescriptorUtils.getArgumentTypeDescriptors(protoDescriptor)) {
parameterTypes.add(dexItemFactory.createType(parameterTypeDescriptor));
}
String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(protoDescriptor);
DexType returnType = dexItemFactory.createType(returnTypeDescriptor);
return dexItemFactory.createProto(returnType, parameterTypes);
}
public boolean hasStartupClasses() {
return !startupClasses.isEmpty();
}
public List<DexType> getStartupClasses() {
return startupClasses;
}
}