Introduce a parser for startup configuration lines
Change-Id: If75f18b5b4edb53e2f90bebeda6a6a9c32abab0a
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
index d6da209..7e4df1f 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfiguration.java
@@ -6,9 +6,7 @@
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;
@@ -76,110 +74,31 @@
public static StartupConfiguration createStartupConfigurationFromLines(
DexItemFactory dexItemFactory, Reporter reporter, List<String> startupDescriptors) {
+ StartupConfigurationParser<DexType, DexMethod, DexType> parser =
+ StartupConfigurationParser.createDexParser(dexItemFactory);
List<StartupClass<DexType, DexMethod>> startupClasses = new ArrayList<>();
for (String startupDescriptor : startupDescriptors) {
if (startupDescriptor.isEmpty()) {
continue;
}
- StartupClass.Builder<DexType, DexMethod> startupClassBuilder = StartupClass.builder();
- startupDescriptor = parseSyntheticFlag(startupDescriptor, startupClassBuilder);
- parseStartupClassOrMethod(
+ parser.parseLine(
startupDescriptor,
- dexItemFactory,
- startupClass ->
- startupClasses.add(startupClassBuilder.setClassReference(startupClass).build()),
+ startupClasses::add,
// TODO(b/238173796): Startup methods should be added as startup methods.
startupMethod ->
startupClasses.add(
- startupClassBuilder.setClassReference(startupMethod.getHolderType()).build()),
- actual ->
+ StartupClass.dexBuilder()
+ .setClassReference(startupMethod.getReference().getHolderType())
+ .setFlags(startupMethod.getFlags())
+ .build()),
+ error ->
reporter.warning(
new StringDiagnostic(
- "Invalid descriptor for startup class or method: " + actual)));
+ "Invalid descriptor for startup class or method: " + error)));
}
return new StartupConfiguration(startupClasses);
}
- public static String parseSyntheticFlag(
- String startupDescriptor, StartupItem.Builder<?, ?, ?> startupItemBuilder) {
- if (!startupDescriptor.isEmpty() && startupDescriptor.charAt(0) == 'S') {
- startupItemBuilder.setSynthetic();
- return startupDescriptor.substring(1);
- }
- return startupDescriptor;
- }
-
- public static void parseStartupClassOrMethod(
- String startupDescriptor,
- DexItemFactory dexItemFactory,
- Consumer<DexType> startupClassConsumer,
- Consumer<DexMethod> startupMethodConsumer,
- Consumer<String> parseErrorHandler) {
- int arrowStartIndex = getArrowStartIndex(startupDescriptor);
- if (arrowStartIndex >= 0) {
- DexMethod startupMethod =
- parseStartupMethodDescriptor(startupDescriptor, arrowStartIndex, dexItemFactory);
- if (startupMethod != null) {
- startupMethodConsumer.accept(startupMethod);
- } else {
- parseErrorHandler.accept(startupDescriptor);
- }
- } else {
- DexType startupClass = parseStartupClassDescriptor(startupDescriptor, dexItemFactory);
- if (startupClass != null) {
- startupClassConsumer.accept(startupClass);
- } else {
- parseErrorHandler.accept(startupDescriptor);
- }
- }
- }
-
- private static int getArrowStartIndex(String startupDescriptor) {
- return startupDescriptor.indexOf("->");
- }
-
- 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 arrowStartIndex, DexItemFactory dexItemFactory) {
- String classDescriptor = startupMethodDescriptor.substring(0, arrowStartIndex);
- DexType classType = parseStartupClassDescriptor(classDescriptor, dexItemFactory);
- if (classType == 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);
- 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();
}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupConfigurationParser.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfigurationParser.java
new file mode 100644
index 0000000..ff88cf2
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupConfigurationParser.java
@@ -0,0 +1,150 @@
+// 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::typeFromDescriptor);
+ }
+
+ 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);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java b/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java
index d38b1ce..c745c90 100644
--- a/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java
+++ b/src/main/java/com/android/tools/r8/experimental/startup/StartupItem.java
@@ -120,13 +120,22 @@
public StartupItem<C, M, ?> build() {
if (classReference != null) {
- return new StartupClass<>(flags, classReference);
+ return buildStartupClass();
} else {
- assert methodReference != null;
- return new StartupMethod<>(flags, methodReference);
+ return buildStartupMethod();
}
}
+ public StartupClass<C, M> buildStartupClass() {
+ assert classReference != null;
+ return new StartupClass<>(flags, classReference);
+ }
+
+ public StartupMethod<C, M> buildStartupMethod() {
+ assert methodReference != null;
+ return new StartupMethod<>(flags, methodReference);
+ }
+
@SuppressWarnings("unchecked")
public B self() {
return (B) this;
diff --git a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
index 695bb3a..7962792 100644
--- a/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
+++ b/src/test/java/com/android/tools/r8/startup/utils/StartupTestingUtils.java
@@ -15,12 +15,14 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.ThrowableConsumer;
import com.android.tools.r8.experimental.startup.StartupConfiguration;
+import com.android.tools.r8.experimental.startup.StartupConfigurationParser;
import com.android.tools.r8.experimental.startup.StartupItem;
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.TypeReference;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.ClassReferenceUtils;
import com.android.tools.r8.utils.MethodReferenceUtils;
@@ -71,23 +73,18 @@
public static void removeStartupListFromStdout(
D8TestRunResult runResult,
Consumer<StartupItem<ClassReference, MethodReference, ?>> startupItemConsumer) {
- DexItemFactory dexItemFactory = new DexItemFactory();
+ StartupConfigurationParser<ClassReference, MethodReference, TypeReference> parser =
+ StartupConfigurationParser.createReferenceParser();
StringBuilder stdoutBuilder = new StringBuilder();
String startupDescriptorPrefix = "[" + startupInstrumentationTag + "] ";
for (String line : StringUtils.splitLines(runResult.getStdOut(), true)) {
if (line.startsWith(startupDescriptorPrefix)) {
- StartupItem.Builder<ClassReference, MethodReference, ?> startupItemBuilder =
- StartupItem.builder();
String message = line.substring(startupDescriptorPrefix.length());
- message = StartupConfiguration.parseSyntheticFlag(message, startupItemBuilder);
- StartupConfiguration.parseStartupClassOrMethod(
+ parser.parseLine(
message,
- dexItemFactory,
- startupClass -> startupItemBuilder.setClassReference(startupClass.asClassReference()),
- startupMethod ->
- startupItemBuilder.setMethodReference(startupMethod.asMethodReference()),
+ startupItemConsumer,
+ startupItemConsumer,
error -> fail("Unexpected parse error: " + error));
- startupItemConsumer.accept(startupItemBuilder.build());
} else {
stdoutBuilder.append(line).append(System.lineSeparator());
}