/******************************************************************************* * 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.compiler.CharOperation; 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.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(); } /** * 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$ } }