/******************************************************************************* * Copyright (c) 2007, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.wst.jsdt.internal.compiler.lookup; import java.util.ArrayList; import java.util.Iterator; import java.util.Map; import org.eclipse.wst.jsdt.core.JavaScriptCore; import org.eclipse.wst.jsdt.core.compiler.CharOperation; import org.eclipse.wst.jsdt.core.infer.InferredType; import org.eclipse.wst.jsdt.internal.compiler.ast.ASTNode; import org.eclipse.wst.jsdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.wst.jsdt.internal.compiler.ast.CaseStatement; import org.eclipse.wst.jsdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.wst.jsdt.internal.compiler.ast.ImportReference; import org.eclipse.wst.jsdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.wst.jsdt.internal.compiler.impl.CompilerOptions; import org.eclipse.wst.jsdt.internal.compiler.impl.ReferenceContext; import org.eclipse.wst.jsdt.internal.compiler.problem.AbortCompilation; import org.eclipse.wst.jsdt.internal.compiler.problem.ProblemReporter; import org.eclipse.wst.jsdt.internal.compiler.util.HashtableOfObject; import org.eclipse.wst.jsdt.internal.compiler.util.ObjectVector; import org.eclipse.wst.jsdt.internal.compiler.util.SimpleSet; import org.eclipse.wst.jsdt.internal.core.Logger; public abstract class Scope implements TypeConstants, TypeIds { /* Scope kinds */ public final static int BLOCK_SCOPE = 1; public final static int CLASS_SCOPE = 3; public final static int COMPILATION_UNIT_SCOPE = 4; public final static int METHOD_SCOPE = 2; public final static int WITH_SCOPE = 5; /* Argument Compatibilities */ public final static int NOT_COMPATIBLE = -1; public final static int COMPATIBLE = 0; public final static int AUTOBOX_COMPATIBLE = 1; public final static int VARARGS_COMPATIBLE = 2; /* Type Compatibilities */ public static final int EQUAL_OR_MORE_SPECIFIC = -1; public static final int NOT_RELATED = 0; public static final int MORE_GENERIC = 1; public int kind; public Scope parent; /* Answer an int describing the relationship between the given types. * * NOT_RELATED * EQUAL_OR_MORE_SPECIFIC : left is compatible with right * MORE_GENERIC : right is compatible with left */ public static int compareTypes(TypeBinding left, TypeBinding right) { if (left.isCompatibleWith(right)) return Scope.EQUAL_OR_MORE_SPECIFIC; if (right.isCompatibleWith(left)) return Scope.MORE_GENERIC; return Scope.NOT_RELATED; } public static 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 TypeBinding.INT; break; case 'v' : if (length == 4 && name[1] == 'o' && name[2] == 'i' && name[3] == 'd') return TypeBinding.VOID; 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 TypeBinding.BOOLEAN; break; case 'c' : if (length == 4 && name[1] == 'h' && name[2] == 'a' && name[3] == 'r') return TypeBinding.CHAR; break; case 'd' : if (length == 6 && name[1] == 'o' && name[2] == 'u' && name[3] == 'b' && name[4] == 'l' && name[5] == 'e') return TypeBinding.DOUBLE; break; case 'f' : if (length == 5 && name[1] == 'l' && name[2] == 'o' && name[3] == 'a' && name[4] == 't') return TypeBinding.FLOAT; break; case 'l' : if (length == 4 && name[1] == 'o' && name[2] == 'n' && name[3] == 'g') return TypeBinding.LONG; break; case 's' : if (length == 5 && name[1] == 'h' && name[2] == 'o' && name[3] == 'r' && name[4] == 't') return TypeBinding.SHORT; } } return null; } protected Scope(int kind, Scope parent) { this.kind = kind; this.parent = parent; } /* * Boxing primitive */ public TypeBinding boxing(TypeBinding type) { if (type.isBaseType()) return environment().computeBoxingType(type); return type; } public ClassScope classScope() { Scope scope = this; do { if (scope instanceof ClassScope) return (ClassScope) scope; scope = scope.parent; } while (scope != null); return null; } public final CompilationUnitScope compilationUnitScope() { Scope lastScope = null; Scope scope = this; do { lastScope = scope; scope = scope.parent; } while (scope != null); return (CompilationUnitScope) lastScope; } /** * Finds the most specific compiler options */ public final CompilerOptions compilerOptions() { return compilationUnitScope().environment.globalOptions; } /** * Internal use only * Given a method, returns null if arguments cannot be converted to parameters. * Will answer a subsituted method in case the method was generic and type inference got triggered; * in case the method was originally compatible, then simply answer it back. */ protected final MethodBinding computeCompatibleMethod(MethodBinding method, TypeBinding[] arguments, InvocationSite invocationSite) { TypeBinding[] parameters = method.parameters; if (parameters == arguments // && (method.returnType.tagBits & TagBits.HasTypeVariable) == 0 ) return method; int argLength = arguments.length; int paramLength = parameters.length; boolean isVarArgs = method.isVarargs(); if (argLength != paramLength) if (!isVarArgs || argLength < paramLength - 1) return null; // incompatible if (parameterCompatibilityLevel(method, arguments) > NOT_COMPATIBLE) return method; return null; // incompatible } public ArrayBinding createArrayType(TypeBinding type, int dimension) { if (type.isValidBinding()) return environment().createArrayType(type, dimension); // do not cache obvious invalid types return new ArrayBinding(type, dimension, environment()); } 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; if (scope instanceof MethodScope) return (MethodScope) scope; while ((scope = scope.parent) != null) { if (scope instanceof MethodScope) return (MethodScope) scope; } return null; // may answer null if no method around } /* Answer the scope receiver type (could be parameterized) */ public final ReferenceBinding enclosingReceiverType() { Scope scope = this; AbstractMethodDeclaration inMethod =null; do { if (scope instanceof MethodScope) { MethodScope methodScope = (MethodScope) scope; inMethod = methodScope.referenceMethod(); if (inMethod.inferredMethod!=null && inMethod.inferredMethod.inType!=null && inMethod.inferredMethod.inType.binding!=null) return inMethod.inferredMethod.inType.binding; } else if (scope instanceof CompilationUnitScope) { CompilationUnitScope compilationUnitScope = (CompilationUnitScope) scope; for (int i=0;i<compilationUnitScope.referenceContext.numberInferredTypes;i++) { InferredType type= compilationUnitScope.referenceContext.inferredTypes[i]; if (type.containsMethod(inMethod)) return (ReferenceBinding)compilationUnitScope.getTypeOrPackage(type.getName(),Binding.TYPE); } } if (scope instanceof ClassScope) { ClassScope classScope=(ClassScope)scope; if (classScope.referenceContext!=null) return classScope.referenceContext.binding; if (classScope.inferredType!=null) return classScope.inferredType.binding; // return environment().convertToParameterizedType(((ClassScope) scope).referenceContext.binding); } scope = scope.parent; } while (scope != null); return null; } public final CompilationUnitBinding enclosingCompilationUnit() { Scope scope = this; do { if (scope instanceof CompilationUnitScope) { return ((CompilationUnitScope) scope).referenceContext.compilationUnitBinding; } scope = scope.parent; } while (scope != null); return null; } /** * Returns the immediately enclosing reference context, starting from current scope parent. * If starting on a class, it will skip current class. If starting on unitScope, returns null. */ public ReferenceContext enclosingReferenceContext() { Scope current = this; while ((current = current.parent) != null) { switch(current.kind) { case METHOD_SCOPE : return ((MethodScope) current).referenceContext; case CLASS_SCOPE : return ((ClassScope) current).referenceContext; case COMPILATION_UNIT_SCOPE : return ((CompilationUnitScope) current).referenceContext; } } return null; } /* Answer the scope enclosing source type (could be generic) */ public final SourceTypeBinding enclosingSourceType() { Scope scope = this; do { if (scope instanceof ClassScope) return ((ClassScope) scope).getReferenceBinding(); else if(scope instanceof CompilationUnitScope) return ((CompilationUnitScope) scope).referenceContext.compilationUnitBinding; scope = scope.parent; } while (scope != null); return null; } public final SourceTypeBinding enclosingTypeBinding() { Scope scope = this; do { if (scope instanceof ClassScope) return ((ClassScope) scope).getReferenceBinding(); else if (scope instanceof CompilationUnitScope) return ((CompilationUnitScope) scope).referenceContext.compilationUnitBinding; 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; } // abstract method lookup lookup (since maybe missing default abstract methods) protected MethodBinding findDefaultAbstractMethod( ReferenceBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite, ReferenceBinding classHierarchyStart, ObjectVector found, MethodBinding concreteMatch) { int startFoundSize = found.size; ReferenceBinding currentType = classHierarchyStart; while (currentType != null) { currentType = currentType.superclass(); } MethodBinding[] candidates = null; int candidatesCount = 0; MethodBinding problemMethod = null; int foundSize = found.size; if (foundSize > startFoundSize) { // argument type compatibility check for (int i = startFoundSize; i < foundSize; i++) { MethodBinding methodBinding = (MethodBinding) found.elementAt(i); MethodBinding compatibleMethod = computeCompatibleMethod(methodBinding, argumentTypes, invocationSite); if (compatibleMethod != null) { if (compatibleMethod.isValidBinding()) { if (candidatesCount == 0) { candidates = new MethodBinding[foundSize - startFoundSize + 1]; if (concreteMatch != null) candidates[candidatesCount++] = concreteMatch; } candidates[candidatesCount++] = compatibleMethod; } else if (problemMethod == null) { problemMethod = compatibleMethod; } } } } if (candidatesCount < 2) { if (concreteMatch == null) { if (candidatesCount == 0) return problemMethod; // can be null concreteMatch = candidates[0]; } return concreteMatch; } // no need to check for visibility - interface methods are public if (compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) return mostSpecificMethodBinding(candidates, candidatesCount, argumentTypes, invocationSite, receiverType); return null; } // Internal use only public ReferenceBinding findDirectMemberType(char[] typeName, ReferenceBinding enclosingType) { if ((enclosingType.tagBits & TagBits.HasNoMemberTypes) != 0) return null; // know it has no member types (nor inherited member types) ReferenceBinding enclosingReceiverType = enclosingReceiverType(); CompilationUnitScope unitScope = compilationUnitScope(); unitScope.recordReference(enclosingType, typeName); ReferenceBinding memberType = enclosingType.getMemberType(typeName); if (memberType != null) { unitScope.recordTypeReference(memberType); if (enclosingReceiverType == null ? memberType.canBeSeenBy(getCurrentPackage()) : memberType.canBeSeenBy(enclosingType, enclosingReceiverType)) return memberType; return new ProblemReferenceBinding(typeName, memberType, ProblemReasons.NotVisible); } return null; } // Internal use only public MethodBinding findExactMethod( ReferenceBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) { CompilationUnitScope unitScope = compilationUnitScope(); unitScope.recordTypeReferences(argumentTypes); MethodBinding exactMethod = (receiverType!=null) ? receiverType.getExactMethod(selector, argumentTypes, unitScope) : unitScope.referenceContext.compilationUnitBinding.getExactMethod(selector, argumentTypes, unitScope); if (exactMethod != null && !exactMethod.isBridge()) { // must find both methods for this case: <S extends A> void foo() {} and <N extends B> N foo() { return null; } // or find an inherited method when the exact match is to a bridge method // special treatment for Object.getClass() in 1.5 mode (substitute parameterized return type) if (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, boolean needResolve) { CompilationUnitScope unitScope = compilationUnitScope(); unitScope.recordTypeReference(receiverType); checkArrayField: { switch (receiverType.kind()) { case Binding.BASE_TYPE : return null; default: break checkArrayField; } } ReferenceBinding currentType = (ReferenceBinding) receiverType; if (!currentType.canBeSeenBy(this)) return new ProblemFieldBinding(currentType, fieldName, ProblemReasons.ReceiverTypeNotVisible); FieldBinding field = currentType.getField(fieldName, needResolve); if (field != null) { if (invocationSite == null ? field.canBeSeenBy(getCurrentPackage()) : field.canBeSeenBy(currentType, invocationSite, this)) return field; return new ProblemFieldBinding(field /* closest match*/, field.declaringClass, fieldName, ProblemReasons.NotVisible); } // collect all superinterfaces of receiverType until the field is found in a supertype int nextPosition = 0; FieldBinding visibleField = null; boolean keepLooking = true; FieldBinding notVisibleField = null; // we could hold onto the not visible field for extra error reporting while (keepLooking) { if (JavaScriptCore.IS_ECMASCRIPT4) { ((SourceTypeBinding) currentType).classScope.connectTypeHierarchy(); } if ((currentType = currentType.superclass()) == null) break; unitScope.recordTypeReference(currentType); if ((field = currentType.getField(fieldName, needResolve)) != null) { keepLooking = false; if (field.canBeSeenBy(receiverType, invocationSite, this)) { if (visibleField == null) visibleField = field; else return new ProblemFieldBinding(visibleField /* closest match*/, visibleField.declaringClass, fieldName, ProblemReasons.Ambiguous); } else { if (notVisibleField == null) notVisibleField = field; } } } if (visibleField != null) return visibleField; if (notVisibleField != null) { return new ProblemFieldBinding(notVisibleField, currentType, fieldName, ProblemReasons.NotVisible); } return null; } // Internal use only public ReferenceBinding findMemberType(char[] typeName, ReferenceBinding enclosingType) { if ((enclosingType.tagBits & TagBits.HasNoMemberTypes) != 0) return null; // know it has no member types (nor inherited member types) ReferenceBinding enclosingSourceType = enclosingSourceType(); PackageBinding currentPackage = getCurrentPackage(); CompilationUnitScope unitScope = compilationUnitScope(); unitScope.recordReference(enclosingType, typeName); ReferenceBinding memberType = enclosingType.getMemberType(typeName); if (memberType != null) { unitScope.recordTypeReference(memberType); if (enclosingSourceType == null ? memberType.canBeSeenBy(currentPackage) : memberType.canBeSeenBy(enclosingType, enclosingSourceType)) return memberType; return new ProblemReferenceBinding(typeName, memberType, ProblemReasons.NotVisible); } // collect all superinterfaces of receiverType until the memberType is found in a supertype ReferenceBinding currentType = enclosingType; ReferenceBinding[] interfacesToVisit = null; int nextPosition = 0; ReferenceBinding visibleMemberType = null; boolean keepLooking = true; ReferenceBinding notVisible = null; // we could hold onto the not visible field for extra error reporting while (keepLooking) { ReferenceBinding sourceType = currentType; if (sourceType.isHierarchyBeingConnected()) return null; // looking for an undefined member type in its own superclass ref ((SourceTypeBinding) sourceType).classScope.connectTypeHierarchy(); if ((currentType = currentType.superclass()) == null) break; unitScope.recordReference(currentType, typeName); if ((memberType = currentType.getMemberType(typeName)) != null) { unitScope.recordTypeReference(memberType); keepLooking = false; if (enclosingSourceType == null ? memberType.canBeSeenBy(currentPackage) : memberType.canBeSeenBy(enclosingType, enclosingSourceType)) { if (visibleMemberType == null) visibleMemberType = memberType; else return new ProblemReferenceBinding(typeName, visibleMemberType, ProblemReasons.Ambiguous); } else { notVisible = memberType; } } } // walk all visible interfaces to find ambiguous references if (interfacesToVisit != null) { ProblemReferenceBinding ambiguous = null; done : for (int i = 0; i < nextPosition; i++) { ReferenceBinding anInterface = interfacesToVisit[i]; unitScope.recordReference(anInterface, typeName); if ((memberType = anInterface.getMemberType(typeName)) != null) { unitScope.recordTypeReference(memberType); if (visibleMemberType == null) { visibleMemberType = memberType; } else { ambiguous = new ProblemReferenceBinding(typeName, visibleMemberType, ProblemReasons.Ambiguous); break done; } } } if (ambiguous != null) return ambiguous; } if (visibleMemberType != null) return visibleMemberType; if (notVisible != null) return new ProblemReferenceBinding(typeName, notVisible, ProblemReasons.NotVisible); return null; } // Internal use only - use findMethod() public MethodBinding findMethod(ReferenceBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) { ReferenceBinding currentType = receiverType; ObjectVector found = new ObjectVector(3); CompilationUnitScope unitScope = compilationUnitScope(); unitScope.recordTypeReferences(argumentTypes); if (receiverType==null) { MethodBinding methodBinding = unitScope.referenceContext.compilationUnitBinding.getExactMethod(selector,argumentTypes, unitScope); if (methodBinding==null) methodBinding= new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.NotFound); return methodBinding; } // superclass lookup long complianceLevel = compilerOptions().complianceLevel; boolean isCompliant14 = complianceLevel >= ClassFileConstants.JDK1_4; boolean isCompliant15 = complianceLevel >= ClassFileConstants.JDK1_5; ReferenceBinding classHierarchyStart = currentType; while (currentType != null) { unitScope.recordTypeReference(currentType); MethodBinding[] currentMethods = currentType.getMethods(selector); int currentLength = currentMethods.length; if (currentLength > 0) { if (isCompliant14 && (found.size > 0)) { nextMethod: for (int i = 0, l = currentLength; i < l; i++) { // currentLength can be modified inside the loop MethodBinding currentMethod = currentMethods[i]; if (currentMethod == null) continue nextMethod; // if 1.4 compliant, must filter out redundant protected methods from superclasses // 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; // BUT we can also ignore any overridden method since we already know the better match (fixes 80028) for (int j = 0, max = found.size; j < max; j++) { MethodBinding matchingMethod = (MethodBinding) found.elementAt(j); if (currentMethod.areParametersEqual(matchingMethod)) { if (isCompliant15) { if (matchingMethod.isBridge() && !currentMethod.isBridge()) continue nextMethod; // keep inherited methods to find concrete method over a bridge method } currentLength--; currentMethods[i] = null; continue nextMethod; } } } } if (currentLength > 0) { // append currentMethods, filtering out null entries if (currentMethods.length == currentLength) { 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(); } if (found.size==0 && (receiverType==null || receiverType instanceof CompilationUnitBinding)) { Binding binding = getTypeOrPackage(selector, Binding.METHOD); if (binding instanceof MethodBinding) { ((MethodBinding) binding).ensureBindingsAreComplete(); found.add(binding); } } // if found several candidates, then eliminate those not matching argument types int foundSize = found.size; MethodBinding[] candidates = null; int candidatesCount = 0; MethodBinding problemMethod = null; if (foundSize > 0) { // argument type compatibility check for (int i = 0; i < foundSize; i++) { MethodBinding methodBinding = (MethodBinding) found.elementAt(i); MethodBinding compatibleMethod = methodBinding;//computeCompatibleMethod(methodBinding, argumentTypes, invocationSite); if (compatibleMethod != null) { if (compatibleMethod.isValidBinding()) { if (foundSize == 1 && compatibleMethod.canBeSeenBy(receiverType, invocationSite, this)) { // return the single visible match now return compatibleMethod; } if (candidatesCount == 0) candidates = new MethodBinding[foundSize]; candidates[candidatesCount++] = compatibleMethod; } else if (problemMethod == null) { problemMethod = compatibleMethod; } } } } // no match was found if (candidatesCount == 0) { // abstract classes may get a match in interfaces; for non abstract // classes, reduces secondary errors since missing interface method // error is already reported MethodBinding interfaceMethod = findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, found, null); if (interfaceMethod != null) return interfaceMethod; if (found.size == 0) return null; if (problemMethod != null) return problemMethod; // still no match; try to find a close match when the parameter // order is wrong or missing some parameters // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=69471 // bad guesses are foo(), when argument types have been supplied // and foo(X, Y), when the argument types are (int, float, Y) // so answer the method with the most argType matches and least parameter type mismatches int bestArgMatches = -1; MethodBinding bestGuess = (MethodBinding) found.elementAt(0); // if no good match so just use the first one found 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; int argMatches = 0; next: for (int a = 0; a < argLength; a++) { TypeBinding arg = argumentTypes[a]; for (int p = a == 0 ? 0 : a - 1; p < paramLength && p < a + 1; p++) { // look one slot before & after to see if the type matches if (params[p] == arg) { argMatches++; continue next; } } } if (argMatches < bestArgMatches) continue nextMethod; if (argMatches == bestArgMatches) { int diff1 = paramLength < argLength ? 2 * (argLength - paramLength) : paramLength - argLength; int bestLength = bestGuess.parameters.length; int diff2 = bestLength < argLength ? 2 * (argLength - bestLength) : bestLength - argLength; if (diff1 >= diff2) continue nextMethod; } bestArgMatches = argMatches; bestGuess = methodBinding; } return bestGuess; // return new ProblemMethodBinding(bestGuess, bestGuess.selector, argumentTypes, ProblemReasons.NotFound); } // tiebreak using 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) { return candidates[0]; } if (visiblesCount == 0) { MethodBinding interfaceMethod = findDefaultAbstractMethod( receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, found, null); if (interfaceMethod != null) return interfaceMethod; return new ProblemMethodBinding(candidates[0], candidates[0].selector, candidates[0].parameters, ProblemReasons.NotVisible); } if (complianceLevel <= ClassFileConstants.JDK1_3) { ReferenceBinding declaringClass = candidates[0].declaringClass; return mostSpecificClassMethodBinding(candidates, visiblesCount, invocationSite); } MethodBinding mostSpecificMethod = mostSpecificMethodBinding(candidates, visiblesCount, argumentTypes, invocationSite, receiverType); return mostSpecificMethod; } // 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, Binding.NO_PARAMETERS, (ReferenceBinding)leafType, ProblemReasons.ReceiverTypeNotVisible); } ReferenceBinding object = getJavaLangObject(); MethodBinding methodBinding = object.getExactMethod(selector, argumentTypes, null); if (methodBinding != null) { // handle the method clone() specially... cannot be protected or throw exceptions if (argumentTypes == Binding.NO_PARAMETERS) { switch (selector[0]) { case 'c': break; case 'g': break; } } if (methodBinding.canBeSeenBy(receiverType, invocationSite, this)) return methodBinding; } methodBinding = findMethod(object, selector, argumentTypes, invocationSite); if (methodBinding == null) return new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.NotFound); return methodBinding; } // Internal use only public Binding findBinding( char[] typeName, int mask, PackageBinding declarationPackage, PackageBinding invocationPackage, boolean searchEnvironment) { compilationUnitScope().recordReference(declarationPackage.compoundName, typeName); Binding typeBinding = (searchEnvironment) ? declarationPackage.getBinding(typeName,mask) : declarationPackage.getBinding0(typeName, mask); if (typeBinding == null) return null; if (typeBinding.isValidBinding()) { if (declarationPackage != invocationPackage && typeBinding instanceof ReferenceBinding && !((ReferenceBinding)typeBinding).canBeSeenBy(invocationPackage)) return new ProblemReferenceBinding(typeName,(ReferenceBinding) typeBinding, ProblemReasons.NotVisible); } return typeBinding; } // Internal use only public ReferenceBinding findType( char[] typeName, PackageBinding declarationPackage, PackageBinding invocationPackage) { return (ReferenceBinding)findBinding(typeName, Binding.TYPE, declarationPackage, invocationPackage, true); } public LocalVariableBinding findVariable(char[] variable) { return null; } /* API * * Answer the binding that corresponds to the argument name. * flag is a mask of the following values VARIABLE (= FIELD or LOCAL), TYPE, PACKAGE. * Only bindings corresponding to the mask can be answered. * * For example, getBinding("foo", VARIABLE, site) will answer * the binding for the field or local named "foo" (or an error binding if none exists). * If a type named "foo" exists, it will not be detected (and an error binding will be answered) * * The VARIABLE mask has precedence over the TYPE mask. * * If the VARIABLE mask is not set, neither fields nor locals will be looked for. * * InvocationSite implements: * isSuperAccess(); this is used to determine if the discovered field is visible. * * Limitations: cannot request FIELD independently of LOCAL, or vice versa */ public Binding getBinding(char[] name, int mask, InvocationSite invocationSite, boolean needResolve) { CompilationUnitScope unitScope = compilationUnitScope(); LookupEnvironment env = unitScope.environment; try { env.missingClassFileLocation = invocationSite; Binding binding = null; FieldBinding problemField = null; if ((mask & Binding.VARIABLE) != 0) { boolean insideStaticContext = false; boolean insideConstructorCall = false; FieldBinding foundField = null; // can be a problem field which is answered if a valid field is not found ProblemFieldBinding foundInsideProblem = null; // inside Constructor call or inside static context Scope scope = this; int depth = 0; int foundDepth = 0; ReferenceBinding foundActualReceiverType = null; done : while (true) { // done when a COMPILATION_UNIT_SCOPE is found switch (scope.kind) { case METHOD_SCOPE : MethodScope methodScope = (MethodScope) scope; insideStaticContext |= methodScope.isStatic; insideConstructorCall |= methodScope.isConstructorCall; // Fall through... could duplicate the code below to save a cast - questionable optimization case BLOCK_SCOPE : LocalVariableBinding variableBinding = scope.findVariable(name); // looks in this scope only if (variableBinding != null) { if (foundField != null && foundField.isValidBinding()) return new ProblemFieldBinding( foundField, // closest match foundField.declaringClass, name, ProblemReasons.InheritedNameHidesEnclosingName); if (depth > 0) invocationSite.setDepth(depth); return variableBinding; } break; case CLASS_SCOPE : ClassScope classScope = (ClassScope) scope; ReferenceBinding receiverType = classScope.enclosingReceiverType(); FieldBinding fieldBinding = classScope.findField(receiverType, name, invocationSite, needResolve); // Use next line instead if willing to enable protected access accross inner types // FieldBinding fieldBinding = findField(enclosingType, name, invocationSite); if (fieldBinding != null) { // skip it if we did not find anything if (fieldBinding.problemId() == ProblemReasons.Ambiguous) { if (foundField == null || foundField.problemId() == ProblemReasons.NotVisible) // supercedes any potential InheritedNameHidesEnclosingName problem return fieldBinding; // make the user qualify the field, likely wants the first inherited field (javac generates an ambiguous error instead) return new ProblemFieldBinding( foundField, // closest match foundField.declaringClass, name, ProblemReasons.InheritedNameHidesEnclosingName); } ProblemFieldBinding insideProblem = null; if (fieldBinding.isValidBinding()) { if (!fieldBinding.isStatic()) { if (insideConstructorCall) { insideProblem = new ProblemFieldBinding( fieldBinding, // closest match fieldBinding.declaringClass, name, ProblemReasons.NonStaticReferenceInConstructorInvocation); } else if (insideStaticContext) { insideProblem = new ProblemFieldBinding( fieldBinding, // closest match fieldBinding.declaringClass, name, ProblemReasons.NonStaticReferenceInStaticContext); } } if (receiverType == fieldBinding.declaringClass || compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) { // found a valid field in the 'immediate' scope (ie. not inherited) // OR in 1.4 mode (inherited shadows enclosing) if (foundField == null) { if (depth > 0){ invocationSite.setDepth(depth); invocationSite.setActualReceiverType(receiverType); } // return the fieldBinding if it is not declared in a superclass of the scope's binding (that is, inherited) return insideProblem == null ? fieldBinding : insideProblem; } if (foundField.isValidBinding()) // if a valid field was found, complain when another is found in an 'immediate' enclosing type (that is, not inherited) if (foundField.declaringClass != fieldBinding.declaringClass) // ie. have we found the same field - do not trust field identity yet return new ProblemFieldBinding( foundField, // closest match foundField.declaringClass, name, ProblemReasons.InheritedNameHidesEnclosingName); } } if (foundField == null || (foundField.problemId() == ProblemReasons.NotVisible && fieldBinding.problemId() != ProblemReasons.NotVisible)) { // only remember the fieldBinding if its the first one found or the previous one was not visible & fieldBinding is... foundDepth = depth; foundActualReceiverType = receiverType; foundInsideProblem = insideProblem; foundField = fieldBinding; } } depth++; insideStaticContext |= receiverType.isStatic(); // 1EX5I8Z - accessing outer fields within a constructor call is permitted // in order to do so, we change the flag as we exit from the type, not the method // itself, because the class scope is used to retrieve the fields. MethodScope enclosingMethodScope = scope.methodScope(); insideConstructorCall = enclosingMethodScope == null ? false : enclosingMethodScope.isConstructorCall; break; case WITH_SCOPE : WithScope withScope = (WithScope) scope; TypeBinding withType = withScope.referenceContext; fieldBinding = withScope.findField(withType, name, invocationSite, needResolve); // Use next line instead if willing to enable protected access accross inner types // FieldBinding fieldBinding = findField(enclosingType, name, invocationSite); if (fieldBinding != null) { // skip it if we did not find anything if (fieldBinding.isValidBinding()) { return fieldBinding; } } break; case COMPILATION_UNIT_SCOPE : if ( (mask & (Binding.FIELD|Binding.VARIABLE)) >0) { variableBinding = scope.findVariable(name); // looks in this scope only if (variableBinding != null) { if (foundField != null && foundField.isValidBinding()) return new ProblemFieldBinding( foundField, // closest match foundField.declaringClass, name, ProblemReasons.InheritedNameHidesEnclosingName); if (depth > 0) invocationSite.setDepth(depth); return variableBinding; } if(unitScope.classScope()!=null) { //ReferenceBinding bind = env.getType(new char[][]{unitScope.superTypeName}); //if(bind==null) break done; foundField = (unitScope.classScope()).findField(unitScope.superBinding, name, invocationSite, true); if(foundField!=null && foundField.isValidBinding()) { return foundField; } } }else if ( (mask & (Binding.METHOD)) >0){ MethodBinding methodBinding = (unitScope.classScope()).findMethod(unitScope.superBinding, name, new TypeBinding[0], invocationSite); if(methodBinding!=null && methodBinding.isValidBinding()) return methodBinding; } break done; } scope = scope.parent; } if (foundInsideProblem != null) return foundInsideProblem; if (foundField != null) { if (foundField.isValidBinding()) { if (foundDepth > 0) { invocationSite.setDepth(foundDepth); invocationSite.setActualReceiverType(foundActualReceiverType); } return foundField; } problemField = foundField; foundField = null; } } if ( (mask&Binding.METHOD)!=0) { Scope scope = this; done : while (true) { // done when a COMPILATION_UNIT_SCOPE is found switch (scope.kind) { case METHOD_SCOPE : MethodScope methodScope = (MethodScope) scope; binding = methodScope.findMethod(name, Binding.NO_PARAMETERS,true); if (binding!=null) return binding; break; case WITH_SCOPE : WithScope withScope = (WithScope) scope; ReferenceBinding withType = withScope.referenceContext; // retrieve an exact visible match (if possible) // compilationUnitScope().recordTypeReference(receiverType); not needed since receiver is the source type MethodBinding methBinding = withScope.findExactMethod(withType, name, Binding.NO_PARAMETERS, invocationSite); if (methBinding == null) methBinding = withScope.findMethod(withType,name, Binding.NO_PARAMETERS, invocationSite); if (methBinding != null) { // skip it if we did not find anything if (methBinding.isValidBinding()) { return methBinding; } } break; case CLASS_SCOPE : ClassScope classScope = (ClassScope) scope; ReferenceBinding receiverType = classScope.enclosingReceiverType(); break; case COMPILATION_UNIT_SCOPE : CompilationUnitScope compilationUnitScope = (CompilationUnitScope) scope; CompilationUnitBinding compilationUnitBinding = compilationUnitScope.enclosingCompilationUnit(); receiverType = compilationUnitBinding; MethodBinding methodBinding = compilationUnitScope.findExactMethod(receiverType, name, Binding.NO_PARAMETERS, invocationSite); if (methodBinding != null) { // skip it if we did not find anything return methodBinding; } break done; } scope = scope.parent; } } // We did not find a local or instance variable. if ((mask & Binding.TYPE|Binding.VARIABLE|Binding.METHOD) != 0) { if ((mask & Binding.TYPE) != 0 && (binding = getBaseType(name)) != null) return binding; binding = getTypeOrPackage(name, mask);// (mask & Binding.PACKAGE) == 0 ? Binding.TYPE : Binding.TYPE | Binding.PACKAGE); if (binding.isValidBinding() || mask == Binding.TYPE) return binding; // answer the problem type binding if we are only looking for a type } else if ((mask & Binding.PACKAGE) != 0) { unitScope.recordSimpleReference(name); if ((binding = env.getTopLevelPackage(name)) != null) return binding; } if (problemField != null) return problemField; if (binding != null && binding.problemId() != ProblemReasons.NotFound) return binding; // answer the better problem binding return new ProblemBinding(name, enclosingTypeBinding(), ProblemReasons.NotFound); } catch (AbortCompilation e) { e.updateContext(invocationSite, referenceCompilationUnit().compilationResult); throw e; } finally { env.missingClassFileLocation = null; } } /* API * * Answer the binding that corresponds to the argument name. * flag is a mask of the following values VARIABLE (= FIELD or LOCAL), TYPE, PACKAGE. * Only bindings corresponding to the mask can be answered. * * For example, getBinding("foo", VARIABLE, site) will answer * the binding for the field or local named "foo" (or an error binding if none exists). * If a type named "foo" exists, it will not be detected (and an error binding will be answered) * * The VARIABLE mask has precedence over the TYPE mask. * * If the VARIABLE mask is not set, neither fields nor locals will be looked for. * * InvocationSite implements: * isSuperAccess(); this is used to determine if the discovered field is visible. * * Limitations: cannot request FIELD independently of LOCAL, or vice versa */ public Binding getLocalBinding(char[] name, int mask, InvocationSite invocationSite, boolean needResolve) { CompilationUnitScope unitScope = compilationUnitScope(); try { Binding binding = null; FieldBinding problemField = null; if ((mask & Binding.VARIABLE) != 0) { boolean insideStaticContext = false; boolean insideConstructorCall = false; FieldBinding foundField = null; // can be a problem field which is answered if a valid field is not found ProblemFieldBinding foundInsideProblem = null; // inside Constructor call or inside static context Scope scope = this; int depth = 0; int foundDepth = 0; ReferenceBinding foundActualReceiverType = null; done : while (true) { // done when a COMPILATION_UNIT_SCOPE is found switch (scope.kind) { case METHOD_SCOPE : MethodScope methodScope = (MethodScope) scope; insideStaticContext |= methodScope.isStatic; insideConstructorCall |= methodScope.isConstructorCall; // Fall through... could duplicate the code below to save a cast - questionable optimization case BLOCK_SCOPE : LocalVariableBinding variableBinding = scope.findVariable(name); // looks in this scope only if (variableBinding != null) { if (foundField != null && foundField.isValidBinding()) return new ProblemFieldBinding( foundField, // closest match foundField.declaringClass, name, ProblemReasons.InheritedNameHidesEnclosingName); if (depth > 0) invocationSite.setDepth(depth); return variableBinding; } break; case CLASS_SCOPE : ClassScope classScope = (ClassScope) scope; ReferenceBinding receiverType = classScope.enclosingReceiverType(); FieldBinding fieldBinding = classScope.findField(receiverType, name, invocationSite, needResolve); // Use next line instead if willing to enable protected access accross inner types // FieldBinding fieldBinding = findField(enclosingType, name, invocationSite); if (fieldBinding != null) { // skip it if we did not find anything if (fieldBinding.problemId() == ProblemReasons.Ambiguous) { if (foundField == null || foundField.problemId() == ProblemReasons.NotVisible) // supercedes any potential InheritedNameHidesEnclosingName problem return fieldBinding; // make the user qualify the field, likely wants the first inherited field (javac generates an ambiguous error instead) return new ProblemFieldBinding( foundField, // closest match foundField.declaringClass, name, ProblemReasons.InheritedNameHidesEnclosingName); } ProblemFieldBinding insideProblem = null; if (fieldBinding.isValidBinding()) { if (!fieldBinding.isStatic()) { if (insideConstructorCall) { insideProblem = new ProblemFieldBinding( fieldBinding, // closest match fieldBinding.declaringClass, name, ProblemReasons.NonStaticReferenceInConstructorInvocation); } else if (insideStaticContext) { insideProblem = new ProblemFieldBinding( fieldBinding, // closest match fieldBinding.declaringClass, name, ProblemReasons.NonStaticReferenceInStaticContext); } } if (receiverType == fieldBinding.declaringClass || compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) { // found a valid field in the 'immediate' scope (ie. not inherited) // OR in 1.4 mode (inherited shadows enclosing) if (foundField == null) { if (depth > 0){ invocationSite.setDepth(depth); invocationSite.setActualReceiverType(receiverType); } // return the fieldBinding if it is not declared in a superclass of the scope's binding (that is, inherited) return insideProblem == null ? fieldBinding : insideProblem; } if (foundField.isValidBinding()) // if a valid field was found, complain when another is found in an 'immediate' enclosing type (that is, not inherited) if (foundField.declaringClass != fieldBinding.declaringClass) // ie. have we found the same field - do not trust field identity yet return new ProblemFieldBinding( foundField, // closest match foundField.declaringClass, name, ProblemReasons.InheritedNameHidesEnclosingName); } } if (foundField == null || (foundField.problemId() == ProblemReasons.NotVisible && fieldBinding.problemId() != ProblemReasons.NotVisible)) { // only remember the fieldBinding if its the first one found or the previous one was not visible & fieldBinding is... foundDepth = depth; foundActualReceiverType = receiverType; foundInsideProblem = insideProblem; foundField = fieldBinding; } } depth++; insideStaticContext |= receiverType.isStatic(); // 1EX5I8Z - accessing outer fields within a constructor call is permitted // in order to do so, we change the flag as we exit from the type, not the method // itself, because the class scope is used to retrieve the fields. MethodScope enclosingMethodScope = scope.methodScope(); insideConstructorCall = enclosingMethodScope == null ? false : enclosingMethodScope.isConstructorCall; break; case WITH_SCOPE : { WithScope withScope = (WithScope) scope; TypeBinding withType = withScope.referenceContext; FieldBinding withBinding = withScope.findField(withType, name, invocationSite, needResolve); // Use next line instead if willing to enable protected access accross inner types // FieldBinding fieldBinding = findField(enclosingType, name, invocationSite); if (withBinding != null) { // skip it if we did not find anything if (withBinding.isValidBinding()) { return withBinding; } } } break; case COMPILATION_UNIT_SCOPE : if ( (mask & (Binding.FIELD|Binding.VARIABLE)) >0) { variableBinding = scope.findVariable(name); // looks in this scope only if (variableBinding != null) { if (foundField != null && foundField.isValidBinding()) return new ProblemFieldBinding( foundField, // closest match foundField.declaringClass, name, ProblemReasons.InheritedNameHidesEnclosingName); if (depth > 0) invocationSite.setDepth(depth); return variableBinding; } if(unitScope.classScope()!=null) { //ReferenceBinding bind = env.getType(new char[][]{unitScope.superTypeName}); //if(bind==null) break done; foundField = (unitScope.classScope()).findField(unitScope.superBinding, name, invocationSite, true); if(foundField!=null && foundField.isValidBinding()) { return foundField; } } }else if ( (mask & (Binding.METHOD)) >0){ MethodBinding methodBinding = (unitScope.classScope()).findMethod(unitScope.superBinding, name, new TypeBinding[0], invocationSite); if(methodBinding!=null && methodBinding.isValidBinding()) return methodBinding; } break done; } scope = scope.parent; } if (foundInsideProblem != null) return foundInsideProblem; if (foundField != null) { if (foundField.isValidBinding()) { if (foundDepth > 0) { invocationSite.setDepth(foundDepth); invocationSite.setActualReceiverType(foundActualReceiverType); } return foundField; } problemField = foundField; foundField = null; } } if ( (mask&Binding.METHOD)!=0) { MethodBinding methodBinding = findMethod(null, name, Binding.NO_PARAMETERS, invocationSite); if (methodBinding!=null && methodBinding.isValidBinding()) return methodBinding; } if (problemField != null) return problemField; if (binding != null && binding.problemId() != ProblemReasons.NotFound) return binding; // answer the better problem binding return new ProblemBinding(name, enclosingTypeBinding(), ProblemReasons.NotFound); } catch (AbortCompilation e) { e.updateContext(invocationSite, referenceCompilationUnit().compilationResult); throw e; } finally { } } /** * <p><b>NOTE:</b> This function does not validate the given argument types because any number of arguments * can be passed to any JavaScript function or constructor.</p> * * @param receiverType * @param argumentTypes * @param invocationSite * @return The constructor for the given receiver type or a {@link ProblemMethodBinding} if the * constructor is not visible. */ public MethodBinding getConstructor(ReferenceBinding receiverType, TypeBinding[] argumentTypes, InvocationSite invocationSite) { CompilationUnitScope unitScope = compilationUnitScope(); LookupEnvironment env = unitScope.environment; try { env.missingClassFileLocation = invocationSite; unitScope.recordTypeReference(receiverType); unitScope.recordTypeReferences(argumentTypes); MethodBinding methodBinding = receiverType.getExactConstructor(argumentTypes); if (methodBinding != null && methodBinding.canBeSeenBy(invocationSite, this)) { return methodBinding; } //get the methods MethodBinding[] methods = receiverType.getMethods(receiverType.sourceName); MethodBinding constructor = null; if (methods == null || methods == Binding.NO_METHODS || methods.length == 0){ constructor = new MethodBinding(0, receiverType.sourceName, receiverType, null,receiverType); } else { if(methods.length > 1) { Logger.log(Logger.WARNING, "There should only ever be one match for a constructor search" + " but found " + methods.length + " when looking for " + new String(receiverType.sourceName) + ". Using the first match."); } //should only ever be one constructor so use the first one in the list constructor = methods[0]; } //if can't be seen return problem binding if(!constructor.canBeSeenBy(invocationSite, this)) { constructor = new ProblemMethodBinding( methods[0], methods[0].selector, methods[0].parameters, ProblemReasons.NotVisible); } return constructor; } catch (AbortCompilation e) { e.updateContext(invocationSite, referenceCompilationUnit().compilationResult); throw e; } finally { env.missingClassFileLocation = null; } } public final PackageBinding getCurrentPackage() { Scope scope, unitScope = this; while ((scope = unitScope.parent) != null) unitScope = scope; return ((CompilationUnitScope) unitScope).getDefaultPackage(); } /** * 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 FieldBinding getField(TypeBinding receiverType, char[] fieldName, InvocationSite invocationSite) { LookupEnvironment env = environment(); try { env.missingClassFileLocation = invocationSite; FieldBinding field = findField(receiverType, fieldName, invocationSite, true /*resolve*/); if (field != null) return field; return new ProblemFieldBinding( receiverType instanceof ReferenceBinding ? (ReferenceBinding) receiverType : null, fieldName, ProblemReasons.NotFound); } catch (AbortCompilation e) { e.updateContext(invocationSite, referenceCompilationUnit().compilationResult); throw e; } finally { env.missingClassFileLocation = null; } } public Binding getFieldOrMethod( TypeBinding receiverType, char[] fieldName, InvocationSite invocationSite ) { LookupEnvironment env = environment(); try { env.missingClassFileLocation = invocationSite; //first look for field FieldBinding field = findField(receiverType, fieldName, invocationSite, true /*resolve*/); if (field != null) return field; /* not sure if this fix is correct, but reciever type is [sometimes] coming in as "BaseTypeBinding" and causing a classcastexception */ MethodBinding method = findMethod( receiverType instanceof ReferenceBinding?(ReferenceBinding)receiverType:null, fieldName, new TypeBinding[0], invocationSite ); if( method != null ) { if (!method.isValidBinding()) { if (method.problemId()!=ProblemReasons.NotFound) return method; } else return method; } return new ProblemFieldBinding( receiverType instanceof ReferenceBinding ? (ReferenceBinding) receiverType : null, fieldName, ProblemReasons.NotFound); } catch (AbortCompilation e) { e.updateContext(invocationSite, referenceCompilationUnit().compilationResult); throw e; } finally { env.missingClassFileLocation = null; } } /* API * * Answer the method binding that corresponds to selector, argumentTypes. * Start the lookup at the enclosing type of the receiver. * InvocationSite implements * isSuperAccess(); this is used to determine if the discovered method is visible. * setDepth(int); this is used to record the depth of the discovered method * relative to the enclosing type of the receiver. (If the method is defined * in the enclosing type of the receiver, the depth is 0; in the next enclosing * type, the depth is 1; and so on * * If no visible method is discovered, an error binding is answered. */ public MethodBinding getImplicitMethod(char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) { boolean insideStaticContext = false; boolean insideConstructorCall = false; MethodBinding foundMethod = null; MethodBinding foundProblem = null; boolean foundProblemVisible = false; Scope scope = this; int depth = 0; // in 1.4 mode (inherited visible shadows enclosing) CompilerOptions options; boolean inheritedHasPrecedence = (options = compilerOptions()).complianceLevel >= ClassFileConstants.JDK1_4; done : while (true) { // done when a COMPILATION_UNIT_SCOPE is found switch (scope.kind) { case METHOD_SCOPE : MethodScope methodScope = (MethodScope) scope; insideStaticContext |= methodScope.isStatic; insideConstructorCall |= methodScope.isConstructorCall; MethodBinding binding = methodScope.findMethod(selector,argumentTypes,true); if (binding!=null) return binding; LocalVariableBinding variable = methodScope.findVariable(selector); if (variable!=null) { } break; case WITH_SCOPE : WithScope withScope = (WithScope) scope; ReferenceBinding withType = withScope.referenceContext; // retrieve an exact visible match (if possible) // compilationUnitScope().recordTypeReference(receiverType); not needed since receiver is the source type MethodBinding methBinding = withScope.findExactMethod(withType, selector, argumentTypes, invocationSite); if (methBinding == null) methBinding = withScope.findMethod(withType, selector, argumentTypes, invocationSite); if (methBinding != null) { // skip it if we did not find anything if (methBinding.isValidBinding()) { return methBinding; } } break; case CLASS_SCOPE : ClassScope classScope = (ClassScope) scope; ReferenceBinding receiverType = classScope.enclosingReceiverType(); // retrieve an exact visible match (if possible) // compilationUnitScope().recordTypeReference(receiverType); not needed since receiver is the source type MethodBinding methodBinding = classScope.findExactMethod(receiverType, selector, argumentTypes, invocationSite); if (methodBinding == null) methodBinding = classScope.findMethod(receiverType, selector, argumentTypes, invocationSite); if (methodBinding != null) { // skip it if we did not find anything if (foundMethod == null) { if (methodBinding.isValidBinding()) { if (!methodBinding.isStatic() && (insideConstructorCall || insideStaticContext)) { if (foundProblem != null && foundProblem.problemId() != ProblemReasons.NotVisible) return foundProblem; // takes precedence return new ProblemMethodBinding( methodBinding, // closest match methodBinding.selector, methodBinding.parameters, insideConstructorCall ? ProblemReasons.NonStaticReferenceInConstructorInvocation : ProblemReasons.NonStaticReferenceInStaticContext); } if (inheritedHasPrecedence || receiverType == methodBinding.declaringClass || (receiverType.getMethods(selector)) != Binding.NO_METHODS) { // found a valid method in the 'immediate' scope (ie. not inherited) // OR in 1.4 mode (inherited visible shadows enclosing) // OR the receiverType implemented a method with the correct name // return the methodBinding if it is not declared in a superclass of the scope's binding (that is, inherited) if (foundProblemVisible) { return foundProblem; } if (depth > 0) { invocationSite.setDepth(depth); invocationSite.setActualReceiverType(receiverType); } return methodBinding; } if (foundProblem == null || foundProblem.problemId() == ProblemReasons.NotVisible) { if (foundProblem != null) foundProblem = null; // only remember the methodBinding if its the first one found // remember that private methods are visible if defined directly by an enclosing class if (depth > 0) { invocationSite.setDepth(depth); invocationSite.setActualReceiverType(receiverType); } foundMethod = methodBinding; } } else { // methodBinding is a problem method if (methodBinding.problemId() != ProblemReasons.NotVisible && methodBinding.problemId() != ProblemReasons.NotFound) return methodBinding; // return the error now if (foundProblem == null) { foundProblem = methodBinding; // hold onto the first not visible/found error and keep the second not found if first is not visible } if (! foundProblemVisible && methodBinding.problemId() == ProblemReasons.NotFound) { MethodBinding closestMatch = ((ProblemMethodBinding) methodBinding).closestMatch; if (closestMatch != null && closestMatch.canBeSeenBy(receiverType, invocationSite, this)) { foundProblem = methodBinding; // hold onto the first not visible/found error and keep the second not found if first is not visible foundProblemVisible = true; } } } } else { // found a valid method so check to see if this is a hiding case if (methodBinding.problemId() == ProblemReasons.Ambiguous || (foundMethod.declaringClass != methodBinding.declaringClass && (receiverType == methodBinding.declaringClass || receiverType.getMethods(selector) != Binding.NO_METHODS))) // ambiguous case -> must qualify the method (javac generates an ambiguous error instead) // otherwise if a method was found, complain when another is found in an 'immediate' enclosing type (that is, not inherited) // NOTE: Unlike fields, a non visible method hides a visible method return new ProblemMethodBinding( methodBinding, // closest match selector, argumentTypes, ProblemReasons.InheritedNameHidesEnclosingName); } } depth++; insideStaticContext |= receiverType.isStatic(); // 1EX5I8Z - accessing outer fields within a constructor call is permitted // in order to do so, we change the flag as we exit from the type, not the method // itself, because the class scope is used to retrieve the fields. MethodScope enclosingMethodScope = scope.methodScope(); insideConstructorCall = enclosingMethodScope == null ? false : enclosingMethodScope.isConstructorCall; break; case COMPILATION_UNIT_SCOPE : CompilationUnitScope compilationUnitScope = (CompilationUnitScope) scope; CompilationUnitBinding compilationUnitBinding = compilationUnitScope.enclosingCompilationUnit(); receiverType = compilationUnitBinding; methodBinding = (foundMethod == null) ? compilationUnitScope.findExactMethod(receiverType, selector, argumentTypes, invocationSite) : compilationUnitScope.findExactMethod(receiverType, foundMethod.selector, foundMethod.parameters, invocationSite); if (methodBinding == null) methodBinding = compilationUnitScope.findMethod(receiverType, selector, argumentTypes, invocationSite); if (methodBinding == null) methodBinding = compilationUnitScope.findMethod(selector, argumentTypes,true); if (methodBinding != null) { // skip it if we did not find anything if (methodBinding.problemId() == ProblemReasons.Ambiguous) { if (foundMethod == null || foundMethod.problemId() == ProblemReasons.NotVisible) { // supercedes any potential InheritedNameHidesEnclosingName problem return methodBinding; } // make the user qualify the method, likely wants the first inherited method (javac generates an ambiguous error instead) return new ProblemMethodBinding( methodBinding, // closest match selector, argumentTypes, ProblemReasons.InheritedNameHidesEnclosingName); } MethodBinding fuzzyProblem = null; MethodBinding insideProblem = null; if (foundMethod == null) { if (receiverType == methodBinding.declaringClass || (receiverType.getMethods(selector)) != Binding.NO_METHODS || ((foundProblem == null || foundProblem.problemId() != ProblemReasons.NotVisible) && compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4)) { // found a valid method in the 'immediate' scope (ie. not inherited) // OR the receiverType implemented a method with the correct name // OR in 1.4 mode (inherited visible shadows enclosing) if (depth > 0) { invocationSite.setDepth(depth); invocationSite.setActualReceiverType(receiverType); } // return the methodBinding if it is not declared in a superclass of the scope's binding (that is, inherited) if (foundProblem != null && foundProblem.problemId() != ProblemReasons.NotVisible) return foundProblem; if (insideProblem != null) return insideProblem; return methodBinding; } } if (foundMethod == null || (foundMethod.problemId() == ProblemReasons.NotVisible && methodBinding.problemId() != ProblemReasons.NotVisible)) { // only remember the methodBinding if its the first one found or the previous one was not visible & methodBinding is... // remember that private methods are visible if defined directly by an enclosing class if (depth > 0) { invocationSite.setDepth(depth); invocationSite.setActualReceiverType(receiverType); } foundProblem = fuzzyProblem; foundProblem = insideProblem; if (fuzzyProblem == null) foundMethod = methodBinding; // only keep it if no error was found } } depth++; insideStaticContext |= receiverType.isStatic(); break done; } scope = scope.parent; } if (insideStaticContext && options.sourceLevel >= ClassFileConstants.JDK1_5) { if (foundProblem != null) { if (foundProblem.declaringClass != null && foundProblem.declaringClass.id == TypeIds.T_JavaLangObject) return foundProblem; // static imports lose to methods from Object if (foundProblem.problemId() == ProblemReasons.NotFound && foundProblemVisible) { return foundProblem; // visible method selectors take precedence } } } if (foundMethod != null) { invocationSite.setActualReceiverType(foundMethod.declaringClass); return foundMethod; } if (foundProblem != null) return foundProblem; return new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.NotFound); } public final ReferenceBinding getJavaLangAssertionError() { CompilationUnitScope unitScope = compilationUnitScope(); unitScope.recordQualifiedReference(JAVA_LANG_ASSERTIONERROR); return unitScope.environment.getResolvedType(JAVA_LANG_ASSERTIONERROR, this); } public final ReferenceBinding getJavaLangClass() { CompilationUnitScope unitScope = compilationUnitScope(); unitScope.recordQualifiedReference(JAVA_LANG_CLASS); return unitScope.environment.getResolvedType(JAVA_LANG_CLASS, this); } public final ReferenceBinding getJavaLangIterable() { CompilationUnitScope unitScope = compilationUnitScope(); unitScope.recordQualifiedReference(JAVA_LANG_ITERABLE); return unitScope.environment.getResolvedType(JAVA_LANG_ITERABLE, this); } public final ReferenceBinding getJavaLangObject() { CompilationUnitScope unitScope = compilationUnitScope(); unitScope.recordQualifiedReference(JAVA_LANG_OBJECT); return unitScope.environment.getResolvedType(JAVA_LANG_OBJECT, this); } public final ReferenceBinding getJavaLangArray() { compilationUnitScope().recordQualifiedReference(ARRAY); return environment().getResolvedType(ARRAY, this); } public final ReferenceBinding getJavaLangString() { compilationUnitScope().recordQualifiedReference(JAVA_LANG_STRING); return environment().getResolvedType(JAVA_LANG_STRING, this); } public final ReferenceBinding getJavaLangNumber() { compilationUnitScope().recordQualifiedReference(NUMBER); return environment().getResolvedType(NUMBER, this); } public final ReferenceBinding getJavaLangFunction() { compilationUnitScope().recordQualifiedReference(FUNCTION); return environment().getResolvedType(FUNCTION, this); } public final ReferenceBinding getJavaLangBoolean() { compilationUnitScope().recordQualifiedReference(BOOLEAN_OBJECT); return environment().getResolvedType(BOOLEAN_OBJECT, this); } public final ReferenceBinding getJavaLangThrowable() { CompilationUnitScope unitScope = compilationUnitScope(); unitScope.recordQualifiedReference(JAVA_LANG_THROWABLE); return unitScope.environment.getResolvedType(JAVA_LANG_THROWABLE, this); } public final ReferenceBinding getJavaLangError() { CompilationUnitScope unitScope = compilationUnitScope(); unitScope.recordQualifiedReference(ERROR); return unitScope.environment.getResolvedType(ERROR, this); } public final ReferenceBinding getJavaLangRegExp() { CompilationUnitScope unitScope = compilationUnitScope(); unitScope.recordQualifiedReference(REGEXP); return unitScope.environment.getResolvedType(REGEXP, this); } /* 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, null, ProblemReasons.NotFound); } public MethodBinding getMethod(TypeBinding receiverType, char[] selector, TypeBinding[] argumentTypes, InvocationSite invocationSite) { CompilationUnitScope unitScope = compilationUnitScope(); LookupEnvironment env = unitScope.environment; try { env.missingClassFileLocation = invocationSite; if (receiverType==null) return new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.NotFound); switch (receiverType.kind()) { case Binding.BASE_TYPE : return new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.NotFound); // case Binding.ARRAY_TYPE : // unitScope.recordTypeReference(receiverType); // return findMethodForArray((ArrayBinding) receiverType, selector, argumentTypes, invocationSite); } unitScope.recordTypeReference(receiverType); ReferenceBinding currentType = (ReferenceBinding) receiverType; if (!currentType.canBeSeenBy(this)) return new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.ReceiverTypeNotVisible); // retrieve an exact visible match (if possible) MethodBinding methodBinding = findExactMethod(currentType, selector, argumentTypes, invocationSite); if (methodBinding != null) return methodBinding; methodBinding = findMethod(currentType, selector, argumentTypes, invocationSite); if (methodBinding == null) return new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.NotFound); if (!methodBinding.isValidBinding()) return methodBinding; return methodBinding; } catch (AbortCompilation e) { e.updateContext(invocationSite, referenceCompilationUnit().compilationResult); throw e; } finally { env.missingClassFileLocation = null; } } /* Answer the package from the compoundName or null if it begins with a type. * Intended to be used while resolving a qualified type name. * * 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 Binding getPackage(char[][] compoundName) { compilationUnitScope().recordQualifiedReference(compoundName); Binding binding = getTypeOrPackage(compoundName[0], Binding.TYPE | Binding.PACKAGE); if (binding == null) return new ProblemReferenceBinding(compoundName[0], null, ProblemReasons.NotFound); if (!binding.isValidBinding()) return binding; if (!(binding instanceof PackageBinding)) return null; // compoundName does not start with a package int currentIndex = 1; PackageBinding packageBinding = (PackageBinding) binding; while (currentIndex < compoundName.length) { binding = packageBinding.getTypeOrPackage(compoundName[currentIndex++], Binding.PACKAGE); if (binding == null) return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), null, ProblemReasons.NotFound); if (!binding.isValidBinding()) return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), binding instanceof ReferenceBinding ? ((ReferenceBinding)binding).closestMatch() : null, binding.problemId()); if (!(binding instanceof PackageBinding)) return packageBinding; packageBinding = (PackageBinding) binding; } return new ProblemReferenceBinding(compoundName, null, ProblemReasons.NotFound); } /* 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, Binding.TYPE); } /* Answer the type binding that corresponds to the given name, starting the lookup in the receiver * or the packageBinding if provided. * The name provided is a simple source name (e.g., "Object" , "Point", ...) */ public final TypeBinding getType(char[] name, PackageBinding packageBinding) { if (packageBinding == null) return getType(name); Binding binding = packageBinding.getTypeOrPackage(name, Binding.TYPE); if (binding == null) return new ProblemReferenceBinding( CharOperation.arrayConcat(packageBinding.compoundName, name), null, ProblemReasons.NotFound); if (!binding.isValidBinding()) return new ProblemReferenceBinding( CharOperation.arrayConcat(packageBinding.compoundName, name), binding instanceof ReferenceBinding ? ((ReferenceBinding)binding).closestMatch() : null, binding.problemId()); ReferenceBinding typeBinding = (ReferenceBinding) binding; if (!typeBinding.canBeSeenBy(this)) return new ProblemReferenceBinding( CharOperation.arrayConcat(packageBinding.compoundName, name), typeBinding, ProblemReasons.NotVisible); return typeBinding; } /* 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) { 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 unitScope = compilationUnitScope(); unitScope.recordQualifiedReference(compoundName); Binding binding = getTypeOrPackage(compoundName[0], typeNameLength == 1 ? Binding.TYPE : Binding.TYPE | Binding.PACKAGE); if (binding == null) return new ProblemReferenceBinding(compoundName[0], null, ProblemReasons.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++], Binding.TYPE); // does not check visibility if (binding == null) return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), null, ProblemReasons.NotFound); if (!binding.isValidBinding()) return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), binding instanceof ReferenceBinding ? ((ReferenceBinding)binding).closestMatch() : null, binding.problemId()); if (!(binding instanceof PackageBinding)) break; packageBinding = (PackageBinding) binding; } if (binding instanceof PackageBinding) return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), null, ProblemReasons.NotFound); checkVisibility = true; } // binding is now a ReferenceBinding ReferenceBinding typeBinding = (ReferenceBinding) binding; unitScope.recordTypeReference(typeBinding); if (checkVisibility) // handles the fall through case if (!typeBinding.canBeSeenBy(this)) return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), typeBinding, ProblemReasons.NotVisible); while (currentIndex < typeNameLength) { typeBinding = getMemberType(compoundName[currentIndex++], typeBinding); if (!typeBinding.isValidBinding()) { if (typeBinding instanceof ProblemReferenceBinding) { ProblemReferenceBinding problemBinding = (ProblemReferenceBinding) typeBinding; return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), problemBinding.closestMatch(), typeBinding.problemId()); } return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), ((ReferenceBinding)binding).closestMatch(), typeBinding.problemId()); } } return typeBinding; } /* Internal use only */ final Binding getTypeOrPackage(char[] name, int mask) { Scope scope = this; Binding foundType = null; boolean insideStaticContext = false; if ((mask & Binding.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 : MethodScope methodScope = (MethodScope) scope; insideStaticContext |= methodScope.isStatic; case BLOCK_SCOPE : ReferenceBinding localType = ((BlockScope) scope).findLocalType(name); // looks in this scope only if (localType != null) { return localType; } break; case CLASS_SCOPE : SourceTypeBinding sourceType = ((ClassScope) scope).getReferenceBinding(); // type variables take precedence over the source type, ex. class X <X> extends X == class X <Y> extends Y // but not when we step out to the enclosing type if (CharOperation.equals(name, sourceType.sourceName)) return sourceType; insideStaticContext |= sourceType.isStatic(); break; case COMPILATION_UNIT_SCOPE : break done; } scope = scope.parent; } } // at this point the scope is a compilation unit scope CompilationUnitScope unitScope = (CompilationUnitScope) scope; HashtableOfObject typeOrPackageCache = unitScope.typeOrPackageCache; if (typeOrPackageCache != null) { Binding binding = (Binding) typeOrPackageCache.get(name); if (binding != null) { // can also include NotFound ProblemReferenceBindings if we already know this name is not found if (binding instanceof ImportBinding) { // single type import cached in faultInImports(), replace it in the cache with the type ImportReference importReference = ((ImportBinding) binding).reference; if (importReference != null) importReference.bits |= ASTNode.Used; if (binding instanceof ImportConflictBinding) typeOrPackageCache.put(name, binding = ((ImportConflictBinding) binding).conflictingTypeBinding); // already know its visible else typeOrPackageCache.put(name, binding = ((ImportBinding) binding).resolvedImport); // already know its visible } if ((mask & Binding.TYPE) != 0) { if (binding instanceof ReferenceBinding) return binding; // cached type found in previous walk below } if ((mask & Binding.PACKAGE) != 0 && binding instanceof PackageBinding) return binding; // cached package found in previous walk below } } // ask for the imports + name if ((mask & Binding.TYPE|Binding.VARIABLE|Binding.METHOD) != 0) { ImportBinding[] imports = unitScope.imports; if (imports != null && typeOrPackageCache == null) { // walk single type imports since faultInImports() has not run yet nextImport : for (int i = 0, length = imports.length; i < length; i++) { ImportBinding importBinding = imports[i]; if (!importBinding.onDemand) { if (CharOperation.equals(importBinding.compoundName[importBinding.compoundName.length - 1], name)) { Binding resolvedImport = unitScope.resolveSingleImport(importBinding); if (resolvedImport == null) continue nextImport; if (resolvedImport instanceof MethodBinding) { resolvedImport = getType(importBinding.compoundName, importBinding.compoundName.length); if (!resolvedImport.isValidBinding()) continue nextImport; } if (resolvedImport instanceof TypeBinding) { ImportReference importReference = importBinding.reference; if (importReference != null) importReference.bits |= ASTNode.Used; return resolvedImport; // already know its visible } } } } } // check on file imports if (imports != null) { for (int i = 0, length = imports.length; i < length; i++) { ImportBinding someImport = imports[i]; if (someImport.reference!=null && someImport.reference.isFileImport()) { Binding resolvedImport = someImport.resolvedImport; Binding temp = null; if (resolvedImport instanceof CompilationUnitBinding) { CompilationUnitBinding compilationUnitBinding =(CompilationUnitBinding)resolvedImport; temp = findBinding(name, mask, compilationUnitBinding.getPackage(), unitScope.getDefaultPackage(), false); if (temp!=null && temp.isValidBinding()) { ImportReference importReference = someImport.reference; importReference.bits |= ASTNode.Used; if (typeOrPackageCache != null) typeOrPackageCache.put(name, temp); return temp; // type is always visible to its own package } } } } } // check if the name is in the current package, skip it if its a sub-package PackageBinding currentPackage = unitScope.getDefaultPackage(); unitScope.recordReference(currentPackage.compoundName, name); Binding binding=currentPackage.getTypeOrPackage(name, mask); if ( (binding instanceof ReferenceBinding || binding instanceof MethodBinding) && !(binding instanceof ProblemReferenceBinding)) { if (typeOrPackageCache != null) typeOrPackageCache.put(name, binding); return binding; // type is always visible to its own package } else if (binding instanceof LocalVariableBinding && binding.isValidBinding()) { compilationUnitScope().addExternalVar((LocalVariableBinding)binding); return binding; } // check on demand imports if (imports != null) { boolean foundInImport = false; Binding type = null; for (int i = 0, length = imports.length; i < length; i++) { ImportBinding someImport = imports[i]; if (someImport.onDemand) { Binding resolvedImport = someImport.resolvedImport; Binding temp = null; if (resolvedImport instanceof PackageBinding) { temp = findBinding(name, mask, (PackageBinding) resolvedImport, currentPackage, false); } else { temp = findDirectMemberType(name, (ReferenceBinding) resolvedImport); } if (temp != type && temp != null) { if (temp.isValidBinding()) { ImportReference importReference = someImport.reference; if (importReference != null) importReference.bits |= ASTNode.Used; if (foundInImport) { // Answer error binding -- import on demand conflict; name found in two import on demand packages. temp = new ProblemReferenceBinding(name, null, ProblemReasons.Ambiguous); if (typeOrPackageCache != null) typeOrPackageCache.put(name, temp); return temp; } type = temp; foundInImport = true; } else if (foundType == null) { foundType = temp; } } } } if (type != null) { if (typeOrPackageCache != null) typeOrPackageCache.put(name, type); return type; } } } unitScope.recordSimpleReference(name); if ((mask & Binding.PACKAGE) != 0) { PackageBinding packageBinding = unitScope.environment.getTopLevelPackage(name); if (packageBinding != null) { if (typeOrPackageCache != null) typeOrPackageCache.put(name, packageBinding); return packageBinding; } } // Answer error binding -- could not find name if (foundType == null) { foundType = new ProblemReferenceBinding(name, null, ProblemReasons.NotFound); if (typeOrPackageCache != null && (mask & Binding.PACKAGE) != 0) // only put NotFound type in cache if you know its not a package typeOrPackageCache.put(name, foundType); } return foundType; } // Added for code assist... NOT Public API // DO NOT USE to resolve import references since this method assumes 'A.B' is relative to a single type import of 'p1.A' // when it may actually mean the type B in the package A // use CompilationUnitScope.getImport(char[][]) instead public final Binding getTypeOrPackage(char[][] compoundName) { return getTypeOrPackage(compoundName,Binding.TYPE | Binding.PACKAGE); } public final Binding getTypeOrPackage(char[][] compoundName, int mask) { int nameLength = compoundName.length; if (nameLength == 1) { TypeBinding binding = getBaseType(compoundName[0]); if (binding != null) return binding; } Binding binding = getTypeOrPackage(compoundName[0], Binding.TYPE | Binding.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++], mask); if (binding == null) return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), null, ProblemReasons.NotFound); if (!binding.isValidBinding()) return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), binding instanceof ReferenceBinding ? ((ReferenceBinding)binding).closestMatch() : null, 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; ReferenceBinding qualifiedType = typeBinding; if (checkVisibility) // handles the fall through case if (!typeBinding.canBeSeenBy(this)) return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), typeBinding, ProblemReasons.NotVisible); while (currentIndex < nameLength) { typeBinding = getMemberType(compoundName[currentIndex++], typeBinding); // checks visibility if (!typeBinding.isValidBinding()) return new ProblemReferenceBinding( CharOperation.subarray(compoundName, 0, currentIndex), ((ReferenceBinding)binding).closestMatch(), typeBinding.problemId()); qualifiedType = typeBinding; } return qualifiedType; } protected boolean hasErasedCandidatesCollisions(TypeBinding one, TypeBinding two, Map invocations, ReferenceBinding type, ASTNode typeRef) { invocations.clear(); TypeBinding[] mecs = minimalErasedCandidates(new TypeBinding[] {one, two}, invocations); if (mecs != null) { nextCandidate: for (int k = 0, max = mecs.length; k < max; k++) { TypeBinding mec = mecs[k]; if (mec == null) continue nextCandidate; Object value = invocations.get(mec); if (value instanceof TypeBinding[]) { TypeBinding[] invalidInvocations = (TypeBinding[]) value; type.tagBits |= TagBits.HierarchyHasProblems; return true; } } } return false; } /** * Returns the immediately enclosing switchCase statement (carried by closest blockScope), */ public CaseStatement innermostSwitchCase() { Scope scope = this; do { if (scope instanceof BlockScope) return ((BlockScope) scope).enclosingCase; scope = scope.parent; } while (scope != null); return null; } protected boolean isAcceptableMethod(MethodBinding one, MethodBinding two) { TypeBinding[] oneParams = one.parameters; TypeBinding[] twoParams = two.parameters; int oneParamsLength = oneParams.length; int twoParamsLength = twoParams.length; if (oneParamsLength == twoParamsLength) { for (int i = 0; i < oneParamsLength; i++) { TypeBinding oneParam = oneParams[i]; TypeBinding twoParam = twoParams[i]; if (oneParam == twoParam) { continue; } if (oneParam.isCompatibleWith(twoParam)) { } else { if (i == oneParamsLength - 1 && one.isVarargs() && two.isVarargs()) { TypeBinding eType = ((ArrayBinding) twoParam).elementsType(); if (oneParam == eType || oneParam.isCompatibleWith(eType)) return true; // special case to choose between 2 varargs methods when the last arg is Object[] } return false; } } return true; } if (one.isVarargs() && two.isVarargs()) { if (oneParamsLength > twoParamsLength) { // special case when autoboxing makes (int, int...) better than (Object...) but not (int...) or (Integer, int...) if (((ArrayBinding) twoParams[twoParamsLength - 1]).elementsType().id != TypeIds.T_JavaLangObject) return false; } // check that each parameter before the vararg parameters are compatible (no autoboxing allowed here) for (int i = (oneParamsLength > twoParamsLength ? twoParamsLength : oneParamsLength) - 2; i >= 0; i--) if (oneParams[i] != twoParams[i] && !oneParams[i].isCompatibleWith(twoParams[i])) return false; if (parameterCompatibilityLevel(one, twoParams) == NOT_COMPATIBLE && parameterCompatibilityLevel(two, oneParams) == VARARGS_COMPATIBLE) return true; } return false; } public boolean isBoxingCompatibleWith(TypeBinding expressionType, TypeBinding targetType) { LookupEnvironment environment = environment(); if (environment.globalOptions.sourceLevel < ClassFileConstants.JDK1_5 || expressionType.isBaseType() == targetType.isBaseType()) return false; // check if autoboxed type is compatible TypeBinding convertedType = environment.computeBoxingType(expressionType); return convertedType == targetType || convertedType.isCompatibleWith(targetType); } /* 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; if (methodScope.initializedField == field) 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) if (((AbstractMethodDeclaration) refContext).binding == method) return true; } scope = scope.parent; } while (scope != null); return false; } /* 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 type declaration */ public final boolean isDefinedInType(ReferenceBinding type) { Scope scope = this; do { if (scope instanceof ClassScope) if (((ClassScope) scope).getReferenceBinding() == type) return true; scope = scope.parent; } while (scope != null); return false; } /** * Returns true if the scope or one of its parent is associated to a given caseStatement, denoting * being part of a given switch case statement. */ public boolean isInsideCase(CaseStatement caseStatement) { Scope scope = this; do { switch (scope.kind) { case Scope.BLOCK_SCOPE : if (((BlockScope) scope).enclosingCase == caseStatement) { return true; } } scope = scope.parent; } while (scope != null); return false; } public boolean isInsideDeprecatedCode(){ 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 && context.isViewedAsDeprecated()) return true; } else { SourceTypeBinding type = ((BlockScope)this).referenceType().binding; // inside field declaration ? check field modifier to see if deprecated if (methodScope.initializedField != null && methodScope.initializedField.isViewedAsDeprecated()) return true; if (type != null) { if (type.isViewedAsDeprecated()) return true; } } break; case Scope.CLASS_SCOPE : ReferenceBinding context = ((ClassScope)this).referenceType().binding; if (context != null) { if (context.isViewedAsDeprecated()) return true; } break; case Scope.COMPILATION_UNIT_SCOPE : // consider import as being deprecated if first type is itself deprecated (123522) CompilationUnitDeclaration unit = referenceCompilationUnit(); if (unit.types != null && unit.types.length > 0) { SourceTypeBinding type = unit.types[0].binding; if (type != null) { if (type.isViewedAsDeprecated()) return true; } } } return false; } public MethodScope methodScope() { Scope scope = this; do { if (scope instanceof MethodScope) return (MethodScope) scope; scope = scope.parent; } while (scope != null); return null; } /** * Returns the most specific set of types compatible with all given types. * (i.e. most specific common super types) * If no types is given, will return an empty array. If not compatible * reference type is found, returns null. In other cases, will return an array * of minimal erased types, where some nulls may appear (and must simply be * ignored). */ protected TypeBinding[] minimalErasedCandidates(TypeBinding[] types, Map allInvocations) { int length = types.length; int indexOfFirst = -1, actualLength = 0; for (int i = 0; i < length; i++) { TypeBinding type = types[i]; if (type == null) continue; if (type.isBaseType()) return null; if (indexOfFirst < 0) indexOfFirst = i; actualLength ++; } switch (actualLength) { case 0: return Binding.NO_TYPES; case 1: return types; } TypeBinding firstType = types[indexOfFirst]; if (firstType.isBaseType()) return null; // record all supertypes of type // intersect with all supertypes of otherType ArrayList typesToVisit = new ArrayList(5); int dim = firstType.dimensions(); TypeBinding leafType = firstType.leafComponentType(); TypeBinding firstErasure = firstType; if (firstErasure != firstType) { allInvocations.put(firstErasure, firstType); } typesToVisit.add(firstType); int max = 1; ReferenceBinding currentType; for (int i = 0; i < max; i++) { TypeBinding typeToVisit = (TypeBinding) typesToVisit.get(i); dim = typeToVisit.dimensions(); if (dim > 0) { leafType = typeToVisit.leafComponentType(); switch(leafType.id) { case T_JavaLangObject: if (dim > 1) { // Object[][] supertype is Object[] TypeBinding elementType = ((ArrayBinding)typeToVisit).elementsType(); if (!typesToVisit.contains(elementType)) { typesToVisit.add(elementType); max++; } continue; } // fallthrough case T_short: case T_char: case T_boolean: case T_int: case T_long: case T_float: case T_double: TypeBinding superType = getJavaLangObject(); if (!typesToVisit.contains(superType)) { typesToVisit.add(superType); max++; } continue; default: } typeToVisit = leafType; } currentType = (ReferenceBinding) typeToVisit; TypeBinding itsSuperclass = currentType.superclass(); if (itsSuperclass != null) { TypeBinding superType = dim == 0 ? itsSuperclass : (TypeBinding)environment().createArrayType(itsSuperclass, dim); // recreate array if needed if (!typesToVisit.contains(superType)) { typesToVisit.add(superType); max++; TypeBinding superTypeErasure = superType; if (superTypeErasure != superType) { allInvocations.put(superTypeErasure, superType); } } } } int superLength = typesToVisit.size(); TypeBinding[] erasedSuperTypes = new TypeBinding[superLength]; int rank = 0; for (Iterator iter = typesToVisit.iterator(); iter.hasNext();) { TypeBinding type = (TypeBinding)iter.next(); leafType = type.leafComponentType(); erasedSuperTypes[rank++] = type; } // intersecting first type supertypes with other types' ones, nullifying non matching supertypes int remaining = superLength; nextOtherType: for (int i = indexOfFirst+1; i < length; i++) { TypeBinding otherType = types[i]; if (otherType == null) continue nextOtherType; if (otherType.isArrayType()) { nextSuperType: for (int j = 0; j < superLength; j++) { TypeBinding erasedSuperType = erasedSuperTypes[j]; if (erasedSuperType == null || erasedSuperType == otherType) continue nextSuperType; TypeBinding match; if ((match = otherType.findSuperTypeWithSameErasure(erasedSuperType)) == null) { erasedSuperTypes[j] = null; if (--remaining == 0) return null; continue nextSuperType; } // record invocation Object invocationData = allInvocations.get(erasedSuperType); if (invocationData == null) { allInvocations.put(erasedSuperType, match); // no array for singleton } else if (invocationData instanceof TypeBinding) { if (match != invocationData) { // using an array to record invocations in order (188103) TypeBinding[] someInvocations = { (TypeBinding) invocationData, match, }; allInvocations.put(erasedSuperType, someInvocations); } } else { // using an array to record invocations in order (188103) TypeBinding[] someInvocations = (TypeBinding[]) invocationData; checkExisting: { int invocLength = someInvocations.length; for (int k = 0; k < invocLength; k++) { if (someInvocations[k] == match) break checkExisting; } System.arraycopy(someInvocations, 0, someInvocations = new TypeBinding[invocLength+1], 0, invocLength); allInvocations.put(erasedSuperType, someInvocations); someInvocations[invocLength] = match; } } } continue nextOtherType; } nextSuperType: for (int j = 0; j < superLength; j++) { TypeBinding erasedSuperType = erasedSuperTypes[j]; if (erasedSuperType == null) continue nextSuperType; TypeBinding match; if (erasedSuperType == otherType) { match = erasedSuperType; } else { if (erasedSuperType.isArrayType()) { match = null; } else { match = otherType.findSuperTypeWithSameErasure(erasedSuperType); } if (match == null) { // incompatible super type erasedSuperTypes[j] = null; if (--remaining == 0) return null; continue nextSuperType; } } // record invocation Object invocationData = allInvocations.get(erasedSuperType); if (invocationData == null) { allInvocations.put(erasedSuperType, match); // no array for singleton } else if (invocationData instanceof TypeBinding) { if (match != invocationData) { // using an array to record invocations in order (188103) TypeBinding[] someInvocations = { (TypeBinding) invocationData, match, }; allInvocations.put(erasedSuperType, someInvocations); } } else { // using an array to record invocations in order (188103) TypeBinding[] someInvocations = (TypeBinding[]) invocationData; checkExisting: { int invocLength = someInvocations.length; for (int k = 0; k < invocLength; k++) { if (someInvocations[k] == match) break checkExisting; } System.arraycopy(someInvocations, 0, someInvocations = new TypeBinding[invocLength+1], 0, invocLength); allInvocations.put(erasedSuperType, someInvocations); someInvocations[invocLength] = match; } } } } // eliminate non minimal super types if (remaining > 1) { nextType: for (int i = 0; i < superLength; i++) { TypeBinding erasedSuperType = erasedSuperTypes[i]; if (erasedSuperType == null) continue nextType; nextOtherType: for (int j = 0; j < superLength; j++) { if (i == j) continue nextOtherType; TypeBinding otherType = erasedSuperTypes[j]; if (otherType == null) continue nextOtherType; if (erasedSuperType instanceof ReferenceBinding) { if (erasedSuperType.findSuperTypeWithSameErasure(otherType) != null) { erasedSuperTypes[j] = null; // discard non minimal supertype remaining--; } } else if (erasedSuperType.isArrayType()) { if (erasedSuperType.findSuperTypeWithSameErasure(otherType) != null) { erasedSuperTypes[j] = null; // discard non minimal supertype remaining--; } } } } } return erasedSuperTypes; } // 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, InvocationSite invocationSite) { MethodBinding previous = null; nextVisible : for (int i = 0; i < visibleSize; i++) { MethodBinding method = visible[i]; if (previous != null && method.declaringClass != previous.declaringClass) break; // cannot answer a method farther up the hierarchy than the first method found if (!method.isStatic()) previous = method; // no ambiguity for static methods for (int j = 0; j < visibleSize; j++) { if (i == j) continue; if (!visible[j].areParametersCompatibleWith(method.parameters)) continue nextVisible; } return method; } return new ProblemMethodBinding(visible[0], visible[0].selector, visible[0].parameters, ProblemReasons.Ambiguous); } // caveat: this is not a direct implementation of JLS protected final MethodBinding mostSpecificMethodBinding(MethodBinding[] visible, int visibleSize, TypeBinding[] argumentTypes, InvocationSite invocationSite, ReferenceBinding receiverType) { int[] compatibilityLevels = new int[visibleSize]; for (int i = 0; i < visibleSize; i++) compatibilityLevels[i] = parameterCompatibilityLevel(visible[i], argumentTypes); MethodBinding[] moreSpecific = new MethodBinding[visibleSize]; int count = 0; for (int level = 0, max = VARARGS_COMPATIBLE; level <= max; level++) { nextVisible : for (int i = 0; i < visibleSize; i++) { if (compatibilityLevels[i] != level) continue nextVisible; max = level; // do not examine further categories, will either return mostSpecific or report ambiguous case MethodBinding current = visible[i]; MethodBinding original = current.original(); MethodBinding tiebreakMethod = current.tiebreakMethod(); for (int j = 0; j < visibleSize; j++) { if (i == j || compatibilityLevels[j] != level) continue; MethodBinding next = visible[j]; if (original == next.original()) { // parameterized superclasses & interfaces may be walked twice from different paths so skip next from now on compatibilityLevels[j] = -1; continue; } MethodBinding methodToTest = next; MethodBinding acceptable = computeCompatibleMethod(methodToTest, tiebreakMethod.parameters, invocationSite); /* There are 4 choices to consider with current & next : foo(B) & foo(A) where B extends A 1. the 2 methods are equal (both accept each others parameters) -> want to continue 2. current has more specific parameters than next (so acceptable is a valid method) -> want to continue 3. current has less specific parameters than next (so acceptable is null) -> go on to next 4. current and next are not compatible with each other (so acceptable is null) -> go on to next */ if (acceptable == null || !acceptable.isValidBinding()) continue nextVisible; if (!isAcceptableMethod(tiebreakMethod, acceptable)) continue nextVisible; // pick a concrete method over a bridge method when parameters are equal since the return type of the concrete method is more specific if (current.isBridge() && !next.isBridge()) if (tiebreakMethod.areParametersEqual(acceptable)) continue nextVisible; // skip current so acceptable wins over this bridge method } moreSpecific[i] = current; count++; } } if (count == 1) { for (int i = 0; i < visibleSize; i++) { if (moreSpecific[i] != null) { return visible[i]; } } } else if (count == 0) { return new ProblemMethodBinding(visible[0], visible[0].selector, visible[0].parameters, ProblemReasons.Ambiguous); } // found several methods that are mutually acceptable -> must be equal // so now with the first acceptable method, find the 'correct' inherited method for each other acceptable method AND // see if they are equal after substitution of type variables (do the type variables have to be equal to be considered an override???) nextSpecific : for (int i = 0; i < visibleSize; i++) { MethodBinding current = moreSpecific[i]; if (current != null) { ReferenceBinding[] mostSpecificExceptions = null; SimpleSet possibleMethods = null; MethodBinding original = current.original(); for (int j = 0; j < visibleSize; j++) { MethodBinding next = moreSpecific[j]; if (next == null || i == j) continue; MethodBinding original2 = next.original(); if (original.declaringClass == original2.declaringClass) break nextSpecific; // duplicates thru substitution if (!original.isAbstract()) { if (original2.isAbstract()) continue; // only compare current against other concrete methods TypeBinding superType = original.declaringClass.findSuperTypeWithSameErasure(original2.declaringClass); if (superType == null) continue nextSpecific; // current's declaringClass is not a subtype of next's declaringClass } else if (receiverType != null) { // should not be null if original isAbstract, but be safe TypeBinding superType = receiverType.findSuperTypeWithSameErasure(original.declaringClass); if (original.declaringClass == superType || !(superType instanceof ReferenceBinding)) { // keep original } else { // must find inherited method with the same substituted variables MethodBinding[] superMethods = ((ReferenceBinding) superType).getMethods(original.selector); for (int m = 0, l = superMethods.length; m < l; m++) { if (superMethods[m].original() == original) { original = superMethods[m]; break; } } } superType = receiverType.findSuperTypeWithSameErasure(original2.declaringClass); if (original2.declaringClass == superType || !(superType instanceof ReferenceBinding)) { // keep original2 } else { // must find inherited method with the same substituted variables MethodBinding[] superMethods = ((ReferenceBinding) superType).getMethods(original2.selector); for (int m = 0, l = superMethods.length; m < l; m++) { if (superMethods[m].original() == original2) { original2 = superMethods[m]; break; } } } if (original2 == null || !original.areParametersEqual(original2)) continue nextSpecific; // current does not override next if (!original.returnType.isCompatibleWith(original2.returnType) && !original.returnType.isCompatibleWith(original2.returnType)) { // 15.12.2 continue nextSpecific; // choose original2 instead } } } if (mostSpecificExceptions != null) { Object[] values = possibleMethods.values; int exceptionLength = mostSpecificExceptions.length; nextMethod : for (int p = 0, vLength = values.length; p < vLength; p++) { MethodBinding possible = (MethodBinding) values[p]; if (possible == null) continue nextMethod; if (0 == exceptionLength) { nextException : for (int e = 0; e < exceptionLength; e++) { ReferenceBinding exception = null; for (int f = 0; f < exceptionLength; f++) if (exception == mostSpecificExceptions[f]) continue nextException; continue nextMethod; } return possible; } } // do not return a new methodBinding until we know that it does not cause problems // return new FunctionBinding( // current.modifiers, // current.selector, // current.returnType, // current.parameters, // mostSpecificExceptions, // current.declaringClass // ); } return current; } } // if all moreSpecific methods are equal then see if duplicates exist because of substitution return new ProblemMethodBinding(visible[0], visible[0].selector, visible[0].parameters, ProblemReasons.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 int parameterCompatibilityLevel(MethodBinding method, TypeBinding[] arguments) { TypeBinding[] parameters = method.parameters; int paramLength = parameters.length; int argLength = arguments.length; if (compilerOptions().sourceLevel < ClassFileConstants.JDK1_5) { if (paramLength != argLength) return NOT_COMPATIBLE; for (int i = 0; i < argLength; i++) { TypeBinding param = parameters[i]; TypeBinding arg = arguments[i]; if (arg != param && !arg.isCompatibleWith(param)) return NOT_COMPATIBLE; } return COMPATIBLE; } int level = COMPATIBLE; // no autoboxing or varargs support needed int lastIndex = argLength; LookupEnvironment env = environment(); if (method.isVarargs()) { lastIndex = paramLength - 1; if (paramLength == argLength) { // accept X or X[] but not X[][] TypeBinding param = parameters[lastIndex]; // is an ArrayBinding by definition TypeBinding arg = arguments[lastIndex]; if (param != arg) { level = parameterCompatibilityLevel(arg, param, env); if (level == NOT_COMPATIBLE) { // expect X[], is it called with X param = ((ArrayBinding) param).elementsType(); if (parameterCompatibilityLevel(arg, param, env) == NOT_COMPATIBLE) return NOT_COMPATIBLE; level = VARARGS_COMPATIBLE; // varargs support needed } } } else { if (paramLength < argLength) { // all remaining argument types must be compatible with the elementsType of varArgType TypeBinding param = ((ArrayBinding) parameters[lastIndex]).elementsType(); for (int i = lastIndex; i < argLength; i++) { TypeBinding arg = arguments[i]; if (param != arg && parameterCompatibilityLevel(arg, param, env) == NOT_COMPATIBLE) return NOT_COMPATIBLE; } } else if (lastIndex != argLength) { // can call foo(int i, X ... x) with foo(1) but NOT foo(); return NOT_COMPATIBLE; } level = VARARGS_COMPATIBLE; // varargs support needed } } else if (paramLength != argLength) { return NOT_COMPATIBLE; } // now compare standard arguments from 0 to lastIndex for (int i = 0; i < lastIndex; i++) { TypeBinding param = parameters[i]; TypeBinding arg = arguments[i]; if (arg != param) { int newLevel = parameterCompatibilityLevel(arg, param, env); if (newLevel == NOT_COMPATIBLE) return NOT_COMPATIBLE; if (newLevel > level) level = newLevel; } } return level; } private int parameterCompatibilityLevel(TypeBinding arg, TypeBinding param, LookupEnvironment env) { // only called if env.options.sourceLevel >= ClassFileConstants.JDK1_5 if (arg.isCompatibleWith(param)) return COMPATIBLE; if (arg.isBaseType() != param.isBaseType()) { TypeBinding convertedType = env.computeBoxingType(arg); if (convertedType == param || convertedType.isCompatibleWith(param)) return AUTOBOX_COMPATIBLE; } return NOT_COMPATIBLE; } public abstract ProblemReporter problemReporter(); public final CompilationUnitDeclaration referenceCompilationUnit() { Scope scope, unitScope = this; while ((scope = unitScope.parent) != null) unitScope = scope; return ((CompilationUnitScope) unitScope).referenceContext; } /** * Returns the nearest reference context, starting from current scope. * If starting on a class, it will return current class. If starting on unitScope, returns unit. */ public ReferenceContext referenceContext() { Scope current = this; do { switch(current.kind) { case METHOD_SCOPE : return ((MethodScope) current).referenceContext; case CLASS_SCOPE : return ((ClassScope) current).referenceContext; case COMPILATION_UNIT_SCOPE : return ((CompilationUnitScope) current).referenceContext; } } while ((current = current.parent) != null); return null; } // start position in this scope - for ordering scopes vs. variables int startIndex() { return 0; } }