blob: 11aa25aa1d1810674d2015552c404a93a00445ce [file]
// Copyright (c) 2018, 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;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.references.MethodReference;
import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.DescriptorUtils;
import com.android.tools.r8.utils.StringDiagnostic;
import com.android.tools.r8.utils.internal.collections.Pair;
import com.android.tools.r8.utils.internal.exceptions.Unreachable;
import java.util.function.Consumer;
public class BaseCompilerCommandParser {
public static final String LIB_FLAG = "--lib";
public static final String MIN_API_FLAG = "--min-api";
public static final String OUTPUT_FLAG = "--output";
public static final String THREAD_COUNT_FLAG = "--thread-count";
public static void parsePositiveIntArgument(
Consumer<Diagnostic> errorConsumer,
String flag,
String argument,
Origin origin,
Consumer<Integer> setter) {
int value;
try {
value = Integer.parseInt(argument);
} catch (NumberFormatException e) {
errorConsumer.accept(
new StringDiagnostic("Invalid argument to " + flag + ": " + argument, origin));
return;
}
if (value < 1) {
errorConsumer.accept(
new StringDiagnostic("Invalid argument to " + flag + ": " + argument, origin));
return;
}
setter.accept(value);
}
private static final String PACKAGE_ASSERTION_POSTFIX = "...";
private enum AssertionTransformationType {
ENABLE,
DISABLE,
PASSTHROUGH,
HANDLER
}
private static AssertionsConfiguration.Builder prepareBuilderForScope(
AssertionsConfiguration.Builder builder,
AssertionTransformationType transformation,
MethodReference assertionHandler) {
switch (transformation) {
case ENABLE:
return builder.setCompileTimeEnable();
case DISABLE:
return builder.setCompileTimeDisable();
case PASSTHROUGH:
return builder.setPassthrough();
case HANDLER:
return builder.setAssertionHandler(assertionHandler);
default:
throw new Unreachable();
}
}
private static <C extends BaseCompilerCommand, B extends BaseCompilerCommand.Builder<C, B>>
void addAssertionTransformation(
B builder,
AssertionTransformationType transformation,
MethodReference assertionHandler,
String scope) {
if (scope == null) {
builder.addAssertionsConfiguration(
b -> prepareBuilderForScope(b, transformation, assertionHandler).setScopeAll().build());
} else {
assert !scope.isEmpty();
if (scope.endsWith(PACKAGE_ASSERTION_POSTFIX)) {
builder.addAssertionsConfiguration(
b ->
prepareBuilderForScope(b, transformation, assertionHandler)
.setScopePackage(
scope.substring(0, scope.length() - PACKAGE_ASSERTION_POSTFIX.length()))
.build());
} else {
builder.addAssertionsConfiguration(
b ->
prepareBuilderForScope(b, transformation, assertionHandler)
.setScopeClass(scope)
.build());
}
}
}
protected static <C extends BaseCompilerCommand, B extends BaseCompilerCommand.Builder<C, B>>
String parseAssertionScope(B builder, String suffix, Origin origin) {
if (suffix.isEmpty()) {
return null;
}
if (suffix.equals(":")) {
throw builder.fatalError(new StringDiagnostic("Missing optional argument", origin));
}
if (!suffix.startsWith(":")) {
builder.error(new StringDiagnostic("Illegal assertion scope: " + suffix, origin));
return suffix;
}
String classOrPackageScope = suffix.substring(1);
if (classOrPackageScope.contains(";")
|| classOrPackageScope.contains("[")
|| classOrPackageScope.contains("/")) {
builder.error(
new StringDiagnostic("Illegal assertion scope: " + classOrPackageScope, origin));
}
return classOrPackageScope;
}
static <C extends BaseCompilerCommand, B extends BaseCompilerCommand.Builder<C, B>>
Pair<MethodReference, String> parseAssertionHandler(B builder, String suffix, Origin origin) {
if (suffix.isEmpty() || suffix.equals(":")) {
throw builder.fatalError(
new StringDiagnostic("Missing required argument <handler method>", origin));
}
if (!suffix.startsWith(":")) {
throw builder.fatalError(
new StringDiagnostic("Missing required argument <handler method>", origin));
}
String remaining = suffix.substring(1);
int index = remaining.indexOf(':');
if (index == 0) {
throw builder.fatalError(
new StringDiagnostic("Missing required argument <handler method>", origin));
}
String assertionsHandlerString = index > 0 ? remaining.substring(0, index) : remaining;
int lastDotIndex = assertionsHandlerString.lastIndexOf('.');
if (assertionsHandlerString.length() < 3
|| lastDotIndex <= 0
|| lastDotIndex == assertionsHandlerString.length() - 1
|| !DescriptorUtils.isValidJavaType(assertionsHandlerString.substring(0, lastDotIndex))) {
throw builder.fatalError(
new StringDiagnostic(
"Invalid argument <handler method>: " + assertionsHandlerString, origin));
}
MethodReference assertionsHandler =
Reference.methodFromDescriptor(
DescriptorUtils.javaTypeToDescriptor(
assertionsHandlerString.substring(0, lastDotIndex)),
assertionsHandlerString.substring(lastDotIndex + 1),
"(Ljava/lang/Throwable;)V");
String scopeSuffix = remaining.substring(assertionsHandlerString.length());
String scope = parseAssertionScope(builder, scopeSuffix, origin);
return Pair.create(assertionsHandler, scope);
}
protected static <C extends BaseCompilerCommand, B extends BaseCompilerCommand.Builder<C, B>>
void parseForceEnableAssertions(B builder, String suffix, Origin origin) {
String scope = parseAssertionScope(builder, suffix, origin);
addAssertionTransformation(builder, AssertionTransformationType.ENABLE, null, scope);
}
protected static <C extends BaseCompilerCommand, B extends BaseCompilerCommand.Builder<C, B>>
void parseForceDisableAssertions(B builder, String suffix, Origin origin) {
String scope = parseAssertionScope(builder, suffix, origin);
addAssertionTransformation(builder, AssertionTransformationType.DISABLE, null, scope);
}
protected static <C extends BaseCompilerCommand, B extends BaseCompilerCommand.Builder<C, B>>
void parseForcePassthroughAssertions(B builder, String suffix, Origin origin) {
String scope = parseAssertionScope(builder, suffix, origin);
addAssertionTransformation(builder, AssertionTransformationType.PASSTHROUGH, null, scope);
}
protected static <C extends BaseCompilerCommand, B extends BaseCompilerCommand.Builder<C, B>>
void parseForceAssertionsHandler(B builder, String suffix, Origin origin) {
Pair<MethodReference, String> handlerAndScope = parseAssertionHandler(builder, suffix, origin);
addAssertionTransformation(
builder,
AssertionTransformationType.HANDLER,
handlerAndScope.getFirst(),
handlerAndScope.getSecond());
}
}