| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.compiler.lookup; |
| |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; |
| import org.eclipse.jdt.internal.compiler.env.IBinaryField; |
| import org.eclipse.jdt.internal.compiler.env.IBinaryMethod; |
| import org.eclipse.jdt.internal.compiler.env.IBinaryNestedType; |
| import org.eclipse.jdt.internal.compiler.env.IBinaryType; |
| import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; |
| |
| /* |
| Not all fields defined by this type are initialized when it is created. |
| Some are initialized only when needed. |
| |
| Accessors have been provided for some public fields so all TypeBindings have the same API... |
| but access public fields directly whenever possible. |
| Non-public fields have accessors which should be used everywhere you expect the field to be initialized. |
| |
| null is NOT a valid value for a non-public field... it just means the field is not initialized. |
| */ |
| |
| public final class BinaryTypeBinding extends ReferenceBinding { |
| // all of these fields are ONLY guaranteed to be initialized if accessed using their public accessor method |
| private ReferenceBinding superclass; |
| private ReferenceBinding enclosingType; |
| private ReferenceBinding[] superInterfaces; |
| private FieldBinding[] fields; |
| private MethodBinding[] methods; |
| private ReferenceBinding[] memberTypes; |
| |
| // For the link with the principle structure |
| private LookupEnvironment environment; |
| public BinaryTypeBinding(PackageBinding packageBinding, IBinaryType binaryType, LookupEnvironment environment) { |
| this.compoundName = CharOperation.splitOn('/', binaryType.getName()); |
| computeId(); |
| |
| this.tagBits |= IsBinaryBinding; |
| this.environment = environment; |
| this.fPackage = packageBinding; |
| this.fileName = binaryType.getFileName(); |
| |
| // source name must be one name without "$". |
| char[] possibleSourceName = this.compoundName[this.compoundName.length - 1]; |
| int start = CharOperation.lastIndexOf('$', possibleSourceName) + 1; |
| if (start == 0) { |
| this.sourceName = possibleSourceName; |
| } else { |
| this.sourceName = new char[possibleSourceName.length - start]; |
| System.arraycopy(possibleSourceName, start, this.sourceName, 0, this.sourceName.length); |
| } |
| |
| this.modifiers = binaryType.getModifiers(); |
| if (binaryType.isInterface()) |
| this.modifiers |= AccInterface; |
| |
| if (binaryType.isAnonymous()) { |
| this.tagBits |= AnonymousTypeMask; |
| } else if (binaryType.isLocal()) { |
| this.tagBits |= LocalTypeMask; |
| } else if (binaryType.isMember()) { |
| this.tagBits |= MemberTypeMask; |
| } |
| } |
| |
| public FieldBinding[] availableFields() { |
| FieldBinding[] availableFields = new FieldBinding[fields.length]; |
| int count = 0; |
| |
| for (int i = 0; i < fields.length;i++) { |
| try { |
| availableFields[count] = resolveTypeFor(fields[i]); |
| count++; |
| } catch (AbortCompilation a){ |
| // silent abort |
| } |
| } |
| |
| System.arraycopy(availableFields, 0, availableFields = new FieldBinding[count], 0, count); |
| return availableFields; |
| } |
| |
| public MethodBinding[] availableMethods() { |
| if ((modifiers & AccUnresolved) == 0) |
| return methods; |
| |
| MethodBinding[] availableMethods = new MethodBinding[methods.length]; |
| int count = 0; |
| |
| for (int i = 0; i < methods.length;i++) { |
| try { |
| availableMethods[count] = resolveTypesFor(methods[i]); |
| count++; |
| } catch (AbortCompilation a){ |
| // silent abort |
| } |
| } |
| System.arraycopy(availableMethods, 0, availableMethods = new MethodBinding[count], 0, count); |
| return availableMethods; |
| } |
| |
| void cachePartsFrom(IBinaryType binaryType, boolean needFieldsAndMethods) { |
| char[] superclassName = binaryType.getSuperclassName(); |
| if (superclassName != null) |
| // attempt to find the superclass if it exists in the cache (otherwise - resolve it when requested) |
| this.superclass = environment.getTypeFromConstantPoolName(superclassName, 0, -1); |
| |
| char[] enclosingTypeName = binaryType.getEnclosingTypeName(); |
| if (enclosingTypeName != null) { |
| // attempt to find the enclosing type if it exists in the cache (otherwise - resolve it when requested) |
| this.enclosingType = environment.getTypeFromConstantPoolName(enclosingTypeName, 0, -1); |
| this.tagBits |= MemberTypeMask; // must be a member type not a top-level or local type |
| if (this.enclosingType().isStrictfp()) |
| this.modifiers |= AccStrictfp; |
| if (this.enclosingType().isDeprecated()) |
| this.modifiers |= AccDeprecatedImplicitly; |
| } |
| |
| this.memberTypes = NoMemberTypes; |
| IBinaryNestedType[] memberTypeStructures = binaryType.getMemberTypes(); |
| if (memberTypeStructures != null) { |
| int size = memberTypeStructures.length; |
| if (size > 0) { |
| this.memberTypes = new ReferenceBinding[size]; |
| for (int i = 0; i < size; i++) |
| // attempt to find each member type if it exists in the cache (otherwise - resolve it when requested) |
| this.memberTypes[i] = environment.getTypeFromConstantPoolName(memberTypeStructures[i].getName(), 0, -1); |
| } |
| } |
| |
| this.superInterfaces = NoSuperInterfaces; |
| char[][] interfaceNames = binaryType.getInterfaceNames(); |
| if (interfaceNames != null) { |
| int size = interfaceNames.length; |
| if (size > 0) { |
| this.superInterfaces = new ReferenceBinding[size]; |
| for (int i = 0; i < size; i++) |
| // attempt to find each superinterface if it exists in the cache (otherwise - resolve it when requested) |
| this.superInterfaces[i] = environment.getTypeFromConstantPoolName(interfaceNames[i], 0, -1); |
| } |
| } |
| if (needFieldsAndMethods) { |
| createFields(binaryType.getFields()); |
| createMethods(binaryType.getMethods()); |
| } else { // protect against incorrect use of the needFieldsAndMethods flag, see 48459 |
| this.fields = NoFields; |
| this.methods = NoMethods; |
| } |
| } |
| private void createFields(IBinaryField[] iFields) { |
| this.fields = NoFields; |
| if (iFields != null) { |
| int size = iFields.length; |
| if (size > 0) { |
| this.fields = new FieldBinding[size]; |
| for (int i = 0; i < size; i++) { |
| IBinaryField field = iFields[i]; |
| this.fields[i] = |
| new FieldBinding( |
| field.getName(), |
| environment.getTypeFromSignature(field.getTypeName(), 0, -1), |
| field.getModifiers() | AccUnresolved, |
| this, |
| field.getConstant()); |
| } |
| } |
| } |
| } |
| private MethodBinding createMethod(IBinaryMethod method) { |
| int methodModifiers = method.getModifiers() | AccUnresolved; |
| |
| ReferenceBinding[] exceptions = NoExceptions; |
| char[][] exceptionTypes = method.getExceptionTypeNames(); |
| if (exceptionTypes != null) { |
| int size = exceptionTypes.length; |
| if (size > 0) { |
| exceptions = new ReferenceBinding[size]; |
| for (int i = 0; i < size; i++) |
| exceptions[i] = environment.getTypeFromConstantPoolName(exceptionTypes[i], 0, -1); |
| } |
| } |
| |
| TypeBinding[] parameters = NoParameters; |
| char[] methodSignature = method.getMethodDescriptor(); // of the form (I[Ljava/jang/String;)V |
| int numOfParams = 0; |
| char nextChar; |
| int index = 0; // first character is always '(' so skip it |
| while ((nextChar = methodSignature[++index]) != ')') { |
| if (nextChar != '[') { |
| numOfParams++; |
| if (nextChar == 'L') |
| while ((nextChar = methodSignature[++index]) != ';'){/*empty*/} |
| } |
| } |
| |
| // Ignore synthetic argument for member types. |
| int startIndex = (method.isConstructor() && isMemberType() && !isStatic()) ? 1 : 0; |
| int size = numOfParams - startIndex; |
| if (size > 0) { |
| parameters = new TypeBinding[size]; |
| index = 1; |
| int end = 0; // first character is always '(' so skip it |
| for (int i = 0; i < numOfParams; i++) { |
| while ((nextChar = methodSignature[++end]) == '['){/*empty*/} |
| if (nextChar == 'L') |
| while ((nextChar = methodSignature[++end]) != ';'){/*empty*/} |
| |
| if (i >= startIndex) // skip the synthetic arg if necessary |
| parameters[i - startIndex] = environment.getTypeFromSignature(methodSignature, index, end); |
| index = end + 1; |
| } |
| } |
| |
| MethodBinding binding = null; |
| if (method.isConstructor()) |
| binding = new MethodBinding(methodModifiers, parameters, exceptions, this); |
| else |
| binding = new MethodBinding( |
| methodModifiers, |
| method.getSelector(), |
| environment.getTypeFromSignature(methodSignature, index + 1, -1), // index is currently pointing at the ')' |
| parameters, |
| exceptions, |
| this); |
| return binding; |
| } |
| /** |
| * Create method bindings for binary type, filtering out <clinit> and synthetics |
| */ |
| private void createMethods(IBinaryMethod[] iMethods) { |
| int total = 0, initialTotal = 0, iClinit = -1; |
| int[] toSkip = null; |
| if (iMethods != null) { |
| total = initialTotal = iMethods.length; |
| for (int i = total; --i >= 0;) { |
| IBinaryMethod method = iMethods[i]; |
| if ((method.getModifiers() & AccSynthetic) != 0) { |
| // discard synthetics methods |
| if (toSkip == null) toSkip = new int[iMethods.length]; |
| toSkip[i] = -1; |
| total--; |
| } else if (iClinit == -1) { |
| char[] methodName = method.getSelector(); |
| if (methodName.length == 8 && methodName[0] == '<') { |
| // discard <clinit> |
| iClinit = i; |
| total--; |
| } |
| } |
| } |
| } |
| if (total == 0) { |
| this.methods = NoMethods; |
| return; |
| } |
| |
| this.methods = new MethodBinding[total]; |
| if (total == initialTotal) { |
| for (int i = 0; i < initialTotal; i++) |
| this.methods[i] = createMethod(iMethods[i]); |
| } else { |
| for (int i = 0, index = 0; i < initialTotal; i++) |
| if (iClinit != i && (toSkip == null || toSkip[i] != -1)) |
| this.methods[index++] = createMethod(iMethods[i]); |
| } |
| modifiers |= AccUnresolved; // until methods() is sent |
| } |
| /* Answer the receiver's enclosing type... null if the receiver is a top level type. |
| * |
| * NOTE: enclosingType of a binary type is resolved when needed |
| */ |
| |
| public ReferenceBinding enclosingType() { |
| if (enclosingType == null) |
| return null; |
| if (enclosingType instanceof UnresolvedReferenceBinding) |
| enclosingType = ((UnresolvedReferenceBinding) enclosingType).resolve(environment); |
| return enclosingType; |
| } |
| // NOTE: the type of each field of a binary type is resolved when needed |
| |
| public FieldBinding[] fields() { |
| for (int i = fields.length; --i >= 0;) |
| resolveTypeFor(fields[i]); |
| return fields; |
| } |
| // NOTE: the return type, arg & exception types of each method of a binary type are resolved when needed |
| |
| public MethodBinding getExactConstructor(TypeBinding[] argumentTypes) { |
| int argCount = argumentTypes.length; |
| nextMethod : for (int m = methods.length; --m >= 0;) { |
| MethodBinding method = methods[m]; |
| if (method.selector == ConstructorDeclaration.ConstantPoolName && method.parameters.length == argCount) { |
| resolveTypesFor(method); |
| TypeBinding[] toMatch = method.parameters; |
| for (int p = 0; p < argCount; p++) |
| if (toMatch[p] != argumentTypes[p]) |
| continue nextMethod; |
| return method; |
| } |
| } |
| return null; |
| } |
| // NOTE: the return type, arg & exception types of each method of a binary type are resolved when needed |
| // searches up the hierarchy as long as no potential (but not exact) match was found. |
| |
| public MethodBinding getExactMethod(char[] selector, TypeBinding[] argumentTypes) { |
| int argCount = argumentTypes.length; |
| int selectorLength = selector.length; |
| boolean foundNothing = true; |
| nextMethod : for (int m = methods.length; --m >= 0;) { |
| MethodBinding method = methods[m]; |
| if (method.selector.length == selectorLength && CharOperation.equals(method.selector, selector)) { |
| foundNothing = false; // inner type lookups must know that a method with this name exists |
| if (method.parameters.length == argCount) { |
| resolveTypesFor(method); |
| TypeBinding[] toMatch = method.parameters; |
| for (int p = 0; p < argCount; p++) |
| if (toMatch[p] != argumentTypes[p]) |
| continue nextMethod; |
| return method; |
| } |
| } |
| } |
| |
| if (foundNothing) { |
| if (isInterface()) { |
| if (superInterfaces.length == 1) |
| return superInterfaces[0].getExactMethod(selector, argumentTypes); |
| } else if (superclass != null) { |
| return superclass.getExactMethod(selector, argumentTypes); |
| } |
| } |
| return null; |
| } |
| // NOTE: the type of a field of a binary type is resolved when needed |
| |
| public FieldBinding getField(char[] fieldName, boolean needResolve) { |
| int fieldLength = fieldName.length; |
| for (int f = fields.length; --f >= 0;) { |
| char[] name = fields[f].name; |
| if (name.length == fieldLength && CharOperation.equals(name, fieldName)) |
| return needResolve ? resolveTypeFor(fields[f]) : fields[f]; |
| } |
| return null; |
| } |
| // NOTE: the return type, arg & exception types of each method of a binary type are resolved when needed |
| |
| public MethodBinding[] getMethods(char[] selector) { |
| int count = 0; |
| int lastIndex = -1; |
| int selectorLength = selector.length; |
| for (int m = 0, length = methods.length; m < length; m++) { |
| MethodBinding method = methods[m]; |
| if (method.selector.length == selectorLength && CharOperation.equals(method.selector, selector)) { |
| resolveTypesFor(method); |
| count++; |
| lastIndex = m; |
| } |
| } |
| if (count == 1) |
| return new MethodBinding[] {methods[lastIndex]}; |
| if (count > 0) { |
| MethodBinding[] result = new MethodBinding[count]; |
| count = 0; |
| for (int m = 0; m <= lastIndex; m++) { |
| MethodBinding method = methods[m]; |
| if (method.selector.length == selectorLength && CharOperation.equals(method.selector, selector)) |
| result[count++] = method; |
| } |
| return result; |
| } |
| return NoMethods; |
| } |
| // NOTE: member types of binary types are resolved when needed |
| |
| public ReferenceBinding[] memberTypes() { |
| for (int i = memberTypes.length; --i >= 0;) |
| if (memberTypes[i] instanceof UnresolvedReferenceBinding) |
| memberTypes[i] = ((UnresolvedReferenceBinding) memberTypes[i]).resolve(environment); |
| return memberTypes; |
| } |
| // NOTE: the return type, arg & exception types of each method of a binary type are resolved when needed |
| |
| public MethodBinding[] methods() { |
| if ((modifiers & AccUnresolved) == 0) |
| return methods; |
| |
| for (int i = methods.length; --i >= 0;) |
| resolveTypesFor(methods[i]); |
| modifiers ^= AccUnresolved; |
| return methods; |
| } |
| TypeBinding resolveType(TypeBinding type) { |
| if (type instanceof UnresolvedReferenceBinding) |
| return ((UnresolvedReferenceBinding) type).resolve(environment); |
| if (type instanceof ArrayBinding) { |
| ArrayBinding array = (ArrayBinding) type; |
| if (array.leafComponentType instanceof UnresolvedReferenceBinding) |
| array.leafComponentType = ((UnresolvedReferenceBinding) array.leafComponentType).resolve(environment); |
| } |
| return type; |
| } |
| private FieldBinding resolveTypeFor(FieldBinding field) { |
| if ((field.modifiers & AccUnresolved) != 0) { |
| field.type = resolveType(field.type); |
| field.modifiers ^= AccUnresolved; |
| } |
| return field; |
| } |
| private MethodBinding resolveTypesFor(MethodBinding method) { |
| if ((method.modifiers & AccUnresolved) == 0) |
| return method; |
| |
| if (!method.isConstructor()) |
| method.returnType = resolveType(method.returnType); |
| for (int i = method.parameters.length; --i >= 0;) |
| method.parameters[i] = resolveType(method.parameters[i]); |
| for (int i = method.thrownExceptions.length; --i >= 0;) |
| if (method.thrownExceptions[i] instanceof UnresolvedReferenceBinding) |
| method.thrownExceptions[i] = ((UnresolvedReferenceBinding) method.thrownExceptions[i]).resolve(environment); |
| method.modifiers ^= AccUnresolved; |
| return method; |
| } |
| /* Answer the receiver's superclass... null if the receiver is Object or an interface. |
| * |
| * NOTE: superclass of a binary type is resolved when needed |
| */ |
| |
| public ReferenceBinding superclass() { |
| if (superclass == null) |
| return null; |
| if (superclass instanceof UnresolvedReferenceBinding) |
| superclass = ((UnresolvedReferenceBinding) superclass).resolve(environment); |
| return superclass; |
| } |
| // NOTE: superInterfaces of binary types are resolved when needed |
| |
| public ReferenceBinding[] superInterfaces() { |
| for (int i = superInterfaces.length; --i >= 0;) |
| if (superInterfaces[i] instanceof UnresolvedReferenceBinding) |
| superInterfaces[i] = ((UnresolvedReferenceBinding) superInterfaces[i]).resolve(environment); |
| return superInterfaces; |
| } |
| MethodBinding[] unResolvedMethods() { // for the MethodVerifier so it doesn't resolve types |
| return methods; |
| } |
| public String toString() { |
| String s = ""; //$NON-NLS-1$ |
| |
| if (isDeprecated()) s += "deprecated "; //$NON-NLS-1$ |
| if (isPublic()) s += "public "; //$NON-NLS-1$ |
| if (isProtected()) s += "protected "; //$NON-NLS-1$ |
| if (isPrivate()) s += "private "; //$NON-NLS-1$ |
| if (isAbstract() && isClass()) s += "abstract "; //$NON-NLS-1$ |
| if (isStatic() && isNestedType()) s += "static "; //$NON-NLS-1$ |
| if (isFinal()) s += "final "; //$NON-NLS-1$ |
| |
| s += isInterface() ? "interface " : "class "; //$NON-NLS-1$ //$NON-NLS-2$ |
| s += (compoundName != null) ? CharOperation.toString(compoundName) : "UNNAMED TYPE"; //$NON-NLS-1$ |
| |
| s += "\n\textends "; //$NON-NLS-1$ |
| s += (superclass != null) ? superclass.debugName() : "NULL TYPE"; //$NON-NLS-1$ |
| |
| if (superInterfaces != null) { |
| if (superInterfaces != NoSuperInterfaces) { |
| s += "\n\timplements : "; //$NON-NLS-1$ |
| for (int i = 0, length = superInterfaces.length; i < length; i++) { |
| if (i > 0) |
| s += ", "; //$NON-NLS-1$ |
| s += (superInterfaces[i] != null) ? superInterfaces[i].debugName() : "NULL TYPE"; //$NON-NLS-1$ |
| } |
| } |
| } else { |
| s += "NULL SUPERINTERFACES"; //$NON-NLS-1$ |
| } |
| |
| if (enclosingType != null) { |
| s += "\n\tenclosing type : "; //$NON-NLS-1$ |
| s += enclosingType.debugName(); |
| } |
| |
| if (fields != null) { |
| if (fields != NoFields) { |
| s += "\n/* fields */"; //$NON-NLS-1$ |
| for (int i = 0, length = fields.length; i < length; i++) |
| s += (fields[i] != null) ? "\n" + fields[i].toString() : "\nNULL FIELD"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } else { |
| s += "NULL FIELDS"; //$NON-NLS-1$ |
| } |
| |
| if (methods != null) { |
| if (methods != NoMethods) { |
| s += "\n/* methods */"; //$NON-NLS-1$ |
| for (int i = 0, length = methods.length; i < length; i++) |
| s += (methods[i] != null) ? "\n" + methods[i].toString() : "\nNULL METHOD"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } else { |
| s += "NULL METHODS"; //$NON-NLS-1$ |
| } |
| |
| if (memberTypes != null) { |
| if (memberTypes != NoMemberTypes) { |
| s += "\n/* members */"; //$NON-NLS-1$ |
| for (int i = 0, length = memberTypes.length; i < length; i++) |
| s += (memberTypes[i] != null) ? "\n" + memberTypes[i].toString() : "\nNULL TYPE"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } else { |
| s += "NULL MEMBER TYPES"; //$NON-NLS-1$ |
| } |
| |
| s += "\n\n\n"; //$NON-NLS-1$ |
| return s; |
| } |
| } |