/******************************************************************************* * 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.impl.ReferenceContext; import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter; import net.sourceforge.phpdt.internal.compiler.util.ObjectVector; import net.sourceforge.phpeclipse.internal.compiler.ast.AbstractMethodDeclaration; import net.sourceforge.phpeclipse.internal.compiler.ast.CompilationUnitDeclaration; import net.sourceforge.phpeclipse.internal.compiler.ast.ImportReference; import net.sourceforge.phpeclipse.internal.compiler.ast.TypeDeclaration; 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; } 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; } }