blob: afd740ee0b7d05bc3407f3e1bddf672795f10401 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 Google, Inc and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Stefan Xenos (Google) - Initial implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core.nd.indexer;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.codegen.AnnotationTargetTypeConstants;
import org.eclipse.jdt.internal.compiler.env.ClassSignature;
import org.eclipse.jdt.internal.compiler.env.EnumConstantSignature;
import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
import org.eclipse.jdt.internal.compiler.env.IBinaryElementValuePair;
import org.eclipse.jdt.internal.compiler.env.IBinaryField;
import org.eclipse.jdt.internal.compiler.env.IBinaryMethod;
import org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.SignatureWrapper;
import org.eclipse.jdt.internal.core.JarPackageFragmentRoot;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jdt.internal.core.Openable;
import org.eclipse.jdt.internal.core.PackageFragment;
import org.eclipse.jdt.internal.core.nd.Nd;
import org.eclipse.jdt.internal.core.nd.db.IndexException;
import org.eclipse.jdt.internal.core.nd.java.JavaIndex;
import org.eclipse.jdt.internal.core.nd.java.JavaNames;
import org.eclipse.jdt.internal.core.nd.java.NdAnnotation;
import org.eclipse.jdt.internal.core.nd.java.NdAnnotationInConstant;
import org.eclipse.jdt.internal.core.nd.java.NdAnnotationInMethod;
import org.eclipse.jdt.internal.core.nd.java.NdAnnotationInMethodParameter;
import org.eclipse.jdt.internal.core.nd.java.NdAnnotationInType;
import org.eclipse.jdt.internal.core.nd.java.NdAnnotationInVariable;
import org.eclipse.jdt.internal.core.nd.java.NdAnnotationValuePair;
import org.eclipse.jdt.internal.core.nd.java.NdBinding;
import org.eclipse.jdt.internal.core.nd.java.NdComplexTypeSignature;
import org.eclipse.jdt.internal.core.nd.java.NdConstant;
import org.eclipse.jdt.internal.core.nd.java.NdConstantAnnotation;
import org.eclipse.jdt.internal.core.nd.java.NdConstantArray;
import org.eclipse.jdt.internal.core.nd.java.NdConstantClass;
import org.eclipse.jdt.internal.core.nd.java.NdConstantEnum;
import org.eclipse.jdt.internal.core.nd.java.NdMethod;
import org.eclipse.jdt.internal.core.nd.java.NdMethodException;
import org.eclipse.jdt.internal.core.nd.java.NdMethodId;
import org.eclipse.jdt.internal.core.nd.java.NdMethodParameter;
import org.eclipse.jdt.internal.core.nd.java.NdResourceFile;
import org.eclipse.jdt.internal.core.nd.java.NdType;
import org.eclipse.jdt.internal.core.nd.java.NdTypeAnnotation;
import org.eclipse.jdt.internal.core.nd.java.NdTypeAnnotationInMethod;
import org.eclipse.jdt.internal.core.nd.java.NdTypeAnnotationInType;
import org.eclipse.jdt.internal.core.nd.java.NdTypeAnnotationInVariable;
import org.eclipse.jdt.internal.core.nd.java.NdTypeArgument;
import org.eclipse.jdt.internal.core.nd.java.NdTypeBound;
import org.eclipse.jdt.internal.core.nd.java.NdTypeId;
import org.eclipse.jdt.internal.core.nd.java.NdTypeInterface;
import org.eclipse.jdt.internal.core.nd.java.NdTypeParameter;
import org.eclipse.jdt.internal.core.nd.java.NdTypeSignature;
import org.eclipse.jdt.internal.core.nd.java.NdVariable;
import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeDescriptor;
import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeFactory;
import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils;
import org.eclipse.jdt.internal.core.util.CharArrayBuffer;
import org.eclipse.jdt.internal.core.util.Util;
public final class ClassFileToIndexConverter {
private static final char[] JAVA_LANG_OBJECT_FIELD_DESCRIPTOR = "Ljava/lang/Object;".toCharArray(); //$NON-NLS-1$
private static final char[] INNER_TYPE_SEPARATOR = new char[] { '$' };
private static final char[] FIELD_DESCRIPTOR_SUFFIX = new char[] { ';' };
private static final char[] COMMA = new char[]{','};
private static final char[][] EMPTY_CHAR_ARRAY_ARRAY = new char[0][];
private static final boolean ENABLE_LOGGING = false;
private static final char[] EMPTY_CHAR_ARRAY = new char[0];
private static final char[] PATH_SEPARATOR = new char[]{'/'};
private static final char[] ARRAY_FIELD_DESCRIPTOR_PREFIX = new char[] { '[' };
private NdResourceFile resource;
private JavaIndex index;
public ClassFileToIndexConverter(NdResourceFile resourceFile) {
this.resource = resourceFile;
this.index = JavaIndex.getIndex(resourceFile.getNd());
}
private Nd getNd() {
return this.resource.getNd();
}
public static IBinaryType getTypeFromClassFile(IClassFile iClassFile, IProgressMonitor monitor)
throws CoreException, ClassFormatException {
BinaryTypeDescriptor descriptor = BinaryTypeFactory.createDescriptor(iClassFile);
return BinaryTypeFactory.rawReadType(descriptor, true);
}
/**
* Create a type info from the given class file in a jar and adds it to the given list of infos.
*
* @throws CoreException
*/
protected static IBinaryType createInfoFromClassFileInJar(Openable classFile) throws CoreException {
PackageFragment pkg = (PackageFragment) classFile.getParent();
String classFilePath = Util.concatWith(pkg.names, classFile.getElementName(), '/');
IBinaryType info = null;
java.util.zip.ZipFile zipFile = null;
try {
zipFile = ((JarPackageFragmentRoot) pkg.getParent()).getJar();
info = org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader.read(zipFile, classFilePath);
} catch (Exception e) {
throw new CoreException(Package.createStatus("Unable to parse JAR file", e)); //$NON-NLS-1$
} finally {
JavaModelManager.getJavaModelManager().closeZipFile(zipFile);
}
return info;
}
/**
* Adds a type to the index, given an input class file and a binary name. Note that the given binary name is
*
* @param binaryType an object used for parsing the .class file itself
* @param fieldDescriptor the name that is used to locate the class, computed from the .class file's name and location.
* In the event that the .class file has been moved, this may differ from the binary name stored in the .class file
* itself, which is why this is received as an argument rather than extracted from the .class file.
* @throws CoreException
*/
public NdType addType(IBinaryType binaryType, char[] fieldDescriptor, IProgressMonitor monitor) throws CoreException {
char[] fieldDescriptorFromClass = JavaNames.binaryNameToFieldDescriptor(binaryType.getName());
logInfo("adding binary type " + new String(fieldDescriptor)); //$NON-NLS-1$
NdTypeId name = createTypeIdFromFieldDescriptor(fieldDescriptor);
NdType type = name.findTypeByResourceAddress(this.resource.address);
if (type == null) {
type = new NdType(getNd(), this.resource);
}
IBinaryTypeAnnotation[] typeAnnotations = binaryType.getTypeAnnotations();
if (typeAnnotations != null) {
for (IBinaryTypeAnnotation typeAnnotation : typeAnnotations) {
NdTypeAnnotationInType annotation = new NdTypeAnnotationInType(getNd(), type);
initTypeAnnotation(annotation, typeAnnotation);
}
}
type.setTypeId(name);
if (!CharArrayUtils.equals(fieldDescriptorFromClass, fieldDescriptor)) {
type.setFieldDescriptorFromClass(fieldDescriptorFromClass);
}
char[][] interfaces = binaryType.getInterfaceNames();
if (interfaces == null) {
interfaces = EMPTY_CHAR_ARRAY_ARRAY;
}
if (binaryType.getGenericSignature() != null) {
type.setFlag(NdType.FLG_GENERIC_SIGNATURE_PRESENT, true);
}
// Create the default generic signature if the .class file didn't supply one
SignatureWrapper signatureWrapper = GenericSignatures.getGenericSignature(binaryType);
type.setModifiers(binaryType.getModifiers());
type.setDeclaringType(createTypeIdFromBinaryName(binaryType.getEnclosingTypeName()));
readTypeParameters(type, signatureWrapper);
char[] superclassFieldDescriptor;
char[] superclassBinaryName = binaryType.getSuperclassName();
if (superclassBinaryName == null) {
superclassFieldDescriptor = JAVA_LANG_OBJECT_FIELD_DESCRIPTOR;
} else {
superclassFieldDescriptor = JavaNames.binaryNameToFieldDescriptor(superclassBinaryName);
}
type.setSuperclass(createTypeSignature(signatureWrapper, superclassFieldDescriptor));
short interfaceIdx = 0;
while (signatureWrapper.start < signatureWrapper.signature.length) {
// Note that there may be more interfaces listed in the generic signature than in the interfaces list.
// Although the VM spec doesn't discuss this case specifically, there are .class files in the wild with
// this characteristic. In such cases, we take what's in the generic signature and discard what's in the
// interfaces list.
char[] interfaceSpec = interfaceIdx < interfaces.length ? interfaces[interfaceIdx] : EMPTY_CHAR_ARRAY;
new NdTypeInterface(getNd(), type,
createTypeSignature(signatureWrapper, JavaNames.binaryNameToFieldDescriptor(interfaceSpec)));
interfaceIdx++;
}
IBinaryAnnotation[] annotations = binaryType.getAnnotations();
attachAnnotations(type, annotations);
type.setDeclaringMethod(createMethodId(binaryType.getEnclosingTypeName(), binaryType.getEnclosingMethod()));
IBinaryField[] fields = binaryType.getFields();
if (fields != null) {
for (IBinaryField nextField : fields) {
addField(type, nextField);
}
}
IBinaryMethod[] methods = binaryType.getMethods();
if (methods != null) {
for (IBinaryMethod next : methods) {
addMethod(type, next, binaryType);
}
}
char[][][] missingTypeNames = binaryType.getMissingTypeNames();
char[] missingTypeString = getMissingTypeString(missingTypeNames);
type.setMissingTypeNames(missingTypeString);
type.setSourceFileName(binaryType.sourceFileName());
type.setAnonymous(binaryType.isAnonymous());
type.setIsLocal(binaryType.isLocal());
type.setIsMember(binaryType.isMember());
type.setTagBits(binaryType.getTagBits());
type.setSourceNameOverride(binaryType.getSourceName());
return type;
}
private static char[] getMissingTypeString(char[][][] missingTypeNames) {
char[] missingTypeString = null;
if (missingTypeNames != null) {
CharArrayBuffer builder = new CharArrayBuffer();
for (int typeIdx = 0; typeIdx < missingTypeNames.length; typeIdx++) {
char[][] next = missingTypeNames[typeIdx];
if (typeIdx != 0) {
builder.append(COMMA);
}
if (next == null) {
continue;
}
for (int segmentIdx = 0; segmentIdx < next.length; segmentIdx++) {
char[] segment = next[segmentIdx];
if (segment == null) {
continue;
}
if (segmentIdx != 0) {
builder.append(PATH_SEPARATOR);
}
builder.append(segment);
}
}
missingTypeString = builder.getContents();
}
return missingTypeString;
}
private void attachAnnotations(NdMethod method, IBinaryAnnotation[] annotations) {
if (annotations != null) {
for (IBinaryAnnotation next : annotations) {
NdAnnotationInMethod annotation = new NdAnnotationInMethod(getNd(), method);
initAnnotation(annotation, next);
}
}
}
private void attachAnnotations(NdType type, IBinaryAnnotation[] annotations) {
if (annotations != null) {
for (IBinaryAnnotation next : annotations) {
NdAnnotationInType annotation = new NdAnnotationInType(getNd(), type);
initAnnotation(annotation, next);
}
}
}
private void attachAnnotations(NdVariable variable, IBinaryAnnotation[] annotations) {
if (annotations != null) {
for (IBinaryAnnotation next : annotations) {
NdAnnotationInVariable annotation = new NdAnnotationInVariable(getNd(), variable);
initAnnotation(annotation, next);
}
}
}
private void attachAnnotations(NdMethodParameter variable, IBinaryAnnotation[] annotations) {
if (annotations != null) {
for (IBinaryAnnotation next : annotations) {
NdAnnotationInMethodParameter annotation = new NdAnnotationInMethodParameter(getNd(), variable);
initAnnotation(annotation, next);
}
}
}
/**
* Adds the given method to the given type
*
* @throws CoreException
*/
private void addMethod(NdType type, IBinaryMethod next, IBinaryType binaryType)
throws CoreException {
int flags = 0;
NdMethod method = new NdMethod(type);
attachAnnotations(method, next.getAnnotations());
if (next.getGenericSignature() != null) {
flags |= NdMethod.FLG_GENERIC_SIGNATURE_PRESENT;
}
SignatureWrapper signature = GenericSignatures.getGenericSignature(next);
SignatureWrapper descriptor = new SignatureWrapper(next.getMethodDescriptor());
readTypeParameters(method, signature);
IBinaryTypeAnnotation[] typeAnnotations = next.getTypeAnnotations();
if (typeAnnotations != null) {
for (IBinaryTypeAnnotation typeAnnotation : typeAnnotations) {
NdTypeAnnotationInMethod annotation = new NdTypeAnnotationInMethod(getNd(), method);
initTypeAnnotation(annotation, typeAnnotation);
}
}
skipChar(signature, '(');
skipChar(descriptor, '(');
List<char[]> parameterFieldDescriptors = new ArrayList<>();
while (!descriptor.atEnd()) {
if (descriptor.charAtStart() == ')') {
skipChar(descriptor, ')');
break;
}
parameterFieldDescriptors.add(readNextFieldDescriptor(descriptor));
}
char[][] parameterNames = next.getArgumentNames();
int numArgumentsInGenericSignature = countMethodArguments(signature);
int numCompilerDefinedParameters = Math.max(0,
parameterFieldDescriptors.size() - numArgumentsInGenericSignature);
boolean compilerDefinedParametersAreIncludedInSignature = (next.getGenericSignature() == null);
// If there is no generic signature, then fall back to heuristics based on what we know about
// where the java compiler likes to create compiler-defined arguments
if (compilerDefinedParametersAreIncludedInSignature) {
// Constructors for non-static member types get a compiler-defined first argument
if (binaryType.isMember()
&& next.isConstructor()
&& ((binaryType.getModifiers() & Modifier.STATIC) == 0)
&& parameterFieldDescriptors.size() > 0) {
numCompilerDefinedParameters = 1;
} else {
numCompilerDefinedParameters = 0;
}
}
int parameterNameIdx = 0;
int annotatedParametersCount = next.getAnnotatedParametersCount();
short descriptorParameterIdx = 0;
char[] binaryTypeName = binaryType.getName();
while (!signature.atEnd()) {
if (signature.charAtStart() == ')') {
skipChar(signature, ')');
break;
}
char[] nextFieldDescriptor = parameterFieldDescriptors.get(descriptorParameterIdx);
/**
* True iff this a parameter which is part of the field descriptor but not the generic signature -- that is,
* it is a compiler-defined parameter.
*/
boolean isCompilerDefined = descriptorParameterIdx < numCompilerDefinedParameters;
SignatureWrapper nextFieldSignature = signature;
if (isCompilerDefined && !compilerDefinedParametersAreIncludedInSignature) {
nextFieldSignature = new SignatureWrapper(nextFieldDescriptor);
}
NdMethodParameter parameter = new NdMethodParameter(method,
createTypeSignature(nextFieldSignature, nextFieldDescriptor));
parameter.setCompilerDefined(isCompilerDefined);
if (descriptorParameterIdx < annotatedParametersCount) {
IBinaryAnnotation[] parameterAnnotations = next.getParameterAnnotations(descriptorParameterIdx,
binaryTypeName);
attachAnnotations(parameter, parameterAnnotations);
}
if (!isCompilerDefined && parameterNames != null && parameterNames.length > parameterNameIdx) {
parameter.setName(parameterNames[parameterNameIdx++]);
}
descriptorParameterIdx++;
}
skipChar(descriptor, ')');
char[] nextFieldDescriptor = readNextFieldDescriptor(descriptor);
method.setReturnType(createTypeSignature(signature, nextFieldDescriptor));
boolean hasExceptionsInSignature = hasAnotherException(signature);
char[][] exceptionTypes = next.getExceptionTypeNames();
if (exceptionTypes == null) {
exceptionTypes = CharArrayUtils.EMPTY_ARRAY_OF_CHAR_ARRAYS;
}
int throwsIdx = 0;
if (hasExceptionsInSignature) {
while (hasAnotherException(signature)) {
signature.start++;
new NdMethodException(method, createTypeSignature(signature,
JavaNames.binaryNameToFieldDescriptor(exceptionTypes[throwsIdx])));
throwsIdx++;
}
} else if (exceptionTypes.length != 0) {
for (;throwsIdx < exceptionTypes.length; throwsIdx++) {
char[] fieldDescriptor = JavaNames.binaryNameToFieldDescriptor(exceptionTypes[throwsIdx]);
SignatureWrapper convertedWrapper = new SignatureWrapper(fieldDescriptor);
new NdMethodException(method, createTypeSignature(convertedWrapper,
JavaNames.binaryNameToFieldDescriptor(exceptionTypes[throwsIdx])));
}
}
if (hasExceptionsInSignature) {
flags |= NdMethod.FLG_THROWS_SIGNATURE_PRESENT;
}
Object defaultValue = next.getDefaultValue();
if (defaultValue != null) {
method.setDefaultValue(createConstantFromMixedType(defaultValue));
}
method.setMethodId(createMethodId(binaryType.getName(), next.getSelector(), next.getMethodDescriptor()));
method.setModifiers(next.getModifiers());
method.setTagBits(next.getTagBits());
method.setFlags(flags);
}
private boolean hasAnotherException(SignatureWrapper signature) {
return !signature.atEnd() && signature.charAtStart() == '^';
}
private void skipChar(SignatureWrapper signature, char toSkip) {
if (signature.start < signature.signature.length && signature.charAtStart() == toSkip) {
signature.start++;
}
}
/**
* Adds the given field to the given type
*/
private void addField(NdType type, IBinaryField nextField) throws CoreException {
NdVariable variable = new NdVariable(type);
variable.setName(nextField.getName());
if (nextField.getGenericSignature() != null) {
variable.setVariableFlag(NdVariable.FLG_GENERIC_SIGNATURE_PRESENT);
}
attachAnnotations(variable, nextField.getAnnotations());
variable.setConstant(NdConstant.create(getNd(), nextField.getConstant()));
variable.setModifiers(nextField.getModifiers());
SignatureWrapper nextTypeSignature = GenericSignatures.getGenericSignatureFor(nextField);
IBinaryTypeAnnotation[] typeAnnotations = nextField.getTypeAnnotations();
if (typeAnnotations != null) {
for (IBinaryTypeAnnotation next : typeAnnotations) {
NdTypeAnnotationInVariable annotation = new NdTypeAnnotationInVariable(getNd(), variable);
initTypeAnnotation(annotation, next);
}
}
variable.setType(createTypeSignature(nextTypeSignature, nextField.getTypeName()));
variable.setTagBits(nextField.getTagBits());
// char[] fieldDescriptor = nextField.getTypeName();
// // DO NOT SUBMIT:
// IBinaryField bf = IndexBinaryType.createBinaryField(variable);
}
/**
* Reads and attaches any generic type parameters at the current start position in the given wrapper. Sets
* wrapper.start to the character following the type parameters.
*
* @throws CoreException
*/
private void readTypeParameters(NdBinding type, SignatureWrapper wrapper)
throws CoreException {
char[] genericSignature = wrapper.signature;
if (genericSignature.length == 0 || wrapper.charAtStart() != '<') {
return;
}
int indexOfClosingBracket = wrapper.skipAngleContents(wrapper.start) - 1;
wrapper.start++;
NdTypeParameter parameter = null;
while (wrapper.start < indexOfClosingBracket) {
int colonPos = CharOperation.indexOf(':', genericSignature, wrapper.start, indexOfClosingBracket);
if (colonPos > wrapper.start) {
char[] identifier = CharOperation.subarray(genericSignature, wrapper.start, colonPos);
parameter = new NdTypeParameter(type, identifier);
wrapper.start = colonPos + 1;
// The first bound is a class as long as it doesn't start with a double-colon
parameter.setFirstBoundIsClass(wrapper.charAtStart() != ':');
}
skipChar(wrapper, ':');
NdTypeSignature boundSignature = createTypeSignature(wrapper, JAVA_LANG_OBJECT_FIELD_DESCRIPTOR);
new NdTypeBound(parameter, boundSignature);
}
skipChar(wrapper, '>');
}
private char[] readNextFieldDescriptor(SignatureWrapper genericSignature) {
int endPosition = findEndOfFieldDescriptor(genericSignature);
char[] result = CharArrayUtils.subarray(genericSignature.signature, genericSignature.start, endPosition);
genericSignature.start = endPosition;
return result;
}
private int findEndOfFieldDescriptor(SignatureWrapper genericSignature) {
char[] signature = genericSignature.signature;
if (signature == null || signature.length == 0) {
return genericSignature.start;
}
int current = genericSignature.start;
while (current < signature.length) {
char firstChar = signature[current];
switch (firstChar) {
case 'L':
case 'T': {
return CharArrayUtils.indexOf(';', signature, current, signature.length) + 1;
}
case '[': {
current++;
break;
}
case 'V':
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
return current + 1;
default:
throw new IndexException(Package.createStatus("Field descriptor starts with unknown character: " //$NON-NLS-1$
+ genericSignature.toString()));
}
}
return current;
}
/**
* Given a generic signature which is positioned at the open brace for method arguments, this returns the number of
* method arguments. The start position of the given signature is not modified.
*/
private int countMethodArguments(SignatureWrapper genericSignature) {
SignatureWrapper lookaheadSignature = new SignatureWrapper(genericSignature.signature);
lookaheadSignature.start = genericSignature.start;
skipChar(lookaheadSignature, '(');
int argumentCount = 0;
while (!lookaheadSignature.atEnd() && !(lookaheadSignature.charAtStart() == ')')) {
switch (lookaheadSignature.charAtStart()) {
case 'T': {
// Skip the 'T' prefix
lookaheadSignature.nextWord();
skipChar(lookaheadSignature, ';');
argumentCount++;
break;
}
case '[': {
// Skip the '[' prefix
lookaheadSignature.start++;
break;
}
case 'V':
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
argumentCount++;
lookaheadSignature.start++;
break;
case 'L':
for (;;) {
lookaheadSignature.nextWord();
lookaheadSignature.start = lookaheadSignature.skipAngleContents(lookaheadSignature.start);
char nextChar = lookaheadSignature.charAtStart();
if (nextChar == ';') {
break;
}
if (nextChar != '.') {
throw new IllegalStateException(
"Unknown char in generic signature " + lookaheadSignature.toString()); //$NON-NLS-1$
}
}
skipChar(lookaheadSignature, ';');
argumentCount++;
break;
default:
throw new IllegalStateException("Generic signature starts with unknown character: " //$NON-NLS-1$
+ lookaheadSignature.toString());
}
}
return argumentCount;
}
/**
* Reads a type signature from the given {@link SignatureWrapper}, starting at the character pointed to by
* wrapper.start. On return, wrapper.start will point to the first character following the type signature. Returns
* null if given an empty signature or the signature for the void type.
*
* @param genericSignature
* the generic signature to parse
* @param fieldDescriptorIfVariable
* the field descriptor to use if the type is a type variable -- or null if unknown (the field descriptor
* for java.lang.Object will be used)
* @throws CoreException
*/
private NdTypeSignature createTypeSignature(SignatureWrapper genericSignature, char[] fieldDescriptorIfVariable)
throws CoreException {
char[] signature = genericSignature.signature;
if (signature == null || signature.length == 0) {
return null;
}
char firstChar = genericSignature.charAtStart();
switch (firstChar) {
case 'T': {
// Skip the 'T' prefix
genericSignature.start++;
NdComplexTypeSignature typeSignature = new NdComplexTypeSignature(getNd());
char[] fieldDescriptor = fieldDescriptorIfVariable;
if (fieldDescriptor == null) {
fieldDescriptor = JAVA_LANG_OBJECT_FIELD_DESCRIPTOR;
}
typeSignature.setRawType(createTypeIdFromFieldDescriptor(fieldDescriptor));
typeSignature.setVariableIdentifier(genericSignature.nextWord());
// Skip the trailing semicolon
skipChar(genericSignature, ';');
return typeSignature;
}
case '[': {
// Skip the '[' prefix
genericSignature.start++;
char[] nestedFieldDescriptor = null;
if (fieldDescriptorIfVariable != null && fieldDescriptorIfVariable.length > 0
&& fieldDescriptorIfVariable[0] == '[') {
nestedFieldDescriptor = CharArrayUtils.subarray(fieldDescriptorIfVariable, 1);
}
// Determine the array argument type
NdTypeSignature elementType = createTypeSignature(genericSignature, nestedFieldDescriptor);
char[] computedFieldDescriptor = CharArrayUtils.concat(ARRAY_FIELD_DESCRIPTOR_PREFIX,
elementType.getRawType().getFieldDescriptor().getChars());
NdTypeId rawType = createTypeIdFromFieldDescriptor(computedFieldDescriptor);
// We encode signatures as though they were a one-argument generic type whose element
// type is the generic argument.
NdComplexTypeSignature typeSignature = new NdComplexTypeSignature(getNd());
typeSignature.setRawType(rawType);
NdTypeArgument typeArgument = new NdTypeArgument(getNd(), typeSignature);
typeArgument.setType(elementType);
return typeSignature;
}
case 'V':
genericSignature.start++;
return null;
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
genericSignature.start++;
return createTypeIdFromFieldDescriptor(new char[] { firstChar });
case 'L':
return parseClassTypeSignature(null, genericSignature);
case '+':
case '-':
case '*':
throw new CoreException(Package.createStatus("Unexpected wildcard in top-level of generic signature: " //$NON-NLS-1$
+ genericSignature.toString()));
default:
throw new CoreException(Package.createStatus("Generic signature starts with unknown character: " //$NON-NLS-1$
+ genericSignature.toString()));
}
}
/**
* Parses a ClassTypeSignature (as described in section 4.7.9.1 of the Java VM Specification Java SE 8 Edition). The
* read pointer should be located just after the identifier. The caller is expected to have already read the field
* descriptor for the type.
*/
private NdTypeSignature parseClassTypeSignature(NdComplexTypeSignature parentTypeOrNull, SignatureWrapper wrapper)
throws CoreException {
char[] identifier = wrapper.nextWord();
char[] fieldDescriptor;
if (parentTypeOrNull != null) {
fieldDescriptor = CharArrayUtils.concat(
parentTypeOrNull.getRawType().getFieldDescriptorWithoutTrailingSemicolon(),
INNER_TYPE_SEPARATOR, identifier, FIELD_DESCRIPTOR_SUFFIX);
} else {
fieldDescriptor = CharArrayUtils.concat(identifier, FIELD_DESCRIPTOR_SUFFIX);
}
char[] genericSignature = wrapper.signature;
boolean hasGenericArguments = (genericSignature.length > wrapper.start)
&& genericSignature[wrapper.start] == '<';
boolean isRawTypeWithNestedClass = genericSignature[wrapper.start] == '.';
NdTypeId rawType = createTypeIdFromFieldDescriptor(fieldDescriptor);
NdTypeSignature result = rawType;
boolean checkForSemicolon = true;
// Special optimization for signatures with no type annotations, no arrays, and no generic arguments that
// are not an inner type of a class that can't use this optimization. Basically, if there would be no attributes
// set on a NdComplexTypeSignature besides what it picks up from its raw type, we just use the raw type.
if (hasGenericArguments || parentTypeOrNull != null || isRawTypeWithNestedClass) {
NdComplexTypeSignature typeSignature = new NdComplexTypeSignature(getNd());
typeSignature.setRawType(rawType);
if (hasGenericArguments) {
wrapper.start++;
while (wrapper.start < genericSignature.length && (genericSignature[wrapper.start] != '>')) {
NdTypeArgument typeArgument = new NdTypeArgument(getNd(), typeSignature);
switch (genericSignature[wrapper.start]) {
case '+': {
typeArgument.setWildcard(NdTypeArgument.WILDCARD_SUPER);
wrapper.start++;
break;
}
case '-': {
typeArgument.setWildcard(NdTypeArgument.WILDCARD_EXTENDS);
wrapper.start++;
break;
}
case '*': {
typeArgument.setWildcard(NdTypeArgument.WILDCARD_QUESTION);
wrapper.start++;
continue;
}
}
NdTypeSignature nextSignature = createTypeSignature(wrapper, null);
typeArgument.setType(nextSignature);
}
skipChar(wrapper, '>');
}
result = typeSignature;
if (parentTypeOrNull != null) {
typeSignature.setGenericDeclaringType(parentTypeOrNull);
}
if (genericSignature[wrapper.start] == '.') {
// Don't check for a semicolon if we hit this branch since the recursive call to parseClassTypeSignature
// will do this
checkForSemicolon = false;
// Identifiers shouldn't start with '.'
skipChar(wrapper, '.');
result = parseClassTypeSignature(typeSignature, wrapper);
}
}
if (checkForSemicolon) {
skipChar(wrapper, ';');
}
return result;
}
private NdTypeId createTypeIdFromFieldDescriptor(char[] typeName) {
if (typeName == null) {
return null;
}
return this.index.createTypeId(typeName);
}
/**
* Creates a method ID given a method selector, method descriptor, and binary type name
*/
private NdMethodId createMethodId(char[] binaryTypeName, char[] methodSelector, char[] methodDescriptor) {
if (methodSelector == null || binaryTypeName == null || methodDescriptor == null) {
return null;
}
char[] methodId = JavaNames.getMethodId(binaryTypeName, methodSelector, methodDescriptor);
return this.index.createMethodId(methodId);
}
/**
* Creates a method ID given a method name (which is a method selector followed by a method descriptor.
*/
private NdMethodId createMethodId(char[] binaryTypeName, char[] methodName) {
if (methodName == null || binaryTypeName == null) {
return null;
}
char[] methodId = JavaNames.getMethodId(binaryTypeName, methodName);
return this.index.createMethodId(methodId);
}
private void initTypeAnnotation(NdTypeAnnotation annotation, IBinaryTypeAnnotation next) {
int[] typePath = next.getTypePath();
if (typePath != null && typePath.length > 0) {
byte[] bytePath = new byte[typePath.length];
for (int idx = 0; idx < bytePath.length; idx++) {
bytePath[idx] = (byte)typePath[idx];
}
annotation.setPath(bytePath);
}
int targetType = next.getTargetType();
annotation.setTargetType(targetType);
switch (targetType) {
case AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER:
case AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER:
annotation.setTargetInfo(next.getTypeParameterIndex());
break;
case AnnotationTargetTypeConstants.CLASS_EXTENDS:
annotation.setTargetInfo(next.getSupertypeIndex());
break;
case AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER_BOUND:
case AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER_BOUND:
annotation.setTargetInfo((byte)next.getTypeParameterIndex(), (byte)next.getBoundIndex());
break;
case AnnotationTargetTypeConstants.FIELD:
case AnnotationTargetTypeConstants.METHOD_RETURN:
case AnnotationTargetTypeConstants.METHOD_RECEIVER:
break;
case AnnotationTargetTypeConstants.METHOD_FORMAL_PARAMETER :
annotation.setTargetInfo(next.getMethodFormalParameterIndex());
break;
case AnnotationTargetTypeConstants.THROWS :
annotation.setTargetInfo(next.getThrowsTypeIndex());
break;
default:
throw new IllegalStateException("Target type not handled " + targetType); //$NON-NLS-1$
}
initAnnotation(annotation, next.getAnnotation());
}
private void initAnnotation(NdAnnotation annotation, IBinaryAnnotation next) {
annotation.setType(createTypeIdFromBinaryName(next.getTypeName()));
IBinaryElementValuePair[] pairs = next.getElementValuePairs();
if (pairs != null) {
for (IBinaryElementValuePair element : pairs) {
NdAnnotationValuePair nextPair = new NdAnnotationValuePair(annotation, element.getName());
nextPair.setValue(createConstantFromMixedType(element.getValue()));
}
}
}
private void logInfo(String string) {
if (ENABLE_LOGGING) {
Package.logInfo(string);
}
}
private NdTypeId createTypeIdFromBinaryName(char[] binaryName) {
if (binaryName == null) {
return null;
}
return this.index.createTypeId(JavaNames.binaryNameToFieldDescriptor(binaryName));
}
/**
*
* @param value
* accepts all values returned from {@link IBinaryElementValuePair#getValue()}
*/
public NdConstant createConstantFromMixedType(Object value) {
if (value instanceof Constant) {
Constant constant = (Constant) value;
return NdConstant.create(getNd(), constant);
} else if (value instanceof ClassSignature) {
ClassSignature signature = (ClassSignature) value;
char[] binaryName = JavaNames.binaryNameToFieldDescriptor(signature.getTypeName());
NdTypeSignature typeId = this.index.createTypeId(binaryName);
return NdConstantClass.create(getNd(), typeId);
} else if (value instanceof IBinaryAnnotation) {
IBinaryAnnotation binaryAnnotation = (IBinaryAnnotation) value;
NdAnnotationInConstant annotation = new NdAnnotationInConstant(getNd());
initAnnotation(annotation, binaryAnnotation);
return NdConstantAnnotation.create(getNd(), annotation);
} else if (value instanceof Object[]) {
NdConstantArray result = new NdConstantArray(getNd());
Object[] array = (Object[]) value;
for (Object next : array) {
NdConstant nextConstant = createConstantFromMixedType(next);
nextConstant.setParent(result);
}
return result;
} else if (value instanceof EnumConstantSignature) {
EnumConstantSignature signature = (EnumConstantSignature) value;
NdConstantEnum result = NdConstantEnum.create(createTypeIdFromBinaryName(signature.getTypeName()),
new String(signature.getEnumConstantName()));
return result;
}
throw new IllegalStateException("Unknown constant type " + value.getClass().getName()); //$NON-NLS-1$
}
}