/******************************************************************************* * Copyright (c) 2000, 2003 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 net.sourceforge.phpdt.internal.compiler.lookup; import net.sourceforge.phpdt.core.compiler.CharOperation; import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration; import net.sourceforge.phpdt.internal.compiler.ast.CompilationUnitDeclaration; import net.sourceforge.phpdt.internal.compiler.ast.ImportReference; import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration; import net.sourceforge.phpdt.internal.compiler.impl.ReferenceContext; import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter; import net.sourceforge.phpdt.internal.compiler.util.ObjectVector; public abstract class Scope implements BaseTypes, BindingIds, CompilerModifiers, ProblemReasons, TagBits, TypeConstants, TypeIds { public Scope parent; public int kind; public final static int BLOCK_SCOPE = 1; public final static int METHOD_SCOPE = 2; public final static int CLASS_SCOPE = 3; public final static int COMPILATION_UNIT_SCOPE = 4; protected Scope(int kind, Scope parent) { this.kind = kind; this.parent = parent; } public abstract ProblemReporter problemReporter(); // Internal use only protected final boolean areParametersAssignable(TypeBinding[] parameters, TypeBinding[] arguments) { if (parameters == arguments) return true; int length = parameters.length; if (length != arguments.length) return false; for (int i = 0; i < length; i++) if (parameters[i] != arguments[i]) if (!arguments[i].isCompatibleWith(parameters[i])) return false; return true; } /* * Answer an int describing the relationship between the given types. * * NotRelated EqualOrMoreSpecific : left is compatible with right * MoreGeneric : right is compatible with left */ public static int compareTypes(TypeBinding left, TypeBinding right) { if (left.isCompatibleWith(right)) return EqualOrMoreSpecific; if (right.isCompatibleWith(left)) return MoreGeneric; return NotRelated; } /* * Answer an int describing the relationship between the given type and * unchecked exceptions. * * NotRelated EqualOrMoreSpecific : type is known for sure to be an * unchecked exception type MoreGeneric : type is a supertype of an actual * unchecked exception type */ public int compareUncheckedException(ReferenceBinding type) { int comparison = compareTypes(type, getJavaLangRuntimeException()); if (comparison != 0) return comparison; return compareTypes(type, getJavaLangError()); } public final CompilationUnitScope compilationUnitScope() { Scope lastScope = null; Scope scope = this; do { lastScope = scope; scope = scope.parent; } while (scope != null); return (CompilationUnitScope) lastScope; } public ArrayBinding createArray(TypeBinding type, int dimension) { if (type.isValidBinding()) return environment().createArrayType(type, dimension); else return new ArrayBinding(type, dimension); } public final ClassScope enclosingClassScope() { Scope scope = this; while ((scope = scope.parent) != null) { if (scope instanceof ClassScope) return (ClassScope) scope; } return null; // may answer null if no type around } public final MethodScope enclosingMethodScope() { Scope scope = this; while ((scope = scope.parent) != null) { if (scope instanceof MethodScope) return (MethodScope) scope; } return null; // may answer null if no method around } /* * Answer the receiver's enclosing source type. */ public final SourceTypeBinding enclosingSourceType() { Scope scope = this; do { if (scope instanceof ClassScope) return ((ClassScope) scope).referenceContext.binding; scope = scope.parent; } while (scope != null); return null; } public final LookupEnvironment environment() { Scope scope, unitScope = this; while ((scope = unitScope.parent) != null) unitScope = scope; return ((CompilationUnitScope) unitScope).environment; } // Internal use only public ReferenceBinding findDirectMemberType(char[] typeName, ReferenceBinding enclosingType) { if ((enclosingType.tagBits & HasNoMemberTypes) != 0) return null; // know it has no member types (nor inherited member // types) SourceTypeBinding enclosingSourceType = enclosingSourceType(); compilationUnitScope().recordReference(enclosingType.compoundName, typeName); ReferenceBinding memberType = enclosingType.getMemberType(typeName); if (memberType != null) { compilationUnitScope().recordTypeReference(memberType); // to record // supertypes if (enclosingSourceType == null ? memberType .canBeSeenBy(getCurrentPackage()) : memberType.canBeSeenBy( enclosingType, enclosingSourceType)) return memberType; else return new ProblemReferenceBinding(typeName, memberType, NotVisible); } return null; } // Internal use only public MethodBinding findExactMethod(ReferenceBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) { compilationUnitScope().recordTypeReference(receiverType); compilationUnitScope().recordTypeReferences(argumentTypes); MethodBinding exactMethod = receiverType.getExactMethod(selector, argumentTypes); if (exactMethod != null) { compilationUnitScope().recordTypeReferences( exactMethod.thrownExceptions); if (receiverType.isInterface() || exactMethod.canBeSeenBy(receiverType, invocationSite, this)) return exactMethod; } return null; } // Internal use only /* * Answer the field binding that corresponds to fieldName. Start the lookup * at the receiverType. InvocationSite implements isSuperAccess(); this is * used to determine if the discovered field is visible. Only fields defined * by the receiverType or its supertypes are answered; a field of an * enclosing type will not be found using this API. * * If no visible field is discovered, null is answered. */ public FieldBinding findField(TypeBinding receiverType, char[] fieldName, InvocationSite invocationSite) { if (receiverType.isBaseType()) return null; if (receiverType.isArrayType()) { TypeBinding leafType = receiverType.leafComponentType(); if (leafType instanceof ReferenceBinding) { if (!((ReferenceBinding) leafType).canBeSeenBy(this)) return new ProblemFieldBinding((ReferenceBinding) leafType, fieldName, ReceiverTypeNotVisible); } if (CharOperation.equals(fieldName, LENGTH)) return ArrayBinding.LengthField; return null; } compilationUnitScope().recordTypeReference(receiverType); ReferenceBinding currentType = (ReferenceBinding) receiverType; if (!currentType.canBeSeenBy(this)) return new ProblemFieldBinding(currentType, fieldName, ReceiverTypeNotVisible); FieldBinding field = currentType.getField(fieldName); if (field != null) { if (field.canBeSeenBy(currentType, invocationSite, this)) return field; else return new ProblemFieldBinding(field.declaringClass, fieldName, NotVisible); } // collect all superinterfaces of receiverType until the field is found // in a supertype ReferenceBinding[][] interfacesToVisit = null; int lastPosition = -1; FieldBinding visibleField = null; boolean keepLooking = true; boolean notVisible = false; // we could hold onto the not visible field for extra error reporting while (keepLooking) { ReferenceBinding[] itsInterfaces = currentType.superInterfaces(); if (itsInterfaces != NoSuperInterfaces) { if (interfacesToVisit == null) interfacesToVisit = new ReferenceBinding[5][]; if (++lastPosition == interfacesToVisit.length) System .arraycopy( interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[lastPosition * 2][], 0, lastPosition); interfacesToVisit[lastPosition] = itsInterfaces; } if ((currentType = currentType.superclass()) == null) break; if ((field = currentType.getField(fieldName)) != null) { keepLooking = false; if (field.canBeSeenBy(receiverType, invocationSite, this)) { if (visibleField == null) visibleField = field; else return new ProblemFieldBinding( visibleField.declaringClass, fieldName, Ambiguous); } else { notVisible = true; } } } // walk all visible interfaces to find ambiguous references if (interfacesToVisit != null) { ProblemFieldBinding ambiguous = null; done: for (int i = 0; i <= lastPosition; i++) { ReferenceBinding[] interfaces = interfacesToVisit[i]; for (int j = 0, length = interfaces.length; j < length; j++) { ReferenceBinding anInterface = interfaces[j]; if ((anInterface.tagBits & InterfaceVisited) == 0) { // if interface as not already been visited anInterface.tagBits |= InterfaceVisited; if ((field = anInterface.getField(fieldName)) != null) { if (visibleField == null) { visibleField = field; } else { ambiguous = new ProblemFieldBinding( visibleField.declaringClass, fieldName, Ambiguous); break done; } } else { ReferenceBinding[] itsInterfaces = anInterface .superInterfaces(); if (itsInterfaces != NoSuperInterfaces) { if (++lastPosition == interfacesToVisit.length) System .arraycopy( interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[lastPosition * 2][], 0, lastPosition); interfacesToVisit[lastPosition] = itsInterfaces; } } } } } // bit reinitialization for (int i = 0; i <= lastPosition; i++) { ReferenceBinding[] interfaces = interfacesToVisit[i]; for (int j = 0, length = interfaces.length; j < length; j++) interfaces[j].tagBits &= ~InterfaceVisited; } if (ambiguous != null) return ambiguous; } if (visibleField != null) return visibleField; if (notVisible) return new ProblemFieldBinding(currentType, fieldName, NotVisible); return null; } // Internal use only public ReferenceBinding findMemberType(char[] typeName, ReferenceBinding enclosingType) { if ((enclosingType.tagBits & HasNoMemberTypes) != 0) return null; // know it has no member types (nor inherited member // types) SourceTypeBinding enclosingSourceType = enclosingSourceType(); PackageBinding currentPackage = getCurrentPackage(); compilationUnitScope().recordReference(enclosingType.compoundName, typeName); ReferenceBinding memberType = enclosingType.getMemberType(typeName); if (memberType != null) { compilationUnitScope().recordTypeReference(memberType); // to record // supertypes if (enclosingSourceType == null ? memberType .canBeSeenBy(currentPackage) : memberType.canBeSeenBy( enclosingType, enclosingSourceType)) return memberType; else return new ProblemReferenceBinding(typeName, memberType, NotVisible); } // collect all superinterfaces of receiverType until the memberType is // found in a supertype ReferenceBinding currentType = enclosingType; ReferenceBinding[][] interfacesToVisit = null; int lastPosition = -1; ReferenceBinding visibleMemberType = null; boolean keepLooking = true; ReferenceBinding notVisible = null; // we could hold onto the not visible field for extra error reporting while (keepLooking) { ReferenceBinding[] itsInterfaces = currentType.superInterfaces(); if (itsInterfaces != NoSuperInterfaces) { if (interfacesToVisit == null) interfacesToVisit = new ReferenceBinding[5][]; if (++lastPosition == interfacesToVisit.length) System .arraycopy( interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[lastPosition * 2][], 0, lastPosition); interfacesToVisit[lastPosition] = itsInterfaces; } if ((currentType = currentType.superclass()) == null) break; compilationUnitScope().recordReference(currentType.compoundName, typeName); if ((memberType = currentType.getMemberType(typeName)) != null) { compilationUnitScope().recordTypeReference(memberType); // to // record // supertypes keepLooking = false; if (enclosingSourceType == null ? memberType .canBeSeenBy(currentPackage) : memberType.canBeSeenBy( enclosingType, enclosingSourceType)) { if (visibleMemberType == null) visibleMemberType = memberType; else return new ProblemReferenceBinding(typeName, Ambiguous); } else { notVisible = memberType; } } } // walk all visible interfaces to find ambiguous references if (interfacesToVisit != null) { ProblemReferenceBinding ambiguous = null; done: for (int i = 0; i <= lastPosition; i++) { ReferenceBinding[] interfaces = interfacesToVisit[i]; for (int j = 0, length = interfaces.length; j < length; j++) { ReferenceBinding anInterface = interfaces[j]; if ((anInterface.tagBits & InterfaceVisited) == 0) { // if interface as not already been visited anInterface.tagBits |= InterfaceVisited; compilationUnitScope().recordReference( anInterface.compoundName, typeName); if ((memberType = anInterface.getMemberType(typeName)) != null) { compilationUnitScope().recordTypeReference( memberType); // to record supertypes if (visibleMemberType == null) { visibleMemberType = memberType; } else { ambiguous = new ProblemReferenceBinding( typeName, Ambiguous); break done; } } else { ReferenceBinding[] itsInterfaces = anInterface .superInterfaces(); if (itsInterfaces != NoSuperInterfaces) { if (++lastPosition == interfacesToVisit.length) System .arraycopy( interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[lastPosition * 2][], 0, lastPosition); interfacesToVisit[lastPosition] = itsInterfaces; } } } } } // bit reinitialization for (int i = 0; i <= lastPosition; i++) { ReferenceBinding[] interfaces = interfacesToVisit[i]; for (int j = 0, length = interfaces.length; j < length; j++) interfaces[j].tagBits &= ~InterfaceVisited; } if (ambiguous != null) return ambiguous; } if (visibleMemberType != null) return visibleMemberType; if (notVisible != null) return new ProblemReferenceBinding(typeName, notVisible, NotVisible); return null; } // Internal use only public MethodBinding findMethod(ReferenceBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) { ReferenceBinding currentType = receiverType; MethodBinding matchingMethod = null; ObjectVector found = new ObjectVector(); compilationUnitScope().recordTypeReference(receiverType); compilationUnitScope().recordTypeReferences(argumentTypes); if (currentType.isInterface()) { MethodBinding[] currentMethods = currentType.getMethods(selector); int currentLength = currentMethods.length; if (currentLength == 1) { matchingMethod = currentMethods[0]; } else if (currentLength > 1) { found.addAll(currentMethods); } matchingMethod = findMethodInSuperInterfaces(currentType, selector, found, matchingMethod); currentType = getJavaLangObject(); } // boolean isCompliant14 = // compilationUnitScope().environment.options.complianceLevel >= // CompilerOptions.JDK1_4; // superclass lookup ReferenceBinding classHierarchyStart = currentType; while (currentType != null) { MethodBinding[] currentMethods = currentType.getMethods(selector); int currentLength = currentMethods.length; /* * if 1.4 compliant, must filter out redundant protected methods * from superclasses */ // if (isCompliant14){ // nextMethod: for (int i = 0; i < currentLength; i++){ // MethodBinding currentMethod = currentMethods[i]; // // protected method need to be checked only - default access is // already dealt with in #canBeSeen implementation // // when checking that p.C -> q.B -> p.A cannot see default access // members from A through B. // if ((currentMethod.modifiers & AccProtected) == 0) continue // nextMethod; // if (matchingMethod != null){ // if (currentMethod.areParametersEqual(matchingMethod)){ // currentLength--; // currentMethods[i] = null; // discard this match // continue nextMethod; // } // } else { // for (int j = 0, max = found.size; j < max; j++) { // if // (((MethodBinding)found.elementAt(j)).areParametersEqual(currentMethod)){ // currentLength--; // currentMethods[i] = null; // continue nextMethod; // } // } // } // } // } if (currentLength == 1 && matchingMethod == null && found.size == 0) { matchingMethod = currentMethods[0]; } else if (currentLength > 0) { if (matchingMethod != null) { found.add(matchingMethod); matchingMethod = null; } // append currentMethods, filtering out null entries int maxMethod = currentMethods.length; if (maxMethod == currentLength) { // no method was eliminated // for 1.4 compliance (see // above) found.addAll(currentMethods); } else { for (int i = 0, max = currentMethods.length; i < max; i++) { MethodBinding currentMethod = currentMethods[i]; if (currentMethod != null) found.add(currentMethod); } } } currentType = currentType.superclass(); } int foundSize = found.size; if (foundSize == 0) { if (matchingMethod != null && areParametersAssignable(matchingMethod.parameters, argumentTypes)) { // (if no default abstract) must explicitly look for one // instead, which could be a better match if (!matchingMethod.canBeSeenBy(receiverType, invocationSite, this)) { // ignore matching method (to be consistent with multiple // matches, none visible (matching method is then null) MethodBinding interfaceMethod = findDefaultAbstractMethod( receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, null, found); if (interfaceMethod != null) return interfaceMethod; } return matchingMethod; } return findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, matchingMethod, found); } MethodBinding[] candidates = new MethodBinding[foundSize]; int candidatesCount = 0; // argument type compatibility check for (int i = 0; i < foundSize; i++) { MethodBinding methodBinding = (MethodBinding) found.elementAt(i); if (areParametersAssignable(methodBinding.parameters, argumentTypes)) candidates[candidatesCount++] = methodBinding; } if (candidatesCount == 1) { compilationUnitScope().recordTypeReferences( candidates[0].thrownExceptions); return candidates[0]; // have not checked visibility } if (candidatesCount == 0) { // try to find a close match when the // parameter order is wrong or missing some // parameters MethodBinding interfaceMethod = findDefaultAbstractMethod( receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, matchingMethod, found); if (interfaceMethod != null) return interfaceMethod; int argLength = argumentTypes.length; foundSize = found.size; nextMethod: for (int i = 0; i < foundSize; i++) { MethodBinding methodBinding = (MethodBinding) found .elementAt(i); TypeBinding[] params = methodBinding.parameters; int paramLength = params.length; nextArg: for (int a = 0; a < argLength; a++) { TypeBinding arg = argumentTypes[a]; for (int p = 0; p < paramLength; p++) if (params[p] == arg) continue nextArg; continue nextMethod; } return methodBinding; } return (MethodBinding) found.elementAt(0); // no good match so just // use the first one // found } // visibility check int visiblesCount = 0; for (int i = 0; i < candidatesCount; i++) { MethodBinding methodBinding = candidates[i]; if (methodBinding.canBeSeenBy(receiverType, invocationSite, this)) { if (visiblesCount != i) { candidates[i] = null; candidates[visiblesCount] = methodBinding; } visiblesCount++; } } if (visiblesCount == 1) { compilationUnitScope().recordTypeReferences( candidates[0].thrownExceptions); return candidates[0]; } if (visiblesCount == 0) { MethodBinding interfaceMethod = findDefaultAbstractMethod( receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, matchingMethod, found); if (interfaceMethod != null) return interfaceMethod; return new ProblemMethodBinding(candidates[0], candidates[0].selector, candidates[0].parameters, NotVisible); } if (candidates[0].declaringClass.isClass()) { return mostSpecificClassMethodBinding(candidates, visiblesCount); } else { return mostSpecificInterfaceMethodBinding(candidates, visiblesCount); } } // abstract method lookup lookup (since maybe missing default abstract // methods) public MethodBinding findDefaultAbstractMethod( ReferenceBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite, ReferenceBinding classHierarchyStart, MethodBinding matchingMethod, ObjectVector found) { int startFoundSize = found.size; ReferenceBinding currentType = classHierarchyStart; while (currentType != null) { matchingMethod = findMethodInSuperInterfaces(currentType, selector, found, matchingMethod); currentType = currentType.superclass(); } int foundSize = found.size; if (foundSize == startFoundSize) return matchingMethod; // maybe null MethodBinding[] candidates = new MethodBinding[foundSize - startFoundSize]; int candidatesCount = 0; // argument type compatibility check for (int i = startFoundSize; i < foundSize; i++) { MethodBinding methodBinding = (MethodBinding) found.elementAt(i); if (areParametersAssignable(methodBinding.parameters, argumentTypes)) candidates[candidatesCount++] = methodBinding; } if (candidatesCount == 1) { compilationUnitScope().recordTypeReferences( candidates[0].thrownExceptions); return candidates[0]; } if (candidatesCount == 0) { // try to find a close match when the // parameter order is wrong or missing some // parameters int argLength = argumentTypes.length; nextMethod: for (int i = 0; i < foundSize; i++) { MethodBinding methodBinding = (MethodBinding) found .elementAt(i); TypeBinding[] params = methodBinding.parameters; int paramLength = params.length; nextArg: for (int a = 0; a < argLength; a++) { TypeBinding arg = argumentTypes[a]; for (int p = 0; p < paramLength; p++) if (params[p] == arg) continue nextArg; continue nextMethod; } return methodBinding; } return (MethodBinding) found.elementAt(0); // no good match so just // use the first one // found } // no need to check for visibility - interface methods are public return mostSpecificInterfaceMethodBinding(candidates, candidatesCount); } public MethodBinding findMethodInSuperInterfaces( ReferenceBinding currentType, char[] selector, ObjectVector found, MethodBinding matchingMethod) { ReferenceBinding[] itsInterfaces = currentType.superInterfaces(); if (itsInterfaces != NoSuperInterfaces) { ReferenceBinding[][] interfacesToVisit = new ReferenceBinding[5][]; int lastPosition = -1; if (++lastPosition == interfacesToVisit.length) System .arraycopy( interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[lastPosition * 2][], 0, lastPosition); interfacesToVisit[lastPosition] = itsInterfaces; for (int i = 0; i <= lastPosition; i++) { ReferenceBinding[] interfaces = interfacesToVisit[i]; for (int j = 0, length = interfaces.length; j < length; j++) { currentType = interfaces[j]; if ((currentType.tagBits & InterfaceVisited) == 0) { // if interface as not already been visited currentType.tagBits |= InterfaceVisited; MethodBinding[] currentMethods = currentType .getMethods(selector); int currentLength = currentMethods.length; if (currentLength == 1 && matchingMethod == null && found.size == 0) { matchingMethod = currentMethods[0]; } else if (currentLength > 0) { if (matchingMethod != null) { found.add(matchingMethod); matchingMethod = null; } found.addAll(currentMethods); } itsInterfaces = currentType.superInterfaces(); if (itsInterfaces != NoSuperInterfaces) { if (++lastPosition == interfacesToVisit.length) System .arraycopy( interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[lastPosition * 2][], 0, lastPosition); interfacesToVisit[lastPosition] = itsInterfaces; } } } } // bit reinitialization for (int i = 0; i <= lastPosition; i++) { ReferenceBinding[] interfaces = interfacesToVisit[i]; for (int j = 0, length = interfaces.length; j < length; j++) interfaces[j].tagBits &= ~InterfaceVisited; } } return matchingMethod; } // Internal use only public MethodBinding findMethodForArray(ArrayBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) { TypeBinding leafType = receiverType.leafComponentType(); if (leafType instanceof ReferenceBinding) { if (!((ReferenceBinding) leafType).canBeSeenBy(this)) return new ProblemMethodBinding(selector, MethodBinding.NoParameters, (ReferenceBinding) leafType, ReceiverTypeNotVisible); } ReferenceBinding object = getJavaLangObject(); MethodBinding methodBinding = object.getExactMethod(selector, argumentTypes); if (methodBinding != null) { // handle the method clone() specially... cannot be protected or // throw exceptions if (argumentTypes == NoParameters && CharOperation.equals(selector, CLONE)) return new MethodBinding( (methodBinding.modifiers ^ AccProtected) | AccPublic, CLONE, methodBinding.returnType, argumentTypes, null, object); if (methodBinding.canBeSeenBy(receiverType, invocationSite, this)) return methodBinding; } // answers closest approximation, may not check argumentTypes or // visibility methodBinding = findMethod(object, selector, argumentTypes, invocationSite); if (methodBinding == null) return new ProblemMethodBinding(selector, argumentTypes, NotFound); if (methodBinding.isValidBinding()) { if (!areParametersAssignable(methodBinding.parameters, argumentTypes)) return new ProblemMethodBinding(methodBinding, selector, argumentTypes, NotFound); if (!methodBinding.canBeSeenBy(receiverType, invocationSite, this)) return new ProblemMethodBinding(methodBinding, selector, methodBinding.parameters, NotVisible); } return methodBinding; } // Internal use only public ReferenceBinding findType(char[] typeName, PackageBinding declarationPackage, PackageBinding invocationPackage) { compilationUnitScope().recordReference(declarationPackage.compoundName, typeName); ReferenceBinding typeBinding = declarationPackage.getType(typeName); if (typeBinding == null) return null; if (typeBinding.isValidBinding()) { if (declarationPackage != invocationPackage && !typeBinding.canBeSeenBy(invocationPackage)) return new ProblemReferenceBinding(typeName, typeBinding, NotVisible); } return typeBinding; } public TypeBinding getBaseType(char[] name) { // list should be optimized (with most often used first) int length = name.length; if (length > 2 && length < 8) { switch (name[0]) { case 'i': if (length == 3 && name[1] == 'n' && name[2] == 't') return IntBinding; break; case 'v': if (length == 4 && name[1] == 'o' && name[2] == 'i' && name[3] == 'd') return VoidBinding; break; case 'b': if (length == 7 && name[1] == 'o' && name[2] == 'o' && name[3] == 'l' && name[4] == 'e' && name[5] == 'a' && name[6] == 'n') return BooleanBinding; if (length == 4 && name[1] == 'y' && name[2] == 't' && name[3] == 'e') return ByteBinding; break; case 'c': if (length == 4 && name[1] == 'h' && name[2] == 'a' && name[3] == 'r') return CharBinding; break; case 'd': if (length == 6 && name[1] == 'o' && name[2] == 'u' && name[3] == 'b' && name[4] == 'l' && name[5] == 'e') return DoubleBinding; break; case 'f': if (length == 5 && name[1] == 'l' && name[2] == 'o' && name[3] == 'a' && name[4] == 't') return FloatBinding; break; case 'l': if (length == 4 && name[1] == 'o' && name[2] == 'n' && name[3] == 'g') return LongBinding; break; case 's': if (length == 5 && name[1] == 'h' && name[2] == 'o' && name[3] == 'r' && name[4] == 't') return ShortBinding; } } return null; } public final PackageBinding getCurrentPackage() { Scope scope, unitScope = this; while ((scope = unitScope.parent) != null) unitScope = scope; return ((CompilationUnitScope) unitScope).fPackage; } /** * Returns the modifiers of the innermost enclosing declaration. * * @return modifiers */ public int getDeclarationModifiers() { switch (this.kind) { case Scope.BLOCK_SCOPE: case Scope.METHOD_SCOPE: MethodScope methodScope = methodScope(); if (!methodScope.isInsideInitializer()) { // check method modifiers to see if deprecated MethodBinding context = ((AbstractMethodDeclaration) methodScope.referenceContext).binding; if (context != null) { return context.modifiers; } } else { SourceTypeBinding type = ((BlockScope) this).referenceType().binding; // inside field declaration ? check field modifier to see if // deprecated if (methodScope.initializedField != null) { return methodScope.initializedField.modifiers; } if (type != null) { return type.modifiers; } } break; case Scope.CLASS_SCOPE: ReferenceBinding context = ((ClassScope) this).referenceType().binding; if (context != null) { return context.modifiers; } break; } return -1; } public final ReferenceBinding getJavaIoSerializable() { compilationUnitScope().recordQualifiedReference(JAVA_IO_SERIALIZABLE); ReferenceBinding type = environment().getType(JAVA_IO_SERIALIZABLE); if (type != null) return type; problemReporter().isClassPathCorrect(JAVA_IO_SERIALIZABLE, referenceCompilationUnit()); return null; // will not get here since the above error aborts the // compilation } public final ReferenceBinding getJavaLangClass() { compilationUnitScope().recordQualifiedReference(JAVA_LANG_CLASS); ReferenceBinding type = environment().getType(JAVA_LANG_CLASS); if (type != null) return type; problemReporter().isClassPathCorrect(JAVA_LANG_CLASS, referenceCompilationUnit()); return null; // will not get here since the above error aborts the // compilation } public final ReferenceBinding getJavaLangCloneable() { compilationUnitScope().recordQualifiedReference(JAVA_LANG_CLONEABLE); ReferenceBinding type = environment().getType(JAVA_LANG_CLONEABLE); if (type != null) return type; problemReporter().isClassPathCorrect(JAVA_LANG_CLONEABLE, referenceCompilationUnit()); return null; // will not get here since the above error aborts the // compilation } public final ReferenceBinding getJavaLangError() { compilationUnitScope().recordQualifiedReference(JAVA_LANG_ERROR); ReferenceBinding type = environment().getType(JAVA_LANG_ERROR); if (type != null) return type; problemReporter().isClassPathCorrect(JAVA_LANG_ERROR, referenceCompilationUnit()); return null; // will not get here since the above error aborts the // compilation } public final ReferenceBinding getJavaLangAssertionError() { compilationUnitScope().recordQualifiedReference( JAVA_LANG_ASSERTIONERROR); ReferenceBinding type = environment().getType(JAVA_LANG_ASSERTIONERROR); if (type != null) return type; problemReporter().isClassPathCorrect(JAVA_LANG_ASSERTIONERROR, referenceCompilationUnit()); return null; // will not get here since the above error aborts the // compilation } public final ReferenceBinding getJavaLangObject() { compilationUnitScope().recordQualifiedReference(JAVA_LANG_OBJECT); ReferenceBinding type = environment().getType(JAVA_LANG_OBJECT); if (type != null) return type; problemReporter().isClassPathCorrect(JAVA_LANG_OBJECT, referenceCompilationUnit()); return null; // will not get here since the above error aborts the // compilation } public final ReferenceBinding getJavaLangRuntimeException() { compilationUnitScope().recordQualifiedReference( JAVA_LANG_RUNTIMEEXCEPTION); ReferenceBinding type = environment().getType( JAVA_LANG_RUNTIMEEXCEPTION); if (type != null) return type; problemReporter().isClassPathCorrect(JAVA_LANG_RUNTIMEEXCEPTION, referenceCompilationUnit()); return null; // will not get here since the above error aborts the // compilation } public final ReferenceBinding getJavaLangString() { compilationUnitScope().recordQualifiedReference(JAVA_LANG_STRING); ReferenceBinding type = environment().getType(JAVA_LANG_STRING); if (type != null) return type; problemReporter().isClassPathCorrect(JAVA_LANG_STRING, referenceCompilationUnit()); return null; // will not get here since the above error aborts the // compilation } public final ReferenceBinding getJavaLangThrowable() { compilationUnitScope().recordQualifiedReference(JAVA_LANG_THROWABLE); ReferenceBinding type = environment().getType(JAVA_LANG_THROWABLE); if (type != null) return type; // problemReporter().isClassPathCorrect(JAVA_LANG_THROWABLE, // referenceCompilationUnit()); return null; // will not get here since the above error aborts the // compilation } /* * Answer the type binding corresponding to the typeName argument, relative * to the enclosingType. */ public final ReferenceBinding getMemberType(char[] typeName, ReferenceBinding enclosingType) { ReferenceBinding memberType = findMemberType(typeName, enclosingType); if (memberType != null) return memberType; return new ProblemReferenceBinding(typeName, NotFound); } /* * Answer the type binding corresponding to the compoundName. * * NOTE: If a problem binding is returned, senders should extract the * compound name from the binding & not assume the problem applies to the * entire compoundName. */ public final TypeBinding getType(char[][] compoundName) { int typeNameLength = compoundName.length; if (typeNameLength == 1) { // Would like to remove this test and require senders to specially // handle base types TypeBinding binding = getBaseType(compoundName[0]); if (binding != null) return binding; } compilationUnitScope().recordQualifiedReference(compoundName); Binding binding = getTypeOrPackage(compoundName[0], typeNameLength == 1 ? TYPE : TYPE | PACKAGE); if (binding == null) return new ProblemReferenceBinding(compoundName[0], NotFound); if (!binding.isValidBinding()) return (ReferenceBinding) binding; int currentIndex = 1; boolean checkVisibility = false; if (binding instanceof PackageBinding) { PackageBinding packageBinding = (PackageBinding) binding; while (currentIndex < typeNameLength) { binding = packageBinding .getTypeOrPackage(compoundName[currentIndex++]); // does // not // check // visibility if (binding == null) return new ProblemReferenceBinding(CharOperation.subarray( compoundName, 0, currentIndex), NotFound); if (!binding.isValidBinding()) return new ProblemReferenceBinding(CharOperation.subarray( compoundName, 0, currentIndex), binding.problemId()); if (!(binding instanceof PackageBinding)) break; packageBinding = (PackageBinding) binding; } if (binding instanceof PackageBinding) return new ProblemReferenceBinding(CharOperation.subarray( compoundName, 0, currentIndex), NotFound); checkVisibility = true; } // binding is now a ReferenceBinding ReferenceBinding typeBinding = (ReferenceBinding) binding; compilationUnitScope().recordTypeReference(typeBinding); // to record // supertypes if (checkVisibility) // handles the fall through case if (!typeBinding.canBeSeenBy(this)) return new ProblemReferenceBinding(CharOperation.subarray( compoundName, 0, currentIndex), typeBinding, NotVisible); while (currentIndex < typeNameLength) { typeBinding = getMemberType(compoundName[currentIndex++], typeBinding); if (!typeBinding.isValidBinding()) return new ProblemReferenceBinding(CharOperation.subarray( compoundName, 0, currentIndex), typeBinding.problemId()); } return typeBinding; } /* * Answer the type binding that corresponds the given name, starting the * lookup in the receiver. The name provided is a simple source name (e.g., * "Object" , "Point", ...) */ // The return type of this method could be ReferenceBinding if we did not // answer base types. // NOTE: We could support looking for Base Types last in the search, however // any code using // this feature would be extraordinarily slow. Therefore we don't do this public final TypeBinding getType(char[] name) { // Would like to remove this test and require senders to specially // handle base types TypeBinding binding = getBaseType(name); if (binding != null) return binding; return (ReferenceBinding) getTypeOrPackage(name, TYPE); } // Added for code assist... NOT Public API public final Binding getTypeOrPackage(char[][] compoundName) { int nameLength = compoundName.length; if (nameLength == 1) { TypeBinding binding = getBaseType(compoundName[0]); if (binding != null) return binding; } Binding binding = getTypeOrPackage(compoundName[0], TYPE | PACKAGE); if (!binding.isValidBinding()) return binding; int currentIndex = 1; boolean checkVisibility = false; if (binding instanceof PackageBinding) { PackageBinding packageBinding = (PackageBinding) binding; while (currentIndex < nameLength) { binding = packageBinding .getTypeOrPackage(compoundName[currentIndex++]); if (binding == null) return new ProblemReferenceBinding(CharOperation.subarray( compoundName, 0, currentIndex), NotFound); if (!binding.isValidBinding()) return new ProblemReferenceBinding(CharOperation.subarray( compoundName, 0, currentIndex), binding.problemId()); if (!(binding instanceof PackageBinding)) break; packageBinding = (PackageBinding) binding; } if (binding instanceof PackageBinding) return binding; checkVisibility = true; } // binding is now a ReferenceBinding ReferenceBinding typeBinding = (ReferenceBinding) binding; if (checkVisibility) // handles the fall through case if (!typeBinding.canBeSeenBy(this)) return new ProblemReferenceBinding(CharOperation.subarray( compoundName, 0, currentIndex), typeBinding, NotVisible); while (currentIndex < nameLength) { typeBinding = getMemberType(compoundName[currentIndex++], typeBinding); // checks visibility if (!typeBinding.isValidBinding()) return new ProblemReferenceBinding(CharOperation.subarray( compoundName, 0, currentIndex), typeBinding.problemId()); } return typeBinding; } /* * Internal use only */ final Binding getTypeOrPackage(char[] name, int mask) { Scope scope = this; ReferenceBinding foundType = null; if ((mask & TYPE) == 0) { Scope next = scope; while ((next = scope.parent) != null) scope = next; } else { done: while (true) { // done when a COMPILATION_UNIT_SCOPE is // found switch (scope.kind) { case METHOD_SCOPE: case BLOCK_SCOPE: ReferenceBinding localType = ((BlockScope) scope) .findLocalType(name); // looks in this scope only if (localType != null) { if (foundType != null && foundType != localType) return new ProblemReferenceBinding(name, InheritedNameHidesEnclosingName); return localType; } break; case CLASS_SCOPE: SourceTypeBinding sourceType = ((ClassScope) scope).referenceContext.binding; // 6.5.5.1 - simple name favors member type over top-level // type in same unit ReferenceBinding memberType = findMemberType(name, sourceType); if (memberType != null) { // skip it if we did not find // anything if (memberType.problemId() == Ambiguous) { if (foundType == null || foundType.problemId() == NotVisible) // supercedes any potential // InheritedNameHidesEnclosingName problem return memberType; else // make the user qualify the type, likely wants // the first inherited type return new ProblemReferenceBinding(name, InheritedNameHidesEnclosingName); } // if (memberType.isValidBinding()) { // if (sourceType == memberType.enclosingType() // || environment().options.complianceLevel >= // CompilerOptions.JDK1_4) { // // found a valid type in the 'immediate' scope (ie. // not inherited) // // OR in 1.4 mode (inherited shadows enclosing) // if (foundType == null) // return memberType; // if (foundType.isValidBinding()) // // if a valid type was found, complain when another // is found in an 'immediate' enclosing type (ie. not // inherited) // if (foundType != memberType) // return new ProblemReferenceBinding(name, // InheritedNameHidesEnclosingName); // } // } if (foundType == null || (foundType.problemId() == NotVisible && memberType .problemId() != NotVisible)) // only remember the memberType if its the first one // found or the previous one was not visible & // memberType is... foundType = memberType; } if (CharOperation.equals(sourceType.sourceName, name)) { if (foundType != null && foundType != sourceType && foundType.problemId() != NotVisible) return new ProblemReferenceBinding(name, InheritedNameHidesEnclosingName); return sourceType; } break; case COMPILATION_UNIT_SCOPE: break done; } scope = scope.parent; } if (foundType != null && foundType.problemId() != NotVisible) return foundType; } // at this point the scope is a compilation unit scope CompilationUnitScope unitScope = (CompilationUnitScope) scope; PackageBinding currentPackage = unitScope.fPackage; // ask for the imports + name if ((mask & TYPE) != 0) { // check single type imports. ImportBinding[] imports = unitScope.imports; if (imports != null) { // copy the list, since single type imports are removed if they // cannot be resolved for (int i = 0, length = imports.length; i < length; i++) { ImportBinding typeImport = imports[i]; if (!typeImport.onDemand) { if (CharOperation .equals( typeImport.compoundName[typeImport.compoundName.length - 1], name)) { if (unitScope.resolveSingleTypeImport(typeImport) != null) { ImportReference importReference = typeImport.reference; if (importReference != null) importReference.used = true; return typeImport.resolvedImport; // already // know its // visible } } } } } // check if the name is in the current package, skip it if its a // sub-package unitScope.recordReference(currentPackage.compoundName, name); Binding binding = currentPackage.getTypeOrPackage(name); if (binding instanceof ReferenceBinding) return binding; // type is always visible to its own package // check on demand imports boolean foundInImport = false; ReferenceBinding type = null; if (imports != null) { for (int i = 0, length = imports.length; i < length; i++) { ImportBinding someImport = imports[i]; if (someImport.onDemand) { Binding resolvedImport = someImport.resolvedImport; ReferenceBinding temp = resolvedImport instanceof PackageBinding ? findType( name, (PackageBinding) resolvedImport, currentPackage) : findDirectMemberType(name, (ReferenceBinding) resolvedImport); if (temp != null && temp.isValidBinding()) { // ImportReference importReference = // someImport.reference; // if (importReference != null) importReference.used // = true; if (foundInImport) // Answer error binding -- import on demand // conflict; name found in two import on demand // packages. return new ProblemReferenceBinding(name, Ambiguous); type = temp; foundInImport = true; } } } } if (type != null) return type; } unitScope.recordSimpleReference(name); if ((mask & PACKAGE) != 0) { PackageBinding packageBinding = unitScope.environment .getTopLevelPackage(name); if (packageBinding != null) return packageBinding; } // Answer error binding -- could not find name if (foundType != null) return foundType; // problem type from above return new ProblemReferenceBinding(name, NotFound); } /* * Answer whether the type is defined in the same compilation unit as the * receiver */ public final boolean isDefinedInSameUnit(ReferenceBinding type) { // find the outer most enclosing type ReferenceBinding enclosingType = type; while ((type = enclosingType.enclosingType()) != null) enclosingType = type; // find the compilation unit scope Scope scope, unitScope = this; while ((scope = unitScope.parent) != null) unitScope = scope; // test that the enclosingType is not part of the compilation unit SourceTypeBinding[] topLevelTypes = ((CompilationUnitScope) unitScope).topLevelTypes; for (int i = topLevelTypes.length; --i >= 0;) if (topLevelTypes[i] == enclosingType) return true; return false; } /* * Answer true if the scope is nested inside a given field declaration. * Note: it works as long as the scope.fieldDeclarationIndex is reflecting * the field being traversed e.g. during name resolution. */ public final boolean isDefinedInField(FieldBinding field) { Scope scope = this; do { if (scope instanceof MethodScope) { MethodScope methodScope = (MethodScope) scope; ReferenceContext refContext = methodScope.referenceContext; if (refContext instanceof TypeDeclaration && ((TypeDeclaration) refContext).binding == field.declaringClass && methodScope.fieldDeclarationIndex == field.id) { return true; } } scope = scope.parent; } while (scope != null); return false; } /* * Answer true if the scope is nested inside a given method declaration */ public final boolean isDefinedInMethod(MethodBinding method) { Scope scope = this; do { if (scope instanceof MethodScope) { ReferenceContext refContext = ((MethodScope) scope).referenceContext; if (refContext instanceof AbstractMethodDeclaration && ((AbstractMethodDeclaration) refContext).binding == method) { return true; } } scope = scope.parent; } while (scope != null); return false; } /* * Answer true if the scope is nested inside a given type declaration */ public final boolean isDefinedInType(ReferenceBinding type) { Scope scope = this; do { if (scope instanceof ClassScope) if (((ClassScope) scope).referenceContext.binding == type) { return true; } scope = scope.parent; } while (scope != null); return false; } public boolean isInsideDeprecatedCode() { switch (kind) { case Scope.BLOCK_SCOPE: case Scope.METHOD_SCOPE: MethodScope methodScope = methodScope(); if (!methodScope.isInsideInitializer()) { // check method modifiers to see if deprecated MethodBinding context = ((AbstractMethodDeclaration) methodScope.referenceContext).binding; if (context != null && context.isViewedAsDeprecated()) { return true; } } else { SourceTypeBinding type = ((BlockScope) this).referenceType().binding; // inside field declaration ? check field modifier to see if // deprecated if (methodScope.fieldDeclarationIndex != MethodScope.NotInFieldDecl) { for (int i = 0; i < type.fields.length; i++) { if (type.fields[i].id == methodScope.fieldDeclarationIndex) { // currently inside this field initialization if (type.fields[i].isViewedAsDeprecated()) { return true; } break; } } } if (type != null && type.isViewedAsDeprecated()) { return true; } } break; case Scope.CLASS_SCOPE: ReferenceBinding context = ((ClassScope) this).referenceType().binding; if (context != null && context.isViewedAsDeprecated()) { return true; } break; } return false; } public final boolean isJavaIoSerializable(TypeBinding tb) { return tb == getJavaIoSerializable(); } public final boolean isJavaLangCloneable(TypeBinding tb) { return tb == getJavaLangCloneable(); } public final boolean isJavaLangObject(TypeBinding type) { return type.id == T_JavaLangObject; } public final MethodScope methodScope() { Scope scope = this; do { if (scope instanceof MethodScope) return (MethodScope) scope; scope = scope.parent; } while (scope != null); return null; } // Internal use only /* * All methods in visible are acceptable matches for the method in * question... The methods defined by the receiver type appear before those * defined by its superclass and so on. We want to find the one which * matches best. * * Since the receiver type is a class, we know each method's declaring class * is either the receiver type or one of its superclasses. It is an error if * the best match is defined by a superclass, when a lesser match is defined * by the receiver type or a closer superclass. */ protected final MethodBinding mostSpecificClassMethodBinding( MethodBinding[] visible, int visibleSize) { MethodBinding method = null; MethodBinding previous = null; nextVisible: for (int i = 0; i < visibleSize; i++) { method = visible[i]; if (previous != null && method.declaringClass != previous.declaringClass) break; // cannot answer a method farther up the hierarchy than // the first method found previous = method; for (int j = 0; j < visibleSize; j++) { if (i == j) continue; MethodBinding next = visible[j]; if (!areParametersAssignable(next.parameters, method.parameters)) continue nextVisible; } compilationUnitScope() .recordTypeReferences(method.thrownExceptions); return method; } return new ProblemMethodBinding(visible[0].selector, visible[0].parameters, Ambiguous); } // Internal use only /* * All methods in visible are acceptable matches for the method in * question... Since the receiver type is an interface, we ignore the * possibility that 2 inherited but unrelated superinterfaces may define the * same method in acceptable but not identical ways... we just take the best * match that we find since any class which implements the receiver * interface MUST implement all signatures for the method... in which case * the best match is correct. * * NOTE: This is different than javac... in the following example, the * message send of bar(X) in class Y is supposed to be ambiguous. But any * class which implements the interface I MUST implement both signatures for * bar. If this class was the receiver of the message send instead of the * interface I, then no problem would be reported. * * interface I1 { void bar(J j); } interface I2 { // void bar(J j); void * bar(Object o); } interface I extends I1, I2 {} interface J {} * * class X implements J {} * * class Y extends X { public void foo(I i, X x) { i.bar(x); } } */ protected final MethodBinding mostSpecificInterfaceMethodBinding( MethodBinding[] visible, int visibleSize) { MethodBinding method = null; nextVisible: for (int i = 0; i < visibleSize; i++) { method = visible[i]; for (int j = 0; j < visibleSize; j++) { if (i == j) continue; MethodBinding next = visible[j]; if (!areParametersAssignable(next.parameters, method.parameters)) continue nextVisible; } compilationUnitScope() .recordTypeReferences(method.thrownExceptions); return method; } return new ProblemMethodBinding(visible[0].selector, visible[0].parameters, Ambiguous); } public final ClassScope outerMostClassScope() { ClassScope lastClassScope = null; Scope scope = this; do { if (scope instanceof ClassScope) lastClassScope = (ClassScope) scope; scope = scope.parent; } while (scope != null); return lastClassScope; // may answer null if no class around } public final MethodScope outerMostMethodScope() { MethodScope lastMethodScope = null; Scope scope = this; do { if (scope instanceof MethodScope) lastMethodScope = (MethodScope) scope; scope = scope.parent; } while (scope != null); return lastMethodScope; // may answer null if no method around } public final CompilationUnitDeclaration referenceCompilationUnit() { Scope scope, unitScope = this; while ((scope = unitScope.parent) != null) unitScope = scope; return ((CompilationUnitScope) unitScope).referenceContext; } // start position in this scope - for ordering scopes vs. variables int startIndex() { return 0; } }