| /******************************************************************************* |
| * 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$ |
| } |
| } |