/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core.search.indexing;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.search.SearchDocument;
import org.eclipse.jdt.internal.compiler.ExtraFlags;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.compiler.classfmt.FieldInfo;
import org.eclipse.jdt.internal.compiler.classfmt.MethodInfo;
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.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import org.eclipse.jdt.internal.core.util.Util;
public class BinaryIndexer extends AbstractIndexer implements SuffixConstants {
private static final char[] BYTE= "byte".toCharArray(); //$NON-NLS-1$
private static final char[] CHAR= "char".toCharArray(); //$NON-NLS-1$
private static final char[] DOUBLE= "double".toCharArray(); //$NON-NLS-1$
private static final char[] FLOAT= "float".toCharArray(); //$NON-NLS-1$
private static final char[] INT= "int".toCharArray(); //$NON-NLS-1$
private static final char[] LONG= "long".toCharArray(); //$NON-NLS-1$
private static final char[] SHORT= "short".toCharArray(); //$NON-NLS-1$
private static final char[] BOOLEAN= "boolean".toCharArray(); //$NON-NLS-1$
private static final char[] VOID= "void".toCharArray(); //$NON-NLS-1$
private static final char[] INIT= "<init>".toCharArray(); //$NON-NLS-1$
public BinaryIndexer(SearchDocument document) {
super(document);
}
private void addBinaryStandardAnnotations(long annotationTagBits) {
if ((annotationTagBits & TagBits.AnnotationTargetMASK) != 0) {
char[][] compoundName= TypeConstants.JAVA_LANG_ANNOTATION_TARGET;
addAnnotationTypeReference(compoundName[compoundName.length - 1]);
addBinaryTargetAnnotation(annotationTagBits);
}
if ((annotationTagBits & TagBits.AnnotationRetentionMASK) != 0) {
char[][] compoundName= TypeConstants.JAVA_LANG_ANNOTATION_RETENTION;
addAnnotationTypeReference(compoundName[compoundName.length - 1]);
addBinaryRetentionAnnotation(annotationTagBits);
}
if ((annotationTagBits & TagBits.AnnotationDeprecated) != 0) {
char[][] compoundName= TypeConstants.JAVA_LANG_DEPRECATED;
addAnnotationTypeReference(compoundName[compoundName.length - 1]);
}
if ((annotationTagBits & TagBits.AnnotationDocumented) != 0) {
char[][] compoundName= TypeConstants.JAVA_LANG_ANNOTATION_DOCUMENTED;
addAnnotationTypeReference(compoundName[compoundName.length - 1]);
}
if ((annotationTagBits & TagBits.AnnotationInherited) != 0) {
char[][] compoundName= TypeConstants.JAVA_LANG_ANNOTATION_INHERITED;
addAnnotationTypeReference(compoundName[compoundName.length - 1]);
}
if ((annotationTagBits & TagBits.AnnotationOverride) != 0) {
char[][] compoundName= TypeConstants.JAVA_LANG_OVERRIDE;
addAnnotationTypeReference(compoundName[compoundName.length - 1]);
}
if ((annotationTagBits & TagBits.AnnotationSuppressWarnings) != 0) {
char[][] compoundName= TypeConstants.JAVA_LANG_SUPPRESSWARNINGS;
addAnnotationTypeReference(compoundName[compoundName.length - 1]);
}
}
private void addBinaryTargetAnnotation(long bits) {
char[][] compoundName= null;
if ((bits & TagBits.AnnotationForAnnotationType) != 0) {
compoundName= TypeConstants.JAVA_LANG_ANNOTATION_ELEMENTTYPE;
addTypeReference(compoundName[compoundName.length - 1]);
addFieldReference(TypeConstants.UPPER_ANNOTATION_TYPE);
}
if ((bits & TagBits.AnnotationForConstructor) != 0) {
if (compoundName == null) {
compoundName= TypeConstants.JAVA_LANG_ANNOTATION_ELEMENTTYPE;
addTypeReference(compoundName[compoundName.length - 1]);
}
addFieldReference(TypeConstants.UPPER_CONSTRUCTOR);
}
if ((bits & TagBits.AnnotationForField) != 0) {
if (compoundName == null) {
compoundName= TypeConstants.JAVA_LANG_ANNOTATION_ELEMENTTYPE;
addTypeReference(compoundName[compoundName.length - 1]);
}
addFieldReference(TypeConstants.UPPER_FIELD);
}
if ((bits & TagBits.AnnotationForLocalVariable) != 0) {
if (compoundName == null) {
compoundName= TypeConstants.JAVA_LANG_ANNOTATION_ELEMENTTYPE;
addTypeReference(compoundName[compoundName.length - 1]);
}
addFieldReference(TypeConstants.UPPER_LOCAL_VARIABLE);
}
if ((bits & TagBits.AnnotationForMethod) != 0) {
if (compoundName == null) {
compoundName= TypeConstants.JAVA_LANG_ANNOTATION_ELEMENTTYPE;
addTypeReference(compoundName[compoundName.length - 1]);
}
addFieldReference(TypeConstants.UPPER_METHOD);
}
if ((bits & TagBits.AnnotationForPackage) != 0) {
if (compoundName == null) {
compoundName= TypeConstants.JAVA_LANG_ANNOTATION_ELEMENTTYPE;
addTypeReference(compoundName[compoundName.length - 1]);
}
addFieldReference(TypeConstants.UPPER_PACKAGE);
}
if ((bits & TagBits.AnnotationForParameter) != 0) {
if (compoundName == null) {
compoundName= TypeConstants.JAVA_LANG_ANNOTATION_ELEMENTTYPE;
addTypeReference(compoundName[compoundName.length - 1]);
}
addFieldReference(TypeConstants.UPPER_PARAMETER);
}
if ((bits & TagBits.AnnotationForType) != 0) {
if (compoundName == null) {
compoundName= TypeConstants.JAVA_LANG_ANNOTATION_ELEMENTTYPE;
addTypeReference(compoundName[compoundName.length - 1]);
}
addFieldReference(TypeConstants.TYPE);
}
}
private void addBinaryRetentionAnnotation(long bits) {
char[][] compoundName= TypeConstants.JAVA_LANG_ANNOTATION_RETENTIONPOLICY;
addTypeReference(compoundName[compoundName.length - 1]);
if ((bits & TagBits.AnnotationRuntimeRetention) == TagBits.AnnotationRuntimeRetention) {
addFieldReference(TypeConstants.UPPER_RUNTIME);
} else if ((bits & TagBits.AnnotationClassRetention) != 0) {
addFieldReference(TypeConstants.UPPER_CLASS);
} else if ((bits & TagBits.AnnotationSourceRetention) != 0) {
addFieldReference(TypeConstants.UPPER_SOURCE);
}
}
private void addBinaryAnnotation(IBinaryAnnotation annotation) {
addAnnotationTypeReference(replace('/', '.', Signature.toCharArray(annotation.getTypeName())));
IBinaryElementValuePair[] valuePairs= annotation.getElementValuePairs();
if (valuePairs != null) {
for (int j= 0, vpLength= valuePairs.length; j < vpLength; j++) {
IBinaryElementValuePair valuePair= valuePairs[j];
addMethodReference(valuePair.getName(), 0);
Object pairValue= valuePair.getValue();
addPairValue(pairValue);
}
}
}
private void addPairValue(Object pairValue) {
if (pairValue instanceof EnumConstantSignature) {
EnumConstantSignature enumConstant= (EnumConstantSignature)pairValue;
addTypeReference(replace('/', '.', Signature.toCharArray(enumConstant.getTypeName())));
addNameReference(enumConstant.getEnumConstantName());
} else if (pairValue instanceof ClassSignature) {
ClassSignature classConstant= (ClassSignature)pairValue;
addTypeReference(replace('/', '.', Signature.toCharArray(classConstant.getTypeName())));
} else if (pairValue instanceof IBinaryAnnotation) {
addBinaryAnnotation((IBinaryAnnotation)pairValue);
} else if (pairValue instanceof Object[]) {
Object[] objects= (Object[])pairValue;
for (int i= 0, l= objects.length; i < l; i++) {
addPairValue(objects[i]);
}
}
}
public void addTypeReference(char[] typeName) {
int length= typeName.length;
if (length > 2 && typeName[length - 2] == '$') {
switch (typeName[length - 1]) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return; // skip local type names
}
}
// consider that A$B is a member type: so replace '$' with '.'
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=40116)
typeName= CharOperation.replaceOnCopy(typeName, '$', '.'); // copy it so the original is not modified
super.addTypeReference(typeName);
}
/**
* For example: - int foo(String[]) is ([Ljava/lang/String;)I => java.lang.String[] in a
* char[][] - void foo(int) is (I)V ==> int
*/
private void convertToArrayType(char[][] parameterTypes, int counter, int arrayDim) {
int length= parameterTypes[counter].length;
char[] arrayType= new char[length + arrayDim * 2];
System.arraycopy(parameterTypes[counter], 0, arrayType, 0, length);
for (int i= 0; i < arrayDim; i++) {
arrayType[length + (i * 2)]= '[';
arrayType[length + (i * 2) + 1]= ']';
}
parameterTypes[counter]= arrayType;
}
/**
* For example: - int foo(String[]) is ([Ljava/lang/String;)I => java.lang.String[] in a
* char[][] - void foo(int) is (I)V ==> int
*/
private char[] convertToArrayType(char[] typeName, int arrayDim) {
int length= typeName.length;
char[] arrayType= new char[length + arrayDim * 2];
System.arraycopy(typeName, 0, arrayType, 0, length);
for (int i= 0; i < arrayDim; i++) {
arrayType[length + (i * 2)]= '[';
arrayType[length + (i * 2) + 1]= ']';
}
return arrayType;
}
private char[] decodeFieldType(char[] signature) throws ClassFormatException {
if (signature == null)
return null;
int arrayDim= 0;
for (int i= 0, max= signature.length; i < max; i++) {
switch (signature[i]) {
case 'B':
if (arrayDim > 0)
return convertToArrayType(BYTE, arrayDim);
return BYTE;
case 'C':
if (arrayDim > 0)
return convertToArrayType(CHAR, arrayDim);
return CHAR;
case 'D':
if (arrayDim > 0)
return convertToArrayType(DOUBLE, arrayDim);
return DOUBLE;
case 'F':
if (arrayDim > 0)
return convertToArrayType(FLOAT, arrayDim);
return FLOAT;
case 'I':
if (arrayDim > 0)
return convertToArrayType(INT, arrayDim);
return INT;
case 'J':
if (arrayDim > 0)
return convertToArrayType(LONG, arrayDim);
return LONG;
case 'L':
int indexOfSemiColon= CharOperation.indexOf(';', signature, i + 1);
if (indexOfSemiColon == -1)
throw new ClassFormatException(ClassFormatException.ErrInvalidMethodSignature);
if (arrayDim > 0) {
return convertToArrayType(replace('/', '.', CharOperation.subarray(signature, i + 1, indexOfSemiColon)), arrayDim);
}
return replace('/', '.', CharOperation.subarray(signature, i + 1, indexOfSemiColon));
case 'S':
if (arrayDim > 0)
return convertToArrayType(SHORT, arrayDim);
return SHORT;
case 'Z':
if (arrayDim > 0)
return convertToArrayType(BOOLEAN, arrayDim);
return BOOLEAN;
case 'V':
return VOID;
case '[':
arrayDim++;
break;
default:
throw new ClassFormatException(ClassFormatException.ErrInvalidMethodSignature);
}
}
return null;
}
/**
* For example: - int foo(String[]) is ([Ljava/lang/String;)I => java.lang.String[] in a
* char[][] - void foo(int) is (I)V ==> int
*/
private char[][] decodeParameterTypes(char[] signature, boolean firstIsSynthetic) throws ClassFormatException {
if (signature == null)
return null;
int indexOfClosingParen= CharOperation.lastIndexOf(')', signature);
if (indexOfClosingParen == 1) {
// there is no parameter
return null;
}
if (indexOfClosingParen == -1) {
throw new ClassFormatException(ClassFormatException.ErrInvalidMethodSignature);
}
char[][] parameterTypes= new char[3][];
int parameterTypesCounter= 0;
int arrayDim= 0;
for (int i= 1; i < indexOfClosingParen; i++) {
if (parameterTypesCounter == parameterTypes.length) {
// resize
System.arraycopy(parameterTypes, 0, (parameterTypes= new char[parameterTypesCounter * 2][]), 0, parameterTypesCounter);
}
switch (signature[i]) {
case 'B':
parameterTypes[parameterTypesCounter++]= BYTE;
if (arrayDim > 0)
convertToArrayType(parameterTypes, parameterTypesCounter - 1, arrayDim);
arrayDim= 0;
break;
case 'C':
parameterTypes[parameterTypesCounter++]= CHAR;
if (arrayDim > 0)
convertToArrayType(parameterTypes, parameterTypesCounter - 1, arrayDim);
arrayDim= 0;
break;
case 'D':
parameterTypes[parameterTypesCounter++]= DOUBLE;
if (arrayDim > 0)
convertToArrayType(parameterTypes, parameterTypesCounter - 1, arrayDim);
arrayDim= 0;
break;
case 'F':
parameterTypes[parameterTypesCounter++]= FLOAT;
if (arrayDim > 0)
convertToArrayType(parameterTypes, parameterTypesCounter - 1, arrayDim);
arrayDim= 0;
break;
case 'I':
parameterTypes[parameterTypesCounter++]= INT;
if (arrayDim > 0)
convertToArrayType(parameterTypes, parameterTypesCounter - 1, arrayDim);
arrayDim= 0;
break;
case 'J':
parameterTypes[parameterTypesCounter++]= LONG;
if (arrayDim > 0)
convertToArrayType(parameterTypes, parameterTypesCounter - 1, arrayDim);
arrayDim= 0;
break;
case 'L':
int indexOfSemiColon= CharOperation.indexOf(';', signature, i + 1);
if (indexOfSemiColon == -1)
throw new ClassFormatException(ClassFormatException.ErrInvalidMethodSignature);
if (firstIsSynthetic && parameterTypesCounter == 0) {
// skip first synthetic parameter
firstIsSynthetic= false;
} else {
parameterTypes[parameterTypesCounter++]= replace('/', '.', CharOperation.subarray(signature, i + 1, indexOfSemiColon));
if (arrayDim > 0)
convertToArrayType(parameterTypes, parameterTypesCounter - 1, arrayDim);
}
i= indexOfSemiColon;
arrayDim= 0;
break;
case 'S':
parameterTypes[parameterTypesCounter++]= SHORT;
if (arrayDim > 0)
convertToArrayType(parameterTypes, parameterTypesCounter - 1, arrayDim);
arrayDim= 0;
break;
case 'Z':
parameterTypes[parameterTypesCounter++]= BOOLEAN;
if (arrayDim > 0)
convertToArrayType(parameterTypes, parameterTypesCounter - 1, arrayDim);
arrayDim= 0;
break;
case '[':
arrayDim++;
break;
default:
throw new ClassFormatException(ClassFormatException.ErrInvalidMethodSignature);
}
}
if (parameterTypes.length != parameterTypesCounter) {
System.arraycopy(parameterTypes, 0, parameterTypes= new char[parameterTypesCounter][], 0, parameterTypesCounter);
}
return parameterTypes;
}
private char[] decodeReturnType(char[] signature) throws ClassFormatException {
if (signature == null)
return null;
int indexOfClosingParen= CharOperation.lastIndexOf(')', signature);
if (indexOfClosingParen == -1)
throw new ClassFormatException(ClassFormatException.ErrInvalidMethodSignature);
int arrayDim= 0;
for (int i= indexOfClosingParen + 1, max= signature.length; i < max; i++) {
switch (signature[i]) {
case 'B':
if (arrayDim > 0)
return convertToArrayType(BYTE, arrayDim);
return BYTE;
case 'C':
if (arrayDim > 0)
return convertToArrayType(CHAR, arrayDim);
return CHAR;
case 'D':
if (arrayDim > 0)
return convertToArrayType(DOUBLE, arrayDim);
return DOUBLE;
case 'F':
if (arrayDim > 0)
return convertToArrayType(FLOAT, arrayDim);
return FLOAT;
case 'I':
if (arrayDim > 0)
return convertToArrayType(INT, arrayDim);
return INT;
case 'J':
if (arrayDim > 0)
return convertToArrayType(LONG, arrayDim);
return LONG;
case 'L':
int indexOfSemiColon= CharOperation.indexOf(';', signature, i + 1);
if (indexOfSemiColon == -1)
throw new ClassFormatException(ClassFormatException.ErrInvalidMethodSignature);
if (arrayDim > 0) {
return convertToArrayType(replace('/', '.', CharOperation.subarray(signature, i + 1, indexOfSemiColon)), arrayDim);
}
return replace('/', '.', CharOperation.subarray(signature, i + 1, indexOfSemiColon));
case 'S':
if (arrayDim > 0)
return convertToArrayType(SHORT, arrayDim);
return SHORT;
case 'Z':
if (arrayDim > 0)
return convertToArrayType(BOOLEAN, arrayDim);
return BOOLEAN;
case 'V':
return VOID;
case '[':
arrayDim++;
break;
default:
throw new ClassFormatException(ClassFormatException.ErrInvalidMethodSignature);
}
}
return null;
}
private int extractArgCount(char[] signature, char[] className) throws ClassFormatException {
int indexOfClosingParen= CharOperation.lastIndexOf(')', signature);
if (indexOfClosingParen == 1) {
// there is no parameter
return 0;
}
if (indexOfClosingParen == -1) {
throw new ClassFormatException(ClassFormatException.ErrInvalidMethodSignature);
}
int parameterTypesCounter= 0;
for (int i= 1; i < indexOfClosingParen; i++) {
switch (signature[i]) {
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
parameterTypesCounter++;
break;
case 'L':
int indexOfSemiColon= CharOperation.indexOf(';', signature, i + 1);
if (indexOfSemiColon == -1)
throw new ClassFormatException(ClassFormatException.ErrInvalidMethodSignature);
// verify if first parameter is synthetic
if (className != null && parameterTypesCounter == 0) {
char[] classSignature= Signature.createCharArrayTypeSignature(className, true);
int length= indexOfSemiColon - i + 1;
if (classSignature.length > (length + 1)) {
// synthetic means that parameter type has same signature than given class
for (int j= i, k= 0; j < indexOfSemiColon; j++, k++) {
if (!(signature[j] == classSignature[k] || (signature[j] == '/' && classSignature[k] == '.'))) {
parameterTypesCounter++;
break;
}
}
} else {
parameterTypesCounter++;
}
className= null; // do not verify following parameters
} else {
parameterTypesCounter++;
}
i= indexOfSemiColon;
break;
case '[':
break;
default:
throw new ClassFormatException(ClassFormatException.ErrInvalidMethodSignature);
}
}
return parameterTypesCounter;
}
private char[] extractClassName(int[] constantPoolOffsets, ClassFileReader reader, int index) {
// the entry at i has to be a field ref or a method/interface method ref.
int class_index= reader.u2At(constantPoolOffsets[index] + 1);
int utf8Offset= constantPoolOffsets[reader.u2At(constantPoolOffsets[class_index] + 1)];
return reader.utf8At(utf8Offset + 3, reader.u2At(utf8Offset + 1));
}
private char[] extractName(int[] constantPoolOffsets, ClassFileReader reader, int index) {
int nameAndTypeIndex= reader.u2At(constantPoolOffsets[index] + 3);
int utf8Offset= constantPoolOffsets[reader.u2At(constantPoolOffsets[nameAndTypeIndex] + 1)];
return reader.utf8At(utf8Offset + 3, reader.u2At(utf8Offset + 1));
}
private char[] extractClassReference(int[] constantPoolOffsets, ClassFileReader reader, int index) {
// the entry at i has to be a class ref.
int utf8Offset= constantPoolOffsets[reader.u2At(constantPoolOffsets[index] + 1)];
return reader.utf8At(utf8Offset + 3, reader.u2At(utf8Offset + 1));
}
/**
* Extract all type, method, field and interface method references from the constant pool
*/
private void extractReferenceFromConstantPool(byte[] contents, ClassFileReader reader) throws ClassFormatException {
int[] constantPoolOffsets= reader.getConstantPoolOffsets();
int constantPoolCount= constantPoolOffsets.length;
for (int i= 1; i < constantPoolCount; i++) {
int tag= reader.u1At(constantPoolOffsets[i]);
/**
* u1 tag u2 class_index u2 name_and_type_index
*/
char[] name= null;
char[] type= null;
switch (tag) {
case ClassFileConstants.FieldRefTag:
// add reference to the class/interface and field name and type
name= extractName(constantPoolOffsets, reader, i);
addFieldReference(name);
break;
case ClassFileConstants.MethodRefTag:
// add reference to the class and method name and type
case ClassFileConstants.InterfaceMethodRefTag:
// add reference to the interface and method name and type
name= extractName(constantPoolOffsets, reader, i);
type= extractType(constantPoolOffsets, reader, i);
if (CharOperation.equals(INIT, name)) {
// get class name and see if it's a local type or not
char[] className= extractClassName(constantPoolOffsets, reader, i);
boolean localType= false;
if (className != null) {
for (int c= 0, max= className.length; c < max; c++) {
switch (className[c]) {
case '/':
className[c]= '.';
break;
case '$':
localType= true;
break;
}
}
}
// add a constructor reference, use class name to extract arg count if it's a local type to remove synthetic parameter
addConstructorReference(className, extractArgCount(type, localType ? className : null));
} else {
// add a method reference
addMethodReference(name, extractArgCount(type, null));
}
break;
case ClassFileConstants.ClassTag:
// add a type reference
name= extractClassReference(constantPoolOffsets, reader, i);
if (name.length > 0 && name[0] == '[')
break; // skip over array references
name= replace('/', '.', name); // so that it looks like java.lang.String
addTypeReference(name);
// also add a simple reference on each segment of the qualification (see http://bugs.eclipse.org/bugs/show_bug.cgi?id=24741)
char[][] qualification= CharOperation.splitOn('.', name);
for (int j= 0, length= qualification.length; j < length; j++) {
addNameReference(qualification[j]);
}
break;
}
}
}
private char[] extractType(int[] constantPoolOffsets, ClassFileReader reader, int index) {
int constantPoolIndex= reader.u2At(constantPoolOffsets[index] + 3);
int utf8Offset= constantPoolOffsets[reader.u2At(constantPoolOffsets[constantPoolIndex] + 3)];
return reader.utf8At(utf8Offset + 3, reader.u2At(utf8Offset + 1));
}
public void indexDocument() {
try {
final byte[] contents= this.document.getByteContents();
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=107124
// contents can potentially be null if a IOException occurs while retrieving the contents
if (contents == null)
return;
final String path= this.document.getPath();
ClassFileReader reader= new ClassFileReader(contents, path == null ? null : path.toCharArray());
// first add type references
char[] className= replace('/', '.', reader.getName()); // looks like java/lang/String
// need to extract the package name and the simple name
int packageNameIndex= CharOperation.lastIndexOf('.', className);
char[] packageName= null;
char[] name= null;
if (packageNameIndex >= 0) {
packageName= CharOperation.subarray(className, 0, packageNameIndex);
name= CharOperation.subarray(className, packageNameIndex + 1, className.length);
} else {
packageName= CharOperation.NO_CHAR;
name= className;
}
char[] enclosingTypeName= null;
boolean isNestedType= reader.isNestedType();
if (isNestedType) {
if (reader.isAnonymous()) {
name= CharOperation.NO_CHAR;
} else {
name= reader.getInnerSourceName();
}
if (reader.isLocal() || reader.isAnonymous()) {
// set specific ['0'] value for local and anonymous to be able to filter them
enclosingTypeName= ONE_ZERO;
} else {
char[] fullEnclosingName= reader.getEnclosingTypeName();
int nameLength= fullEnclosingName.length - packageNameIndex - 1;
if (nameLength <= 0) {
// See PR 1GIR345: ITPJCORE:ALL - Indexer: NegativeArraySizeException
return;
}
enclosingTypeName= new char[nameLength];
System.arraycopy(fullEnclosingName, packageNameIndex + 1, enclosingTypeName, 0, nameLength);
}
}
// type parameters
char[][] typeParameterSignatures= null;
char[] genericSignature= reader.getGenericSignature();
if (genericSignature != null) {
CharOperation.replace(genericSignature, '/', '.');
typeParameterSignatures= Signature.getTypeParameters(genericSignature);
}
// eliminate invalid innerclasses (1G4KCF7)
if (name == null)
return;
char[][] superinterfaces= replace('/', '.', reader.getInterfaceNames());
char[][] enclosingTypeNames= enclosingTypeName == null ? null : new char[][] { enclosingTypeName };
int modifiers= reader.getModifiers();
switch (TypeDeclaration.kind(modifiers)) {
case TypeDeclaration.CLASS_DECL:
char[] superclass= replace('/', '.', reader.getSuperclassName());
addClassDeclaration(modifiers, packageName, name, enclosingTypeNames, superclass, superinterfaces, typeParameterSignatures, false);
break;
case TypeDeclaration.INTERFACE_DECL:
addInterfaceDeclaration(modifiers, packageName, name, enclosingTypeNames, superinterfaces, typeParameterSignatures, false);
break;
case TypeDeclaration.ENUM_DECL:
superclass= replace('/', '.', reader.getSuperclassName());
addEnumDeclaration(modifiers, packageName, name, enclosingTypeNames, superclass, superinterfaces, false);
break;
case TypeDeclaration.ANNOTATION_TYPE_DECL:
addAnnotationTypeDeclaration(modifiers, packageName, name, enclosingTypeNames, false);
break;
}
// Look for references in class annotations
IBinaryAnnotation[] annotations= reader.getAnnotations();
if (annotations != null) {
for (int a= 0, length= annotations.length; a < length; a++) {
IBinaryAnnotation annotation= annotations[a];
addBinaryAnnotation(annotation);
}
}
long tagBits= reader.getTagBits() & TagBits.AllStandardAnnotationsMask;
if (tagBits != 0) {
addBinaryStandardAnnotations(tagBits);
}
int extraFlags= ExtraFlags.getExtraFlags(reader);
// first reference all methods declarations and field declarations
MethodInfo[] methods= (MethodInfo[])reader.getMethods();
boolean noConstructor= true;
if (methods != null) {
for (int i= 0, max= methods.length; i < max; i++) {
MethodInfo method= methods[i];
boolean isConstructor= method.isConstructor();
char[] descriptor= method.getMethodDescriptor();
char[][] parameterTypes= decodeParameterTypes(descriptor, isConstructor && isNestedType);
char[] returnType= decodeReturnType(descriptor);
char[][] exceptionTypes= replace('/', '.', method.getExceptionTypeNames());
if (isConstructor) {
noConstructor= false;
char[] signature= method.getGenericSignature();
if (signature == null) {
if (reader.isNestedType() && ((modifiers & ClassFileConstants.AccStatic) == 0)) {
signature= removeFirstSyntheticParameter(descriptor);
} else {
signature= descriptor;
}
}
addConstructorDeclaration(
name,
parameterTypes == null ? 0 : parameterTypes.length,
signature,
parameterTypes,
method.getArgumentNames(),
method.getModifiers(),
packageName,
modifiers,
exceptionTypes,
extraFlags);
} else {
if (!method.isClinit()) {
addMethodDeclaration(method.getSelector(), parameterTypes, returnType, exceptionTypes);
}
}
// look for references in method annotations
annotations= method.getAnnotations();
if (annotations != null) {
for (int a= 0, length= annotations.length; a < length; a++) {
IBinaryAnnotation annotation= annotations[a];
addBinaryAnnotation(annotation);
}
}
tagBits= method.getTagBits() & TagBits.AllStandardAnnotationsMask;
if (tagBits != 0) {
addBinaryStandardAnnotations(tagBits);
}
}
}
if (noConstructor) {
addDefaultConstructorDeclaration(className, packageName, modifiers, extraFlags);
}
FieldInfo[] fields= (FieldInfo[])reader.getFields();
if (fields != null) {
for (int i= 0, max= fields.length; i < max; i++) {
FieldInfo field= fields[i];
char[] fieldName= field.getName();
char[] fieldType= decodeFieldType(replace('/', '.', field.getTypeName()));
addFieldDeclaration(fieldType, fieldName);
// look for references in field annotations
annotations= field.getAnnotations();
if (annotations != null) {
for (int a= 0, length= annotations.length; a < length; a++) {
IBinaryAnnotation annotation= annotations[a];
addBinaryAnnotation(annotation);
}
}
tagBits= field.getTagBits() & TagBits.AllStandardAnnotationsMask;
if (tagBits != 0) {
addBinaryStandardAnnotations(tagBits);
}
}
}
// record all references found inside the .class file
extractReferenceFromConstantPool(contents, reader);
} catch (ClassFormatException e) {
// ignore
this.document.removeAllIndexEntries();
Util.log(
IStatus.WARNING,
"The Java indexing could not index " + this.document.getPath() + ". This .class file doesn't follow the class file format specification. Please report this issue against the .class file vendor"); //$NON-NLS-1$ //$NON-NLS-2$
} catch (RuntimeException e) {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=182154
// logging the entry that could not be indexed and continue with the next one
// we remove all entries relative to the boggus document
this.document.removeAllIndexEntries();
Util.log(
IStatus.WARNING,
"The Java indexing could not index " + this.document.getPath() + ". This .class file doesn't follow the class file format specification. Please report this issue against the .class file vendor"); //$NON-NLS-1$ //$NON-NLS-2$
}
}
private char[] removeFirstSyntheticParameter(char[] descriptor) {
if (descriptor == null)
return null;
if (descriptor.length < 3)
return descriptor;
if (descriptor[0] != '(')
return descriptor;
if (descriptor[1] != ')') {
// remove the first synthetic parameter
int start= Util.scanTypeSignature(descriptor, 1) + 1;
int length= descriptor.length - start;
char[] signature= new char[length + 1];
signature[0]= descriptor[0];
System.arraycopy(descriptor, start, signature, 1, length);
return signature;
} else {
return descriptor;
}
}
/*
* Modify the array by replacing all occurences of toBeReplaced with newChar
*/
private char[][] replace(char toBeReplaced, char newChar, char[][] array) {
if (array == null)
return null;
for (int i= 0, max= array.length; i < max; i++) {
replace(toBeReplaced, newChar, array[i]);
}
return array;
}
/*
* Modify the array by replacing all occurences of toBeReplaced with newChar
*/
private char[] replace(char toBeReplaced, char newChar, char[] array) {
if (array == null)
return null;
for (int i= 0, max= array.length; i < max; i++) {
if (array[i] == toBeReplaced) {
array[i]= newChar;
}
}
return array;
}
}