blob: 79ebc1e6ca1c830b40700a5f462fa6e250c5b0b4 [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.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexType;
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.references.TypeReference;
import com.android.tools.r8.utils.DescriptorUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
public class StartupConfigurationParser<C, M, T> {
interface MethodFactory<C, M, T> {
M createMethod(
C methodHolder, String methodName, List<T> methodParameterTypes, T methodReturnType);
}
private final Function<String, C> classFactory;
private final MethodFactory<C, M, T> methodFactory;
private final Function<String, T> typeFactory;
StartupConfigurationParser(
Function<String, C> classFactory,
MethodFactory<C, M, T> methodFactory,
Function<String, T> typeFactory) {
this.classFactory = classFactory;
this.methodFactory = methodFactory;
this.typeFactory = typeFactory;
}
public static StartupConfigurationParser<DexType, DexMethod, DexType> createDexParser(
DexItemFactory dexItemFactory) {
return new StartupConfigurationParser<>(
dexItemFactory::createType,
(methodHolder, methodName, methodParameters, methodReturnType) ->
dexItemFactory.createMethod(
methodHolder,
dexItemFactory.createProto(methodReturnType, methodParameters),
dexItemFactory.createString(methodName)),
dexItemFactory::createType);
}
public static StartupConfigurationParser<ClassReference, MethodReference, TypeReference>
createReferenceParser() {
return new StartupConfigurationParser<>(
Reference::classFromDescriptor, Reference::method, Reference::returnTypeFromDescriptor);
}
public void parseLines(
List<String> startupDescriptors,
Consumer<? super StartupClass<C, M>> startupClassConsumer,
Consumer<? super StartupMethod<C, M>> startupMethodConsumer,
Consumer<String> parseErrorHandler) {
for (String startupDescriptor : startupDescriptors) {
if (!startupDescriptor.isEmpty()) {
parseLine(
startupDescriptor, startupClassConsumer, startupMethodConsumer, parseErrorHandler);
}
}
}
public void parseLine(
String startupDescriptor,
Consumer<? super StartupClass<C, M>> startupClassConsumer,
Consumer<? super StartupMethod<C, M>> startupMethodConsumer,
Consumer<String> parseErrorHandler) {
StartupItem.Builder<C, M, ?> startupItemBuilder = StartupItem.builder();
startupDescriptor = parseSyntheticFlag(startupDescriptor, startupItemBuilder);
parseStartupClassOrMethod(
startupDescriptor,
startupItemBuilder,
startupClassConsumer,
startupMethodConsumer,
parseErrorHandler);
}
private static String parseSyntheticFlag(
String startupDescriptor, StartupItem.Builder<?, ?, ?> startupItemBuilder) {
if (!startupDescriptor.isEmpty() && startupDescriptor.charAt(0) == 'S') {
startupItemBuilder.setSynthetic();
return startupDescriptor.substring(1);
}
return startupDescriptor;
}
private void parseStartupClassOrMethod(
String startupDescriptor,
StartupItem.Builder<C, M, ?> startupItemBuilder,
Consumer<? super StartupClass<C, M>> startupClassConsumer,
Consumer<? super StartupMethod<C, M>> startupMethodConsumer,
Consumer<String> parseErrorHandler) {
int arrowStartIndex = getArrowStartIndex(startupDescriptor);
if (arrowStartIndex >= 0) {
M startupMethod = parseStartupMethodDescriptor(startupDescriptor, arrowStartIndex);
if (startupMethod != null) {
startupMethodConsumer.accept(
startupItemBuilder.setMethodReference(startupMethod).buildStartupMethod());
} else {
parseErrorHandler.accept(startupDescriptor);
}
} else {
C startupClass = parseStartupClassDescriptor(startupDescriptor);
if (startupClass != null) {
startupClassConsumer.accept(
startupItemBuilder.setClassReference(startupClass).buildStartupClass());
} else {
parseErrorHandler.accept(startupDescriptor);
}
}
}
private static int getArrowStartIndex(String startupDescriptor) {
return startupDescriptor.indexOf("->");
}
private C parseStartupClassDescriptor(String startupClassDescriptor) {
if (DescriptorUtils.isClassDescriptor(startupClassDescriptor)) {
return classFactory.apply(startupClassDescriptor);
} else {
return null;
}
}
private M parseStartupMethodDescriptor(String startupMethodDescriptor, int arrowStartIndex) {
String classDescriptor = startupMethodDescriptor.substring(0, arrowStartIndex);
C methodHolder = parseStartupClassDescriptor(classDescriptor);
if (methodHolder == null) {
return null;
}
int methodNameStartIndex = arrowStartIndex + 2;
String protoWithNameDescriptor = startupMethodDescriptor.substring(methodNameStartIndex);
int methodNameEndIndex = protoWithNameDescriptor.indexOf('(');
if (methodNameEndIndex <= 0) {
return null;
}
String methodName = protoWithNameDescriptor.substring(0, methodNameEndIndex);
String protoDescriptor = protoWithNameDescriptor.substring(methodNameEndIndex);
return parseStartupMethodProto(methodHolder, methodName, protoDescriptor);
}
private M parseStartupMethodProto(C methodHolder, String methodName, String protoDescriptor) {
List<T> parameterTypes = new ArrayList<>();
for (String parameterTypeDescriptor :
DescriptorUtils.getArgumentTypeDescriptors(protoDescriptor)) {
parameterTypes.add(typeFactory.apply(parameterTypeDescriptor));
}
String returnTypeDescriptor = DescriptorUtils.getReturnTypeDescriptor(protoDescriptor);
T returnType = typeFactory.apply(returnTypeDescriptor);
return methodFactory.createMethod(methodHolder, methodName, parameterTypes, returnType);
}
}