blob: 97d49a51e91beb247000497f994e43698fe0e4df [file] [log] [blame]
// Copyright (c) 2020, 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.graph;
import static com.android.tools.r8.utils.DescriptorUtils.getClassBinaryNameFromDescriptor;
import static com.android.tools.r8.utils.DescriptorUtils.getDescriptorFromClassBinaryName;
import com.android.tools.r8.errors.Unimplemented;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.DescriptorUtils;
import com.google.common.collect.ImmutableList;
import java.lang.reflect.GenericSignatureFormatError;
import java.nio.CharBuffer;
import java.util.List;
import java.util.function.Function;
/**
* Internal encoding of the generics signature attribute as defined by JVMS 7 $ 4.3.4.
* <pre>
* ClassSignature ::=
* FormalTypeParameters? SuperclassSignature SuperinterfaceSignature*
*
*
* FormalTypeParameters ::=
* < FormalTypeParameter+ >
*
* FormalTypeParameter ::=
* Identifier ClassBound InterfaceBound*
*
* ClassBound ::=
* : FieldTypeSignature?
*
* InterfaceBound ::=
* : FieldTypeSignature
*
* SuperclassSignature ::=
* ClassTypeSignature
*
* SuperinterfaceSignature ::=
* ClassTypeSignature
*
*
* FieldTypeSignature ::=
* ClassTypeSignature
* ArrayTypeSignature
* TypeVariableSignature
*
*
* ClassTypeSignature ::=
* L PackageSpecifier? SimpleClassTypeSignature ClassTypeSignatureSuffix* ;
*
* PackageSpecifier ::=
* Identifier / PackageSpecifier*
*
* SimpleClassTypeSignature ::=
* Identifier TypeArguments?
*
* ClassTypeSignatureSuffix ::=
* . SimpleClassTypeSignature
*
* TypeVariableSignature ::=
* T Identifier ;
*
* TypeArguments ::=
* < TypeArgument+ >
*
* TypeArgument ::=
* WildcardIndicator? FieldTypeSignature
* *
*
* WildcardIndicator ::=
* +
* -
*
* ArrayTypeSignature ::=
* [ TypeSignature
*
* TypeSignature ::=
* FieldTypeSignature
* BaseType
*
*
* MethodTypeSignature ::=
* FormalTypeParameters? (TypeSignature*) ReturnType ThrowsSignature*
*
* ReturnType ::=
* TypeSignature
* VoidDescriptor
*
* ThrowsSignature ::=
* ^ ClassTypeSignature
* ^ TypeVariableSignature
* </pre>
*/
public class GenericSignature {
private static final List<FormalTypeParameter> EMPTY_TYPE_PARAMS = ImmutableList.of();
interface DexDefinitionSignature<T extends DexDefinition> {
default boolean isClassSignature() {
return false;
}
default ClassSignature asClassSignature() {
return null;
}
default boolean isFieldTypeSignature() {
return false;
}
default FieldTypeSignature asFieldTypeSignature() {
return null;
}
default boolean isMethodTypeSignature() {
return false;
}
default MethodTypeSignature asMethodTypeSignature() {
return null;
}
}
public static class FormalTypeParameter {
final String name;
final FieldTypeSignature classBound;
final List<FieldTypeSignature> interfaceBounds;
FormalTypeParameter(
String name, FieldTypeSignature classBound, List<FieldTypeSignature> interfaceBounds) {
this.name = name;
this.classBound = classBound;
this.interfaceBounds = interfaceBounds;
}
public String getName() {
return name;
}
public FieldTypeSignature getClassBound() {
return classBound;
}
public List<FieldTypeSignature> getInterfaceBounds() {
return interfaceBounds;
}
}
public static class ClassSignature implements DexDefinitionSignature<DexClass> {
static final ClassSignature UNKNOWN_CLASS_SIGNATURE =
new ClassSignature(
ImmutableList.of(),
ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE,
ImmutableList.of());
final List<FormalTypeParameter> formalTypeParameters;
final ClassTypeSignature superClassSignature;
final List<ClassTypeSignature> superInterfaceSignatures;
ClassSignature(
List<FormalTypeParameter> formalTypeParameters,
ClassTypeSignature superClassSignature,
List<ClassTypeSignature> superInterfaceSignatures) {
assert formalTypeParameters != null;
assert superClassSignature != null;
assert superInterfaceSignatures != null;
this.formalTypeParameters = formalTypeParameters;
this.superClassSignature = superClassSignature;
this.superInterfaceSignatures = superInterfaceSignatures;
}
public ClassTypeSignature superClassSignature() {
return superClassSignature;
}
public List<ClassTypeSignature> superInterfaceSignatures() {
return superInterfaceSignatures;
}
@Override
public boolean isClassSignature() {
return true;
}
@Override
public ClassSignature asClassSignature() {
return this;
}
}
public abstract static class TypeSignature {
public boolean isFieldTypeSignature() {
return false;
}
public FieldTypeSignature asFieldTypeSignature() {
return null;
}
public boolean isBaseTypeSignature() {
return false;
}
public BaseTypeSignature asBaseTypeSignature() {
return null;
}
public TypeSignature toArrayTypeSignature(AppView<?> appView) {
return null;
}
public TypeSignature toArrayElementTypeSignature(AppView<?> appView) {
return null;
}
}
// TODO(b/129925954): better structures for a circle of
// TypeSignature - FieldTypeSignature - ClassTypeSignature - TypeArgument
public abstract static class FieldTypeSignature
extends TypeSignature implements DexDefinitionSignature<DexEncodedField> {
@Override
public boolean isFieldTypeSignature() {
return true;
}
@Override
public FieldTypeSignature asFieldTypeSignature() {
return this;
}
public boolean isClassTypeSignature() {
return false;
}
public ClassTypeSignature asClassTypeSignature() {
return null;
}
public boolean isArrayTypeSignature() {
return false;
}
public ArrayTypeSignature asArrayTypeSignature() {
return null;
}
public boolean isTypeVariableSignature() {
return false;
}
public TypeVariableSignature asTypeVariableSignature() {
return null;
}
public boolean isUnknown() {
return this == ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE;
}
}
public static class ClassTypeSignature extends FieldTypeSignature {
static final ClassTypeSignature UNKNOWN_CLASS_TYPE_SIGNATURE =
new ClassTypeSignature(DexItemFactory.nullValueType, ImmutableList.of());
final DexType type;
// E.g., for Map<K, V>, a signature will indicate what types are for K and V.
// Note that this could be nested, e.g., Map<K, Consumer<V>>.
// TODO(b/129925954): What about * ?
final List<FieldTypeSignature> typeArguments;
// TODO(b/129925954): towards immutable structure?
// Double-linked enclosing-inner relations.
ClassTypeSignature enclosingTypeSignature;
ClassTypeSignature innerTypeSignature;
ClassTypeSignature(DexType type, List<FieldTypeSignature> typeArguments) {
assert type != null;
assert typeArguments != null;
this.type = type;
this.typeArguments = typeArguments;
}
public DexType type() {
return type;
}
public List<FieldTypeSignature> typeArguments() {
return typeArguments;
}
@Override
public boolean isClassTypeSignature() {
return true;
}
@Override
public ClassTypeSignature asClassTypeSignature() {
return this;
}
@Override
public ArrayTypeSignature toArrayTypeSignature(AppView<?> appView) {
return new ArrayTypeSignature(this);
}
static void link(ClassTypeSignature outer, ClassTypeSignature inner) {
assert outer.innerTypeSignature == null && inner.enclosingTypeSignature == null;
outer.innerTypeSignature = inner;
inner.enclosingTypeSignature = outer;
}
// TODO(b/129925954): rewrite GenericSignatureRewriter with this pattern?
public interface Converter<R> {
R init();
R visitType(DexType type, R result);
R visitTypeArgument(FieldTypeSignature typeArgument, R result);
R visitInnerTypeSignature(ClassTypeSignature innerTypeSignature, R result);
}
public <R> R convert(Converter<R> converter) {
R result = converter.init();
result = converter.visitType(type, result);
for (FieldTypeSignature typeArgument : typeArguments) {
result = converter.visitTypeArgument(typeArgument, result);
}
if (innerTypeSignature != null) {
result = converter.visitInnerTypeSignature(innerTypeSignature, result);
}
return result;
}
}
public static class ArrayTypeSignature extends FieldTypeSignature {
final TypeSignature elementSignature;
ArrayTypeSignature(TypeSignature elementSignature) {
assert elementSignature != null;
this.elementSignature = elementSignature;
}
public TypeSignature elementSignature() {
return elementSignature;
}
@Override
public boolean isArrayTypeSignature() {
return true;
}
@Override
public ArrayTypeSignature asArrayTypeSignature() {
return this;
}
@Override
public TypeSignature toArrayTypeSignature(AppView<?> appView) {
return new ArrayTypeSignature(this);
}
@Override
public TypeSignature toArrayElementTypeSignature(AppView<?> appView) {
return elementSignature;
}
}
public static class TypeVariableSignature extends FieldTypeSignature {
final String typeVariable;
private TypeVariableSignature(String typeVariable) {
assert typeVariable != null;
this.typeVariable = typeVariable;
}
@Override
public boolean isTypeVariableSignature() {
return true;
}
@Override
public TypeVariableSignature asTypeVariableSignature() {
return this;
}
@Override
public ArrayTypeSignature toArrayTypeSignature(AppView<?> appView) {
return new ArrayTypeSignature(this);
}
public String getTypeVariable() {
return typeVariable;
}
}
// TODO(b/129925954): Canonicalization?
public static class BaseTypeSignature extends TypeSignature {
final DexType type;
BaseTypeSignature(DexType type) {
assert type != null;
assert type.isPrimitiveType() : type.toDescriptorString();
this.type = type;
}
@Override
public boolean isBaseTypeSignature() {
return true;
}
@Override
public BaseTypeSignature asBaseTypeSignature() {
return this;
}
@Override
public ArrayTypeSignature toArrayTypeSignature(AppView<?> appView) {
assert !type.isVoidType();
return new ArrayTypeSignature(this);
}
}
public static class ReturnType {
static final ReturnType VOID = new ReturnType(null);
// `null` indicates that it's `void`.
final TypeSignature typeSignature;
ReturnType(TypeSignature typeSignature) {
this.typeSignature = typeSignature;
}
public boolean isVoidDescriptor() {
return typeSignature == null;
}
public TypeSignature typeSignature() {
return typeSignature;
}
}
public static class MethodTypeSignature implements DexDefinitionSignature<DexEncodedMethod> {
static final MethodTypeSignature UNKNOWN_METHOD_TYPE_SIGNATURE =
new MethodTypeSignature(
ImmutableList.of(), ImmutableList.of(), ReturnType.VOID, ImmutableList.of());
final List<FormalTypeParameter> formalTypeParameters;
final List<TypeSignature> typeSignatures;
final ReturnType returnType;
final List<TypeSignature> throwsSignatures;
MethodTypeSignature(
final List<FormalTypeParameter> formalTypeParameters,
List<TypeSignature> typeSignatures,
ReturnType returnType,
List<TypeSignature> throwsSignatures) {
assert formalTypeParameters != null;
assert typeSignatures != null;
assert returnType != null;
assert throwsSignatures != null;
this.formalTypeParameters = formalTypeParameters;
this.typeSignatures = typeSignatures;
this.returnType = returnType;
this.throwsSignatures = throwsSignatures;
}
public TypeSignature getParameterTypeSignature(int i) {
if (typeSignatures.isEmpty() || i < 0 || i >= typeSignatures.size()) {
return null;
}
return typeSignatures.get(i);
}
public ReturnType returnType() {
return returnType;
}
public List<TypeSignature> throwsSignatures() {
return throwsSignatures;
}
@Override
public boolean isMethodTypeSignature() {
return true;
}
@Override
public MethodTypeSignature asMethodTypeSignature() {
return this;
}
public List<FormalTypeParameter> getFormalTypeParameters() {
return formalTypeParameters;
}
}
enum Kind {
CLASS, FIELD, METHOD;
static Kind fromDexDefinition(DexDefinition definition) {
if (definition.isDexClass()) {
return CLASS;
}
if (definition.isDexEncodedField()) {
return FIELD;
}
if (definition.isDexEncodedMethod()) {
return METHOD;
}
throw new Unreachable("Unexpected kind of DexDefinition: " + definition);
}
Function<String, ? extends DexDefinitionSignature<? extends DexDefinition>>
parserMethod(Parser parser) {
switch (this) {
case CLASS:
return parser::parseClassSignature;
case FIELD:
return parser::parseFieldTypeSignature;
case METHOD:
return parser::parseMethodTypeSignature;
}
throw new Unreachable("Unexpected kind: " + this);
}
}
public static class Parser {
// TODO(b/129925954): Can we merge variants of to*Signature below and just expose
// type-parameterized version of this, like
// <T extends DexDefinitionSignature<?>> T toGenericSignature
// without unchecked cast?
private static DexDefinitionSignature<? extends DexDefinition> toGenericSignature(
DexClass currentClassContext,
DexDefinition definition,
AppView<AppInfoWithLiveness> appView) {
DexAnnotationSet annotations = definition.annotations();
if (annotations.annotations.length == 0) {
return null;
}
for (int i = 0; i < annotations.annotations.length; i++) {
DexAnnotation annotation = annotations.annotations[i];
if (!DexAnnotation.isSignatureAnnotation(annotation, appView.dexItemFactory())) {
continue;
}
Kind kind = Kind.fromDexDefinition(definition);
Parser parser = new Parser(currentClassContext, appView);
String signature = DexAnnotation.getSignature(annotation);
try {
return kind.parserMethod(parser).apply(signature);
} catch (GenericSignatureFormatError e) {
appView.options().warningInvalidSignature(
definition, currentClassContext.getOrigin(), signature, e);
}
}
return null;
}
public static ClassSignature toClassSignature(
DexClass clazz, AppView<AppInfoWithLiveness> appView) {
DexDefinitionSignature<?> signature = toGenericSignature(clazz, clazz, appView);
if (signature != null) {
assert signature.isClassSignature();
return signature.asClassSignature();
}
return ClassSignature.UNKNOWN_CLASS_SIGNATURE;
}
public static FieldTypeSignature toFieldTypeSignature(
DexEncodedField field, AppView<AppInfoWithLiveness> appView) {
DexClass currentClassContext = appView.definitionFor(field.holder());
DexDefinitionSignature<?> signature =
toGenericSignature(currentClassContext, field, appView);
if (signature != null) {
assert signature.isFieldTypeSignature();
return signature.asFieldTypeSignature();
}
return ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE;
}
public static MethodTypeSignature toMethodTypeSignature(
DexEncodedMethod method, AppView<AppInfoWithLiveness> appView) {
DexClass currentClassContext = appView.definitionFor(method.holder());
DexDefinitionSignature<?> signature =
toGenericSignature(currentClassContext, method, appView);
if (signature != null) {
assert signature.isMethodTypeSignature();
return signature.asMethodTypeSignature();
}
return MethodTypeSignature.UNKNOWN_METHOD_TYPE_SIGNATURE;
}
/*
* Parser:
*/
private char symbol; // 0: eof; else valid term symbol or first char of identifier.
private String identifier;
/*
* Scanner:
* eof is private to the scan methods
* and it's set only when a scan is issued at the end of the buffer.
*/
private boolean eof;
private char[] buffer;
private int pos;
private Parser(DexClass currentClassContext, AppView<AppInfoWithLiveness> appView) {
this.currentClassContext = currentClassContext;
this.appView = appView;
}
ClassSignature parseClassSignature(String signature) {
try {
setInput(signature);
return parseClassSignature();
} catch (Unimplemented e) {
// TODO(b/129925954): Should not catch this once fully implemented
return ClassSignature.UNKNOWN_CLASS_SIGNATURE;
} catch (GenericSignatureFormatError e) {
throw e;
} catch (Throwable t) {
Error e = new GenericSignatureFormatError(
"Unknown error parsing class signature: " + t.getMessage());
e.addSuppressed(t);
throw e;
}
}
MethodTypeSignature parseMethodTypeSignature(String signature) {
try {
setInput(signature);
return parseMethodTypeSignature();
} catch (Unimplemented e) {
// TODO(b/129925954): Should not catch this once fully implemented
return MethodTypeSignature.UNKNOWN_METHOD_TYPE_SIGNATURE;
} catch (GenericSignatureFormatError e) {
throw e;
} catch (Throwable t) {
Error e = new GenericSignatureFormatError(
"Unknown error parsing method signature: " + t.getMessage());
e.addSuppressed(t);
throw e;
}
}
FieldTypeSignature parseFieldTypeSignature(String signature) {
try {
setInput(signature);
return parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
} catch (Unimplemented e) {
// TODO(b/129925954): Should not catch this once fully implemented
return ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE;
} catch (GenericSignatureFormatError e) {
throw e;
} catch (Throwable t) {
Error e = new GenericSignatureFormatError(
"Unknown error parsing field signature: " + t.getMessage());
e.addSuppressed(t);
throw e;
}
}
private void setInput(String input) {
this.buffer = input.toCharArray();
this.eof = false;
pos = 0;
symbol = 0;
identifier = null;
scanSymbol();
}
//
// Action:
//
enum ParserPosition {
CLASS_SUPER_OR_INTERFACE_ANNOTATION,
ENCLOSING_INNER_OR_TYPE_ANNOTATION,
MEMBER_ANNOTATION
}
private final AppView<AppInfoWithLiveness> appView;
private final DexClass currentClassContext;
private DexType lastWrittenType = null;
private DexType parsedTypeName(String name, ParserPosition parserPosition) {
if (parserPosition == ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION
&& lastWrittenType == null) {
// We are writing type-arguments for a merged class.
return null;
}
String originalDescriptor = getDescriptorFromClassBinaryName(name);
DexType type =
appView.graphLense().lookupType(appView.dexItemFactory().createType(originalDescriptor));
if (appView.appInfo().wasPruned(type)) {
type = appView.dexItemFactory().objectType;
}
if (parserPosition == ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION
&& currentClassContext != null) {
// We may have merged the type down to the current class type.
DexString classDescriptor = currentClassContext.type.descriptor;
if (!originalDescriptor.equals(classDescriptor.toString())
&& type.descriptor.equals(classDescriptor)) {
lastWrittenType = null;
return type;
}
}
lastWrittenType = type;
return type;
}
private DexType parsedInnerTypeName(DexType enclosingType, String name) {
if (enclosingType == null) {
// We are writing inner type names
return null;
}
assert enclosingType.isClassType();
String enclosingDescriptor = enclosingType.toDescriptorString();
DexType type =
appView
.dexItemFactory()
.createType(
getDescriptorFromClassBinaryName(
getClassBinaryNameFromDescriptor(enclosingDescriptor)
+ DescriptorUtils.INNER_CLASS_SEPARATOR
+ name));
return appView.graphLense().lookupType(type);
}
//
// Parser:
//
private ClassSignature parseClassSignature() {
// ClassSignature ::= FormalTypeParameters? SuperclassSignature SuperinterfaceSignature*.
List<FormalTypeParameter> formalTypeParameters = parseOptFormalTypeParameters();
// SuperclassSignature ::= ClassTypeSignature.
ClassTypeSignature superClassSignature =
parseClassTypeSignature(ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION);
ImmutableList.Builder<ClassTypeSignature> builder = ImmutableList.builder();
while (symbol > 0) {
// SuperinterfaceSignature ::= ClassTypeSignature.
builder.add(parseClassTypeSignature(ParserPosition.CLASS_SUPER_OR_INTERFACE_ANNOTATION));
}
return new ClassSignature(formalTypeParameters, superClassSignature, builder.build());
}
private List<FormalTypeParameter> parseOptFormalTypeParameters() {
// FormalTypeParameters ::= "<" FormalTypeParameter+ ">".
if (symbol != '<') {
return EMPTY_TYPE_PARAMS;
}
scanSymbol();
ImmutableList.Builder<FormalTypeParameter> builder = ImmutableList.builder();
while ((symbol != '>') && (symbol > 0)) {
builder.add(updateFormalTypeParameter());
}
expect('>');
return builder.build();
}
private FormalTypeParameter updateFormalTypeParameter() {
// FormalTypeParameter ::= Identifier ClassBound InterfaceBound*.
scanIdentifier();
assert identifier != null;
String typeParameterIdentifier = identifier;
// ClassBound ::= ":" FieldTypeSignature?.
expect(':');
FieldTypeSignature classBound = ClassTypeSignature.UNKNOWN_CLASS_TYPE_SIGNATURE;
if (symbol == 'L' || symbol == '[' || symbol == 'T') {
classBound = parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION);
}
// Only build the interfacebound builder, which is uncommon, if we actually see an interface.
ImmutableList.Builder<FieldTypeSignature> builder = null;
while (symbol == ':') {
// InterfaceBound ::= ":" FieldTypeSignature.
if (builder == null) {
builder = ImmutableList.builder();
}
scanSymbol();
builder.add(parseFieldTypeSignature(ParserPosition.MEMBER_ANNOTATION));
}
if (builder == null) {
return new FormalTypeParameter(typeParameterIdentifier, classBound, null);
}
return new FormalTypeParameter(typeParameterIdentifier, classBound, builder.build());
}
private FieldTypeSignature parseFieldTypeSignature(ParserPosition parserPosition) {
// FieldTypeSignature ::= ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature.
switch (symbol) {
case 'L':
return parseClassTypeSignature(parserPosition);
case '[':
// ArrayTypeSignature ::= "[" TypeSignature.
scanSymbol();
TypeSignature baseTypeSignature = updateTypeSignature(parserPosition);
return baseTypeSignature.toArrayTypeSignature(appView).asFieldTypeSignature();
case 'T':
return updateTypeVariableSignature();
default:
parseError("Expected L, [ or T", pos);
}
throw new Unreachable("Either FieldTypeSignature is returned or a parse error is thrown.");
}
private ClassTypeSignature parseClassTypeSignature(ParserPosition parserPosition) {
// ClassTypeSignature ::=
// "L" (Identifier "/")* Identifier TypeArguments? ("." Identifier TypeArguments?)* ";".
expect('L');
StringBuilder qualIdent = new StringBuilder();
scanIdentifier();
assert identifier != null;
while (symbol == '/') {
qualIdent.append(identifier).append(symbol);
scanSymbol();
scanIdentifier();
assert identifier != null;
}
qualIdent.append(this.identifier);
DexType parsedEnclosingType = parsedTypeName(qualIdent.toString(), parserPosition);
List<FieldTypeSignature> typeArguments = updateOptTypeArguments();
ClassTypeSignature outerMostTypeSignature =
new ClassTypeSignature(parsedEnclosingType, typeArguments);
ClassTypeSignature outerTypeSignature = outerMostTypeSignature;
ClassTypeSignature innerTypeSignature;
while (symbol == '.') {
// Deal with Member Classes.
scanSymbol();
scanIdentifier();
assert identifier != null;
parsedEnclosingType = parsedInnerTypeName(parsedEnclosingType, identifier);
typeArguments = updateOptTypeArguments();
innerTypeSignature = new ClassTypeSignature(parsedEnclosingType, typeArguments);
ClassTypeSignature.link(outerTypeSignature, innerTypeSignature);
outerTypeSignature = innerTypeSignature;
}
expect(';');
return outerMostTypeSignature;
}
private List<FieldTypeSignature> updateOptTypeArguments() {
ImmutableList.Builder<FieldTypeSignature> builder = ImmutableList.builder();
// OptTypeArguments ::= "<" TypeArgument+ ">".
if (symbol == '<') {
scanSymbol();
builder.add(updateTypeArgument());
while ((symbol != '>') && (symbol > 0)) {
builder.add(updateTypeArgument());
}
expect('>');
}
return builder.build();
}
private FieldTypeSignature updateTypeArgument() {
// TypeArgument ::= (["+" | "-"] FieldTypeSignature) | "*".
if (symbol == '*') {
scanSymbol();
throw new Unimplemented("GenericSignature.TypeArgument *");
} else if (symbol == '+') {
scanSymbol();
return parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION);
} else if (symbol == '-') {
scanSymbol();
return parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION);
} else {
return parseFieldTypeSignature(ParserPosition.ENCLOSING_INNER_OR_TYPE_ANNOTATION);
}
}
private TypeVariableSignature updateTypeVariableSignature() {
// TypeVariableSignature ::= "T" Identifier ";".
expect('T');
scanIdentifier();
assert identifier != null;
expect(';');
return new TypeVariableSignature(identifier);
}
private TypeSignature updateTypeSignature(ParserPosition parserPosition) {
switch (symbol) {
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
DexType type = appView.dexItemFactory().createType(String.valueOf(symbol));
BaseTypeSignature baseTypeSignature = new BaseTypeSignature(type);
scanSymbol();
return baseTypeSignature;
default:
// Not an elementary type, but a FieldTypeSignature.
return parseFieldTypeSignature(parserPosition);
}
}
private MethodTypeSignature parseMethodTypeSignature() {
// MethodTypeSignature ::=
// FormalTypeParameters? "(" TypeSignature* ")" ReturnType ThrowsSignature*.
List<FormalTypeParameter> formalTypeParameters = parseOptFormalTypeParameters();
expect('(');
ImmutableList.Builder<TypeSignature> parameterSignatureBuilder = ImmutableList.builder();
while (symbol != ')' && (symbol > 0)) {
parameterSignatureBuilder.add(updateTypeSignature(ParserPosition.MEMBER_ANNOTATION));
}
expect(')');
ReturnType returnType = updateReturnType();
ImmutableList.Builder<TypeSignature> throwsSignatureBuilder = ImmutableList.builder();
if (symbol == '^') {
do {
scanSymbol();
// ThrowsSignature ::= ("^" ClassTypeSignature) | ("^" TypeVariableSignature).
if (symbol == 'T') {
throwsSignatureBuilder.add(updateTypeVariableSignature());
} else {
throwsSignatureBuilder.add(parseClassTypeSignature(ParserPosition.MEMBER_ANNOTATION));
}
} while (symbol == '^');
}
return new MethodTypeSignature(
formalTypeParameters,
parameterSignatureBuilder.build(),
returnType,
throwsSignatureBuilder.build());
}
private ReturnType updateReturnType() {
// ReturnType ::= TypeSignature | "V".
if (symbol != 'V') {
return new ReturnType(updateTypeSignature(ParserPosition.MEMBER_ANNOTATION));
} else {
scanSymbol();
return ReturnType.VOID;
}
}
//
// Scanner:
//
private void scanSymbol() {
if (!eof) {
assert buffer != null;
if (pos < buffer.length) {
symbol = buffer[pos];
pos++;
} else {
symbol = 0;
eof = true;
}
} else {
parseError("Unexpected end of signature", pos);
}
}
private void expect(char c) {
if (eof) {
parseError("Unexpected end of signature", pos);
}
if (symbol == c) {
scanSymbol();
} else {
parseError("Expected " + c, pos - 1);
}
}
private boolean isStopSymbol(char ch) {
switch (ch) {
case ':':
case '/':
case ';':
case '<':
case '.':
return true;
default:
return false;
}
}
// PRE: symbol is the first char of the identifier.
// POST: symbol = the next symbol AFTER the identifier.
private void scanIdentifier() {
if (!eof && pos < buffer.length) {
StringBuilder identifierBuilder = new StringBuilder(32);
if (!isStopSymbol(symbol)) {
identifierBuilder.append(symbol);
char[] bufferLocal = buffer;
assert bufferLocal != null;
do {
char ch = bufferLocal[pos];
if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'))
|| !isStopSymbol(ch)) {
identifierBuilder.append(bufferLocal[pos]);
pos++;
} else {
identifier = identifierBuilder.toString();
scanSymbol();
return;
}
} while (pos != bufferLocal.length);
identifier = identifierBuilder.toString();
symbol = 0;
eof = true;
} else {
// Identifier starts with incorrect char.
symbol = 0;
eof = true;
parseError();
}
} else {
parseError("Unexpected end of signature", pos);
}
}
private void parseError() {
parseError("Unexpected", pos);
}
private void parseError(String message, int pos) {
String arrow = CharBuffer.allocate(pos).toString().replace('\0', ' ') + '^';
throw new GenericSignatureFormatError(
message + " at position " + (pos + 1) + System.lineSeparator()
+ String.valueOf(buffer) + System.lineSeparator()
+ arrow);
}
}
}