Logo Search packages:      
Sourcecode: ecj version File versions

Scope.java

/*******************************************************************************
 * Copyright (c) 2000, 2006 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;

import java.util.*;

import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.*;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
import org.eclipse.jdt.internal.compiler.util.ObjectVector;
import org.eclipse.jdt.internal.compiler.util.SimpleSet;

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;

      /* 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;
                              if (length == 4 && name[1] == 'y' && name[2] == 't' && name[3] == 'e')
                                    return TypeBinding.BYTE;
                              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;
      }

   // 5.1.10
      public static ReferenceBinding[] greaterLowerBound(ReferenceBinding[] types) {
            if (types == null) return null;
            int length = types.length;
            if (length == 0) return null;
            ReferenceBinding[] result = types;
            int removed = 0;
            for (int i = 0; i < length; i++) {
                  ReferenceBinding iType = result[i];
                  if (iType == null) continue;
                  for (int j = 0; j < length; j++) {
                        if (i == j) continue;
                        ReferenceBinding jType = result[j];
                        if (jType == null) continue;
                        if (iType.isCompatibleWith(jType)) { // if Vi <: Vj, Vj is removed
                              if (result == types) { // defensive copy
                                    System.arraycopy(result, 0, result = new ReferenceBinding[length], 0, length);
                              }
                              result[j] = null;
                              removed ++;
                        }
                  }
            }
            if (removed == 0) return result;
            if (length == removed) return null;
            ReferenceBinding[] trimmedResult = new ReferenceBinding[length - removed];
            for (int i = 0, index = 0; i < length; i++) {
                  ReferenceBinding iType = result[i];
                  if (iType != null) {
                        trimmedResult[index++] = iType;
                  }
            }
            return trimmedResult;
      }
      
      // 5.1.10
      public static TypeBinding[] greaterLowerBound(TypeBinding[] types) {
            if (types == null) return null;
            int length = types.length;
            if (length == 0) return null;
            TypeBinding[] result = types;
            int removed = 0;
            for (int i = 0; i < length; i++) {
                  TypeBinding iType = result[i];
                  if (iType == null) continue;
                  for (int j = 0; j < length; j++) {
                        if (i == j) continue;
                        TypeBinding jType = result[j];
                        if (jType == null) continue;
                        if (iType.isCompatibleWith(jType)) { // if Vi <: Vj, Vj is removed
                              if (result == types) { // defensive copy
                                    System.arraycopy(result, 0, result = new TypeBinding[length], 0, length);
                              }
                              result[j] = null;
                              removed ++;
                        }
                  }
            }
            if (removed == 0) return result;
            if (length == removed) return null;
            TypeBinding[] trimmedResult = new TypeBinding[length - removed];
            for (int i = 0, index = 0; i < length; i++) {
                  TypeBinding iType = result[i];
                  if (iType != null) {
                        trimmedResult[index++] = iType;
                  }
            }
            return trimmedResult;
      }
      
      /**
       * Returns an array of types, where original types got substituted given a substitution.
       * Only allocate an array if anything is different.
       */
      public static ReferenceBinding[] substitute(Substitution substitution, ReferenceBinding[] originalTypes) {
            if (originalTypes == null) return null;
          ReferenceBinding[] substitutedTypes = originalTypes;
          for (int i = 0, length = originalTypes.length; i < length; i++) {
              ReferenceBinding originalType = originalTypes[i];
              ReferenceBinding substitutedParameter = (ReferenceBinding)substitute(substitution, originalType);
              if (substitutedParameter != originalType) {
                  if (substitutedTypes == originalTypes) {
                      System.arraycopy(originalTypes, 0, substitutedTypes = new ReferenceBinding[length], 0, i);
                  }
                  substitutedTypes[i] = substitutedParameter;
              } else if (substitutedTypes != originalTypes) {
                  substitutedTypes[i] = originalType;
              }
          }
          return substitutedTypes;
      }

      /**
       * Returns a type, where original type was substituted using the receiver
       * parameterized type.
       * In raw mode, all parameterized type denoting same original type are converted
       * to raw types. e.g. 
       * class X <T> {
       *   X<T> foo;
       *   X<String> bar;
       * } when used in raw fashion, then type of both foo and bar is raw type X.
       * 
       */
      public static TypeBinding substitute(Substitution substitution, TypeBinding originalType) {
            if (originalType == null) return null;
            switch (originalType.kind()) {
                  
                  case Binding.TYPE_PARAMETER:
                        return substitution.substitute((TypeVariableBinding) originalType);
                        
                  case Binding.PARAMETERIZED_TYPE:
                        ParameterizedTypeBinding originalParameterizedType = (ParameterizedTypeBinding) originalType;
                        ReferenceBinding originalEnclosing = originalType.enclosingType();
                        ReferenceBinding substitutedEnclosing = originalEnclosing;
                        if (originalEnclosing != null) {
                              substitutedEnclosing = (ReferenceBinding) substitute(substitution, originalEnclosing);
                        }
                        TypeBinding[] originalArguments = originalParameterizedType.arguments;
                        TypeBinding[] substitutedArguments = originalArguments;
                        if (originalArguments != null) {
                              if (substitution.isRawSubstitution()) {
                                    return originalParameterizedType.environment.createRawType(originalParameterizedType.type, substitutedEnclosing);
                              }
                              substitutedArguments = substitute(substitution, originalArguments);
                        }
                        if (substitutedArguments != originalArguments || substitutedEnclosing != originalEnclosing) {
//                            identicalVariables: { // if substituted with original variables, then answer the generic type itself
//                                  if (substitutedEnclosing != null) {
//                                        //if (!(substitutedEnclosing instanceof SourceTypeBinding)) break identicalVariables;
//                                        if (substitutedEnclosing != originalEnclosing) break identicalVariables;                                    
//                                  }
//                                  if (originalParameterizedType.type.isBinaryBinding()) break identicalVariables; // generic binary is never used as is, see 85262
//                                  TypeVariableBinding[] originalVariables = originalParameterizedType.type.typeVariables();
//                                  for (int i = 0, length = originalVariables.length; i < length; i++) {
//                                        if (substitutedArguments[i] != originalVariables[i]) break identicalVariables;
//                                  }
//                                  return originalParameterizedType.type;
//                            }
                              return originalParameterizedType.environment.createParameterizedType(
                                          originalParameterizedType.type, substitutedArguments, substitutedEnclosing);
                        }
                        break;
                        
                  case Binding.ARRAY_TYPE:
                        ArrayBinding originalArrayType = (ArrayBinding) originalType;
                        TypeBinding originalLeafComponentType = originalArrayType.leafComponentType;
                        TypeBinding substitute = substitute(substitution, originalLeafComponentType); // substitute could itself be array type
                        if (substitute != originalLeafComponentType) {
                              return originalArrayType.environment.createArrayType(substitute.leafComponentType(), substitute.dimensions() + originalType.dimensions());
                        }
                        break;

                  case Binding.WILDCARD_TYPE:
                    WildcardBinding wildcard = (WildcardBinding) originalType;
                    if (wildcard.boundKind != Wildcard.UNBOUND) {
                          TypeBinding originalBound = wildcard.bound;
                          TypeBinding substitutedBound = substitute(substitution, originalBound);
                          TypeBinding[] originalOtherBounds = wildcard.otherBounds;
                          TypeBinding[] substitutedOtherBounds = substitute(substitution, originalOtherBounds);
                          if (substitutedBound != originalBound || originalOtherBounds != substitutedOtherBounds) {
                              return wildcard.environment.createWildcard(wildcard.genericType, wildcard.rank, substitutedBound, substitutedOtherBounds, wildcard.boundKind);
                          }
                    }
                        break;

                  case Binding.TYPE:
                        if (!originalType.isMemberType()) break;
                        ReferenceBinding originalReferenceType = (ReferenceBinding) originalType;
                        originalEnclosing = originalType.enclosingType();
                        substitutedEnclosing = originalEnclosing;
                        if (originalEnclosing != null) {
                              substitutedEnclosing = (ReferenceBinding) substitute(substitution, originalEnclosing);
                        }
                        
                      // treat as if parameterized with its type variables (non generic type gets 'null' arguments)
                        if (substitutedEnclosing != originalEnclosing) {
                              return substitution.isRawSubstitution() 
                                    ? substitution.environment().createRawType(originalReferenceType, substitutedEnclosing)
                                    :  substitution.environment().createParameterizedType(originalReferenceType, null, substitutedEnclosing);
                        }
                        break;
                  case Binding.GENERIC_TYPE:
                        originalReferenceType = (ReferenceBinding) originalType;
                        originalEnclosing = originalType.enclosingType();
                        substitutedEnclosing = originalEnclosing;
                        if (originalEnclosing != null) {
                              substitutedEnclosing = (ReferenceBinding) substitute(substitution, originalEnclosing);
                        }
                        
                        if (substitution.isRawSubstitution()) {
                              return substitution.environment().createRawType(originalReferenceType, substitutedEnclosing);
                        }
                      // treat as if parameterized with its type variables (non generic type gets 'null' arguments)
                        originalArguments = originalReferenceType.typeVariables();
                        substitutedArguments = substitute(substitution, originalArguments);
//                      if (substitutedArguments != originalArguments || substitutedEnclosing != originalEnclosing) {
                        return substitution.environment().createParameterizedType(originalReferenceType, substitutedArguments, substitutedEnclosing);
//                      }
//                      break;
            }
            return originalType;
      }     

      /**
       * Returns an array of types, where original types got substituted given a substitution.
       * Only allocate an array if anything is different.
       */
      public static TypeBinding[] substitute(Substitution substitution, TypeBinding[] originalTypes) {
            if (originalTypes == null) return null;
          TypeBinding[] substitutedTypes = originalTypes;
          for (int i = 0, length = originalTypes.length; i < length; i++) {
              TypeBinding originalType = originalTypes[i];
              TypeBinding substitutedParameter = substitute(substitution, originalType);
              if (substitutedParameter != originalType) {
                  if (substitutedTypes == originalTypes) {
                      System.arraycopy(originalTypes, 0, substitutedTypes = new TypeBinding[length], 0, i);
                  }
                  substitutedTypes[i] = substitutedParameter;
              } else if (substitutedTypes != originalTypes) {
                  substitutedTypes[i] = originalType;
              }
          }
          return substitutedTypes;
      }

      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 final 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[] genericTypeArguments = invocationSite.genericTypeArguments();
            TypeBinding[] parameters = method.parameters;
            TypeVariableBinding[] typeVariables = method.typeVariables;
            if (parameters == arguments
                  && (method.returnType.tagBits & TagBits.HasTypeVariable) == 0 
                  && genericTypeArguments == null
                  && typeVariables == Binding.NO_TYPE_VARIABLES)
                        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 (typeVariables != Binding.NO_TYPE_VARIABLES) { // generic method
                  TypeBinding[] newArgs = null;
                  for (int i = 0; i < argLength; i++) {
                        TypeBinding param = i < paramLength ? parameters[i] : parameters[paramLength - 1];
                        if (arguments[i].isBaseType() != param.isBaseType()) {
                              if (newArgs == null) {
                                    newArgs = new TypeBinding[argLength];
                                    System.arraycopy(arguments, 0, newArgs, 0, argLength);
                              }
                              newArgs[i] = environment().computeBoxingType(arguments[i]);
                        }     
                  }
                  if (newArgs != null)
                        arguments = newArgs;
                  method = ParameterizedGenericMethodBinding.computeCompatibleMethod(method, arguments, this, invocationSite);
                  if (method == null) return null; // incompatible
                  if (!method.isValidBinding()) return method; // bound check issue is taking precedence
                  parameters = method.parameters; // reacquire them after type inference has performed
            } else if (genericTypeArguments != null) {
                  if (method instanceof ParameterizedGenericMethodBinding) {
                        if (!((ParameterizedGenericMethodBinding) method).wasInferred) {
                              // attempt to invoke generic method of raw type with type hints <String>foo()
                              return new ProblemMethodBinding(method, method.selector, genericTypeArguments, ProblemReasons.TypeArgumentsForRawGenericMethod);
                        }
                  } else {
                        return new ProblemMethodBinding(method, method.selector, genericTypeArguments, ProblemReasons.TypeParameterArityMismatch);
                  }
            }

            if (parameterCompatibilityLevel(method, arguments) > NOT_COMPATIBLE)
                  return method;
            if (genericTypeArguments != null)
                  return new ProblemMethodBinding(method, method.selector, arguments, ProblemReasons.ParameterizedMethodTypeMismatch);
            return null; // incompatible
      }
      
      protected boolean connectTypeVariables(TypeParameter[] typeParameters, boolean checkForErasedCandidateCollisions) {
            if (typeParameters == null || compilerOptions().sourceLevel < ClassFileConstants.JDK1_5) return true;
            boolean noProblems = true;
            Map invocations = new HashMap(2);
            nextVariable : for (int i = 0, paramLength = typeParameters.length; i < paramLength; i++) {
                  TypeParameter typeParameter = typeParameters[i];
                  TypeVariableBinding typeVariable = typeParameter.binding;
                  if (typeVariable == null) return false;

                  typeVariable.superclass = getJavaLangObject();
                  typeVariable.superInterfaces = Binding.NO_SUPERINTERFACES;
                  // set firstBound to the binding of the first explicit bound in parameter declaration
                  typeVariable.firstBound = null; // first bound used to compute erasure

                  TypeReference typeRef = typeParameter.type;
                  if (typeRef == null)
                        continue nextVariable;
                  TypeBinding superType = this.kind == METHOD_SCOPE
                        ? typeRef.resolveType((BlockScope)this, false/*no bound check*/)
                        : typeRef.resolveType((ClassScope)this);
                  if (superType == null) {
                        typeVariable.tagBits |= TagBits.HierarchyHasProblems;
                        noProblems = false;
                        continue nextVariable;
                  }
                  typeRef.resolvedType = superType; // hold onto the problem type
                  if (superType.isArrayType()) {
                        problemReporter().boundCannotBeArray(typeRef, superType);
                        continue nextVariable;
                  }
                  boolean isTypeVariableFirstBound =  superType.isTypeVariable();
                  if (isTypeVariableFirstBound) {
                        TypeVariableBinding varSuperType = (TypeVariableBinding) superType;
                        if (varSuperType.rank >= typeVariable.rank && varSuperType.declaringElement == typeVariable.declaringElement) {
                              problemReporter().forwardTypeVariableReference(typeParameter, varSuperType);
                              typeVariable.tagBits |= TagBits.HierarchyHasProblems;
                              noProblems = false;
                              continue nextVariable;
                        }
                  }
                  ReferenceBinding superRefType = (ReferenceBinding) superType;
                  if (superRefType.isFinal())
                        problemReporter().finalVariableBound(typeVariable, typeRef);
                  if (!superType.isInterface()) {
                        typeVariable.superclass = superRefType;
                  } else {
                        typeVariable.superInterfaces = new ReferenceBinding[] {superRefType};
                  }
                  typeVariable.firstBound = superRefType; // first bound used to compute erasure
                  TypeReference[] boundRefs = typeParameter.bounds;
                  if (boundRefs != null) {
                        for (int j = 0, boundLength = boundRefs.length; j < boundLength; j++) {
                              typeRef = boundRefs[j];
                              superType = this.kind == METHOD_SCOPE
                                    ? typeRef.resolveType((BlockScope)this, false)
                                    : typeRef.resolveType((ClassScope)this);
                              if (superType == null) {
                                    typeVariable.tagBits |= TagBits.HierarchyHasProblems;
                                    noProblems = false;
                                    continue nextVariable;
                              }
                              typeRef.resolvedType = superType; // hold onto the problem type
                              if (isTypeVariableFirstBound && j == 0) {
                                    problemReporter().noAdditionalBoundAfterTypeVariable(typeRef);
                              }
                              if (superType.isArrayType()) {
                                    problemReporter().boundCannotBeArray(typeRef, superType);
                                    continue nextVariable;
                              }
                              superRefType = (ReferenceBinding) superType;
                              if (!superType.isInterface()) {
                                    problemReporter().boundMustBeAnInterface(typeRef, superType);
                                    typeVariable.tagBits |= TagBits.HierarchyHasProblems;
                                    noProblems = false;
                                    continue nextVariable;
                              }
                              // check against superclass
                              if (checkForErasedCandidateCollisions && typeVariable.firstBound == typeVariable.superclass) {
                                    if (hasErasedCandidatesCollisions(superType, typeVariable.superclass, invocations, typeVariable, typeRef)) {
                                          noProblems = false;
                                          continue nextVariable;
                                    }
                              }
                              // check against superinterfaces
                              for (int index = typeVariable.superInterfaces.length; --index >= 0;) {
                                    ReferenceBinding previousInterface = typeVariable.superInterfaces[index];
                                    if (previousInterface == superRefType) {
                                          problemReporter().duplicateBounds(typeRef, superType);
                                          typeVariable.tagBits |= TagBits.HierarchyHasProblems;
                                          noProblems = false;
                                          continue nextVariable;
                                    }
                                    if (checkForErasedCandidateCollisions) {
                                          if (hasErasedCandidatesCollisions(superType, previousInterface, invocations, typeVariable, typeRef)) {
                                                noProblems = false;
                                                continue nextVariable;
                                          }
                                    }
                              }
                              int size = typeVariable.superInterfaces.length;
                              System.arraycopy(typeVariable.superInterfaces, 0, typeVariable.superInterfaces = new ReferenceBinding[size + 1], 0, size);
                              typeVariable.superInterfaces[size] = superRefType;
                        }
                  }
            }
            return noProblems;
      }

      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 TypeVariableBinding[] createTypeVariables(TypeParameter[] typeParameters, Binding declaringElement) {
            // do not construct type variables if source < 1.5
            if (typeParameters == null || compilerOptions().sourceLevel < ClassFileConstants.JDK1_5)
                  return Binding.NO_TYPE_VARIABLES;

            TypeVariableBinding[] typeVariableBindings = Binding.NO_TYPE_VARIABLES;
            PackageBinding unitPackage = compilationUnitScope().fPackage;
            int length = typeParameters.length;
            typeVariableBindings = new TypeVariableBinding[length];
            int count = 0;
            for (int i = 0; i < length; i++) {
                  TypeParameter typeParameter = typeParameters[i];
                  TypeVariableBinding parameterBinding = new TypeVariableBinding(typeParameter.name, declaringElement, i);
                  parameterBinding.fPackage = unitPackage;
                  typeParameter.binding = parameterBinding;

                  // detect duplicates, but keep each variable to reduce secondary errors with instantiating this generic type (assume number of variables is correct)
                  for (int j = 0; j < count; j++) {
                        TypeVariableBinding knownVar = typeVariableBindings[j];
                        if (CharOperation.equals(knownVar.sourceName, typeParameter.name))
                              problemReporter().duplicateTypeParameterInType(typeParameter);
                  }
                  typeVariableBindings[count++] = parameterBinding;
//                      TODO should offer warnings to inform about hiding declaring, enclosing or member types                      
//                      ReferenceBinding type = sourceType;
//                      // check that the member does not conflict with an enclosing type
//                      do {
//                            if (CharOperation.equals(type.sourceName, memberContext.name)) {
//                                  problemReporter().hidingEnclosingType(memberContext);
//                                  continue nextParameter;
//                            }
//                            type = type.enclosingType();
//                      } while (type != null);
//                      // check that the member type does not conflict with another sibling member type
//                      for (int j = 0; j < i; j++) {
//                            if (CharOperation.equals(referenceContext.memberTypes[j].name, memberContext.name)) {
//                                  problemReporter().duplicateNestedType(memberContext);
//                                  continue nextParameter;
//                            }
//                      }
            }
            if (count != length)
                  System.arraycopy(typeVariableBindings, 0, typeVariableBindings = new TypeVariableBinding[count], 0, count);
            return typeVariableBindings;
      }

      public final ClassScope enclosingClassScope() {
            Scope scope = this;
            while ((scope = scope.parent) != null) {
                  if (scope instanceof ClassScope) return (ClassScope) scope;
            }
            return null; // may answer null if no type around
      }
      
      public final MethodScope enclosingMethodScope() {
            Scope scope = this;
            while ((scope = scope.parent) != null) {
                  if (scope instanceof MethodScope) return (MethodScope) scope;
            }
            return null; // may answer null if no method around
      }

      /* Answer the scope receiver type (could be parameterized)
      */
      public final ReferenceBinding enclosingReceiverType() {
            Scope scope = this;
            do {
                  if (scope instanceof ClassScope) {
                        return environment().convertToParameterizedType(((ClassScope) scope).referenceContext.binding);
                  }
                  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).referenceContext.binding;
                  scope = scope.parent;
            } while (scope != null);
            return null;
      }

      public final LookupEnvironment environment() {
            Scope scope, unitScope = this;
            while ((scope = unitScope.parent) != null)
                  unitScope = scope;
            return ((CompilationUnitScope) unitScope).environment;
      }

      // 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) {
                  findMethodInSuperInterfaces(currentType, selector, found);
                  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];
                  }
                  compilationUnitScope().recordTypeReferences(concreteMatch.thrownExceptions);
                  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 mostSpecificInterfaceMethodBinding(candidates, candidatesCount, invocationSite);
      }

      // 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.getExactMethod(selector, argumentTypes, unitScope);
            if (exactMethod != null && exactMethod.typeVariables == Binding.NO_TYPE_VARIABLES && !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
                  unitScope.recordTypeReferences(exactMethod.thrownExceptions);
                  // special treatment for Object.getClass() in 1.5 mode (substitute parameterized return type)
                  if (receiverType.isInterface() || exactMethod.canBeSeenBy(receiverType, invocationSite, this)) {
                        if (receiverType.id != T_JavaLangObject
                              && argumentTypes == Binding.NO_PARAMETERS
                            && CharOperation.equals(selector, GETCLASS)
                            && exactMethod.returnType.isParameterizedType()/*1.5*/) {
                                    return ParameterizedMethodBinding.instantiateGetClass(receiverType, exactMethod, this);
                      }
                        // targeting a generic method could find an exact match with variable return type
                        if (exactMethod.typeVariables != Binding.NO_TYPE_VARIABLES || invocationSite.genericTypeArguments() != null) {
                              MethodBinding compatibleMethod = computeCompatibleMethod(exactMethod, argumentTypes, invocationSite);
                              if (!compatibleMethod.isValidBinding() && exactMethod.typeVariables != Binding.NO_TYPE_VARIABLES) {
                                    return null; // could be a better generic method match (90423), which will be found by non exact match
                              }
                              exactMethod = compatibleMethod;
                        }
                        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: {
                  TypeBinding leafType;
                  switch (receiverType.kind()) {
                        case Binding.BASE_TYPE :
                              return null;
                        case Binding.WILDCARD_TYPE :
                        case Binding.TYPE_PARAMETER : // capture
                              TypeBinding receiverErasure = receiverType.erasure();
                              if (!receiverErasure.isArrayType())
                                    break checkArrayField;
                              leafType = receiverErasure.leafComponentType();
                              break;
                        case Binding.ARRAY_TYPE :
                              leafType = receiverType.leafComponentType();
                              break;
                        default:
                              break checkArrayField;
                  }
                  if (leafType instanceof ReferenceBinding)
                        if (!((ReferenceBinding) leafType).canBeSeenBy(this))
                              return new ProblemFieldBinding((ReferenceBinding)leafType, fieldName, ProblemReasons.ReceiverTypeNotVisible);
                  if (CharOperation.equals(fieldName, LENGTH))
                        return ArrayBinding.ArrayLength;
                  return null;
            }

            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
            ReferenceBinding[] interfacesToVisit = null;
            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) {
                  ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
                  if (itsInterfaces == null) { // needed for statically imported types which don't know their hierarchy yet
                        ((SourceTypeBinding) currentType).scope.connectTypeHierarchy();
                        itsInterfaces = currentType.superInterfaces();
                  }
                  if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
                        if (interfacesToVisit == null) {
                              interfacesToVisit = itsInterfaces;
                              nextPosition = interfacesToVisit.length;
                        } else {
                              int itsLength = itsInterfaces.length;
                              if (nextPosition + itsLength >= interfacesToVisit.length)
                                    System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
                              nextInterface : for (int a = 0; a < itsLength; a++) {
                                    ReferenceBinding next = itsInterfaces[a];
                                    for (int b = 0; b < nextPosition; b++)
                                          if (next == interfacesToVisit[b]) continue nextInterface;
                                    interfacesToVisit[nextPosition++] = next;
                              }
                        }
                  }
                  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;
                        }
                  }
            }

            // walk all visible interfaces to find ambiguous references
            if (interfacesToVisit != null) {
                  ProblemFieldBinding ambiguous = null;
                  done : for (int i = 0; i < nextPosition; i++) {
                        ReferenceBinding anInterface = interfacesToVisit[i];
                        unitScope.recordTypeReference(anInterface);
                        if ((field = anInterface.getField(fieldName, true /*resolve*/)) != null) {
                              if (visibleField == null) {
                                    visibleField = field;
                              } else {
                                    ambiguous = new ProblemFieldBinding(visibleField /* closest match*/, visibleField.declaringClass, fieldName, ProblemReasons.Ambiguous);
                                    break done;
                              }
                        } else {
                              ReferenceBinding[] itsInterfaces = anInterface.superInterfaces();
                              if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
                                    int itsLength = itsInterfaces.length;
                                    if (nextPosition + itsLength >= interfacesToVisit.length)
                                          System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
                                    nextInterface : for (int a = 0; a < itsLength; a++) {
                                          ReferenceBinding next = itsInterfaces[a];
                                          for (int b = 0; b < nextPosition; b++)
                                                if (next == interfacesToVisit[b]) continue nextInterface;
                                          interfacesToVisit[nextPosition++] = next;
                                    }
                              }
                        }
                  }
                  if (ambiguous != null)
                        return ambiguous;
            }

            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[] itsInterfaces = currentType.superInterfaces();
                  if (itsInterfaces == null) { // needed for statically imported types which don't know their hierarchy yet
                        ReferenceBinding sourceType = currentType.isParameterizedType()
                              ? ((ParameterizedTypeBinding) enclosingType).type
                              : currentType;
                        if (sourceType.isHierarchyBeingConnected())
                              return null; // looking for an undefined member type in its own superclass ref
                        ((SourceTypeBinding) sourceType).scope.connectTypeHierarchy();
                        itsInterfaces = currentType.superInterfaces();
                  }
                  if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
                        if (interfacesToVisit == null) {
                              interfacesToVisit = itsInterfaces;
                              nextPosition = interfacesToVisit.length;
                        } else {
                              int itsLength = itsInterfaces.length;
                              if (nextPosition + itsLength >= interfacesToVisit.length)
                                    System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
                              nextInterface : for (int a = 0; a < itsLength; a++) {
                                    ReferenceBinding next = itsInterfaces[a];
                                    for (int b = 0; b < nextPosition; b++)
                                          if (next == interfacesToVisit[b]) continue nextInterface;
                                    interfacesToVisit[nextPosition++] = next;
                              }
                        }
                  }
                  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, null, 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, null, ProblemReasons.Ambiguous);
                                    break done;
                              }
                        } else {
                              ReferenceBinding[] itsInterfaces = anInterface.superInterfaces();
                              if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
                                    int itsLength = itsInterfaces.length;
                                    if (nextPosition + itsLength >= interfacesToVisit.length)
                                          System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
                                    nextInterface : for (int a = 0; a < itsLength; a++) {
                                          ReferenceBinding next = itsInterfaces[a];
                                          for (int b = 0; b < nextPosition; b++)
                                                if (next == interfacesToVisit[b]) continue nextInterface;
                                          interfacesToVisit[nextPosition++] = next;
                                    }
                              }
                        }
                  }
                  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 (currentType.isInterface()) {
                  unitScope.recordTypeReference(currentType);
                  MethodBinding[] currentMethods = currentType.getMethods(selector);
                  if (currentMethods.length > 0)
                        found.addAll(currentMethods);
                  findMethodInSuperInterfaces(currentType, selector, found);
                  currentType = getJavaLangObject();
            }

            // superclass lookup
            long complianceLevel = compilerOptions().complianceLevel;
            boolean isCompliant14 = complianceLevel >= ClassFileConstants.JDK1_4;
            boolean isCompliant15 = complianceLevel >= ClassFileConstants.JDK1_5;
            ReferenceBinding classHierarchyStart = currentType;
            boolean mustBePublic = receiverType.isInterface();
            while (currentType != null) {
                  unitScope.recordTypeReference(currentType);
                  MethodBinding[] currentMethods = currentType.getMethods(selector);
                  int currentLength = currentMethods.length;
                  if (currentLength > 0) {
                        if (isCompliant14 && (mustBePublic || 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 (mustBePublic && !currentMethod.isPublic()) { // only public methods from Object are visible to interface receiverTypes
                                          currentLength--;
                                          currentMethods[i] = 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.typeVariables != Binding.NO_TYPE_VARIABLES && invocationSite.genericTypeArguments() == null)
                                                            continue nextMethod; // keep inherited substituted methods to detect anonymous errors
                                                      if (matchingMethod.hasSubstitutedParameters() && !currentMethod.original().areParametersEqual(matchingMethod.original()))
                                                            continue nextMethod; // keep inherited substituted methods to detect anonymous errors
                                                      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 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 = computeCompatibleMethod(methodBinding, argumentTypes, invocationSite);
                        if (compatibleMethod != null) {
                              if (compatibleMethod.isValidBinding()) {
                                    if (foundSize == 1 && compatibleMethod.canBeSeenBy(receiverType, invocationSite, this)) {
                                          // return the single visible match now
                                          if (isCompliant14 && (receiverType.isAbstract() || receiverType.isTypeVariable()))
                                                return findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, found, compatibleMethod);
                                          unitScope.recordTypeReferences(compatibleMethod.thrownExceptions);
                                          return compatibleMethod;
                                    }
                                    if (candidatesCount == 0)
                                          candidates = new MethodBinding[foundSize];
                                    candidates[candidatesCount++] = compatibleMethod;
                              } else if (problemMethod == null) {
                                    problemMethod = compatibleMethod;
                              }
                        }
                  }
            }

            // no match was found, try to find a close match when the parameter order is wrong or missing some parameters
            if (candidatesCount == 0) {
                  // 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;

                  // 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 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) {
                  if (isCompliant14 && (receiverType.isAbstract() || receiverType.isTypeVariable()))
                        return findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, found, candidates[0]);
                  unitScope.recordTypeReferences(candidates[0].thrownExceptions);
                  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 !declaringClass.isInterface()
                        ? mostSpecificClassMethodBinding(candidates, visiblesCount, invocationSite)
                        : mostSpecificInterfaceMethodBinding(candidates, visiblesCount, invocationSite);
            }

            // check for duplicate parameterized methods
            if (compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) {
                  for (int i = 0; i < visiblesCount; i++) {
                        MethodBinding current = candidates[i];
                        if (current instanceof ParameterizedGenericMethodBinding)
                              current = ((ParameterizedGenericMethodBinding) current).originalMethod;
                        if (current instanceof ParameterizedMethodBinding)
                              for (int j = i + 1; j < visiblesCount; j++)
                                    if (current.declaringClass == candidates[j].declaringClass && current.areParametersEqual(candidates[j]))
                                          return new ProblemMethodBinding(candidates[i].selector, candidates[i].parameters, ProblemReasons.Ambiguous);
                  }
            }

            MethodBinding mostSpecificMethod = mostSpecificMethodBinding(candidates, visiblesCount, argumentTypes, invocationSite, receiverType);
            if (isCompliant15
                  && mostSpecificMethod.isValidBinding()
                  && parameterCompatibilityLevel(mostSpecificMethod, argumentTypes) > COMPATIBLE) {
                        // see if there is a better match in the interfaces - see AutoBoxingTest 99
                        return findDefaultAbstractMethod(receiverType, selector, argumentTypes, invocationSite, classHierarchyStart, found, mostSpecificMethod);
            }
            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': 
                              if (CharOperation.equals(selector, CLONE)) {
                                          return new UpdatedMethodBinding(
                                                compilerOptions().targetJDK >= ClassFileConstants.JDK1_4 ? (TypeBinding)receiverType : (TypeBinding)object, // remember its array type for codegen purpose on target>=1.4.0
                                                (methodBinding.modifiers & ~ClassFileConstants.AccProtected) | ClassFileConstants.AccPublic,
                                                CLONE,
                                                methodBinding.returnType,
                                                argumentTypes,
                                                null,
                                                object);
                              }
                              break;
                          case 'g': 
                              if (CharOperation.equals(selector, GETCLASS) && methodBinding.returnType.isParameterizedType()/*1.5*/) {
                                          return ParameterizedMethodBinding.instantiateGetClass(receiverType, methodBinding, this);
                              }
                              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;
      }

      protected void findMethodInSuperInterfaces(ReferenceBinding currentType, char[] selector, ObjectVector found) {
            ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
            if (itsInterfaces != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
                  ReferenceBinding[] interfacesToVisit = itsInterfaces;
                  int nextPosition = interfacesToVisit.length;
                  for (int i = 0; i < nextPosition; i++) {
                        currentType = interfacesToVisit[i];
                        compilationUnitScope().recordTypeReference(currentType);
                        MethodBinding[] currentMethods = currentType.getMethods(selector);
                        if (currentMethods.length > 0)
                              found.addAll(currentMethods);
                        if ((itsInterfaces = currentType.superInterfaces()) != null && itsInterfaces != Binding.NO_SUPERINTERFACES) {
                              int itsLength = itsInterfaces.length;
                              if (nextPosition + itsLength >= interfacesToVisit.length)
                                    System.arraycopy(interfacesToVisit, 0, interfacesToVisit = new ReferenceBinding[nextPosition + itsLength + 5], 0, nextPosition);
                              nextInterface : for (int a = 0; a < itsLength; a++) {
                                    ReferenceBinding next = itsInterfaces[a];
                                    for (int b = 0; b < nextPosition; b++)
                                          if (next == interfacesToVisit[b]) continue nextInterface;
                                    interfacesToVisit[nextPosition++] = next;
                              }
                        }
                  }
            }
      }

      // Internal use only
      public ReferenceBinding findType(
            char[] typeName,
            PackageBinding declarationPackage,
            PackageBinding invocationPackage) {

            compilationUnitScope().recordReference(declarationPackage.compoundName, typeName);
            ReferenceBinding typeBinding = declarationPackage.getType(typeName);
            if (typeBinding == null)
                  return null;

            if (typeBinding.isValidBinding()) {
                  if (declarationPackage != invocationPackage && !typeBinding.canBeSeenBy(invocationPackage))
                        return new ProblemReferenceBinding(typeName, typeBinding, ProblemReasons.NotVisible);
            }
            return typeBinding;
      }

      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;
                        boolean insideTypeAnnotation = 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;
                                          insideTypeAnnotation = methodScope.insideTypeAnnotation;
                                          
                                          // 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();
                                          if (!insideTypeAnnotation) {
                                                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;
                                                      }
                                                }
                                          }
                                          insideTypeAnnotation = false;
                                          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 :
                                          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 (compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) {
                              // at this point the scope is a compilation unit scope & need to check for imported static fields
                              ImportBinding[] imports = unitScope.imports;
                              if (imports != null) {
                                    // check single static imports
                                    for (int i = 0, length = imports.length; i < length; i++) {
                                          ImportBinding importBinding = imports[i];
                                          if (importBinding.isStatic() && !importBinding.onDemand) {
                                                if (CharOperation.equals(importBinding.compoundName[importBinding.compoundName.length - 1], name)) {
                                                      if (unitScope.resolveSingleImport(importBinding) != null && importBinding.resolvedImport instanceof FieldBinding) {
                                                            foundField = (FieldBinding) importBinding.resolvedImport;
                                                            ImportReference importReference = importBinding.reference;
                                                            if (importReference != null) importReference.used = true;
                                                            invocationSite.setActualReceiverType(foundField.declaringClass);                                                              
                                                            if (foundField.isValidBinding()) {
                                                                  return foundField;
                                                            }
                                                            if (problemField == null)
                                                                  problemField = foundField;
                                                      }
                                                }
                                          }
                                    }
                                    // check on demand imports
                                    boolean foundInImport = false;
                                    for (int i = 0, length = imports.length; i < length; i++) {
                                          ImportBinding importBinding = imports[i];
                                          if (importBinding.isStatic() && importBinding.onDemand) {
                                                Binding resolvedImport = importBinding.resolvedImport;
                                                if (resolvedImport instanceof ReferenceBinding) {
                                                      FieldBinding temp = findField((ReferenceBinding) resolvedImport, name, invocationSite, needResolve);
                                                      if (temp != null) {
                                                            if (!temp.isValidBinding()) {
                                                                  if (problemField == null)
                                                                        problemField = temp;
                                                            } else if (temp.isStatic()) {
                                                                  if (foundField == temp) continue;
                                                                  ImportReference importReference = importBinding.reference;
                                                                  if (importReference != null) importReference.used = true;
                                                                  if (foundInImport)
                                                                        // Answer error binding -- import on demand conflict; name found in two import on demand packages.
                                                                        return new ProblemReferenceBinding(name, null, ProblemReasons.Ambiguous);
                                                                  foundField = temp;
                                                                  foundInImport = true;
                                                            }
                                                      }
                                                }
                                          }
                                    }
                                    if (foundField != null) {
                                          invocationSite.setActualReceiverType(foundField.declaringClass);                                      
                                          return foundField;
                                    }
                              }
                        }
                  }

                  // We did not find a local or instance variable.
                  if ((mask & Binding.TYPE) != 0) {
                        if ((binding = getBaseType(name)) != null)
                              return binding;
                        binding = getTypeOrPackage(name, (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, enclosingSourceType(), ProblemReasons.NotFound);
            } catch (AbortCompilation e) {
                  e.updateContext(invocationSite, referenceCompilationUnit().compilationResult);
                  throw e;
            } finally {
                  env.missingClassFileLocation = null;
            }
      }

      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)) {
                      // targeting a non generic constructor with type arguments ?
                      if (invocationSite.genericTypeArguments() != null)
                        methodBinding = computeCompatibleMethod(methodBinding, argumentTypes, invocationSite);                      
                        return methodBinding;
                  }
                  MethodBinding[] methods = receiverType.getMethods(TypeConstants.INIT);
                  if (methods == Binding.NO_METHODS)
                        return new ProblemMethodBinding(
                              TypeConstants.INIT,
                              argumentTypes,
                              ProblemReasons.NotFound);

                  MethodBinding[] compatible = new MethodBinding[methods.length];
                  int compatibleIndex = 0;
                  MethodBinding problemMethod = null;
                  for (int i = 0, length = methods.length; i < length; i++) {
                        MethodBinding compatibleMethod = computeCompatibleMethod(methods[i], argumentTypes, invocationSite);
                        if (compatibleMethod != null) {
                              if (compatibleMethod.isValidBinding())
                                    compatible[compatibleIndex++] = compatibleMethod;
                              else if (problemMethod == null)
                                    problemMethod = compatibleMethod;
                        }
                  }
                  if (compatibleIndex == 0) {
                        if (problemMethod == null)
                              return new ProblemMethodBinding(TypeConstants.INIT, argumentTypes, ProblemReasons.NotFound);
                        return problemMethod;
                  }
                  // need a more descriptive error... cannot convert from X to Y
      
                  MethodBinding[] visible = new MethodBinding[compatibleIndex];
                  int visibleIndex = 0;
                  for (int i = 0; i < compatibleIndex; i++) {
                        MethodBinding method = compatible[i];
                        if (method.canBeSeenBy(invocationSite, this))
                              visible[visibleIndex++] = method;
                  }
                  if (visibleIndex == 1) return visible[0];
                  if (visibleIndex == 0)
                        return new ProblemMethodBinding(
                              compatible[0],
                              TypeConstants.INIT,
                              compatible[0].parameters,
                              ProblemReasons.NotVisible);
                  // all of visible are from the same declaringClass, even before 1.4 we can call this method instead of mostSpecificClassMethodBinding
                  return mostSpecificMethodBinding(visible, visibleIndex, argumentTypes, invocationSite, receiverType);
            } 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).fPackage;
      }

      /**
       * Returns the modifiers of the innermost enclosing declaration.
       * @return modifiers
       */
      public int getDeclarationModifiers(){
            switch(this.kind){
                  case Scope.BLOCK_SCOPE :
                  case Scope.METHOD_SCOPE :
                        MethodScope methodScope = methodScope();
                        if (!methodScope.isInsideInitializer()){
                              // check method modifiers to see if deprecated
                              MethodBinding context = ((AbstractMethodDeclaration)methodScope.referenceContext).binding;
                              if (context != null)
                                    return context.modifiers;
                        } else {
                              SourceTypeBinding type = ((BlockScope) this).referenceType().binding;

                              // inside field declaration ? check field modifier to see if deprecated
                              if (methodScope.initializedField != null)
                                    return methodScope.initializedField.modifiers;
                              if (type != null)
                                    return type.modifiers;
                        }
                        break;
                  case Scope.CLASS_SCOPE :
                        ReferenceBinding context = ((ClassScope)this).referenceType().binding;
                        if (context != null)
                              return context.modifiers;
                        break;
            }
            return -1;
      }

      public 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;
            }                 
      }
      
      /* 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;
            boolean insideTypeAnnotation = false;
            MethodBinding foundMethod = null;
            MethodBinding foundProblem = null;
            Scope scope = this;
            int depth = 0;
            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;
                              insideTypeAnnotation = methodScope.insideTypeAnnotation;
                              break;
                        case CLASS_SCOPE :
                              ClassScope classScope = (ClassScope) scope;
                              ReferenceBinding receiverType = classScope.enclosingReceiverType();
                              if (!insideTypeAnnotation) {
                                    // 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 (receiverType == methodBinding.declaringClass
                                                            || ((foundProblem == null || foundProblem.problemId() != ProblemReasons.NotVisible) && compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4)
                                                            || (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 (foundProblem != null && foundProblem.problemId() != ProblemReasons.NotVisible)
                                                                        return foundProblem;
                                                                  if (depth > 0) {
                                                                        invocationSite.setDepth(depth);
                                                                        invocationSite.setActualReceiverType(receiverType);
                                                                  }
                                                                  return methodBinding;
                                                      }

                                                      if (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.problemId() == ProblemReasons.NotVisible && methodBinding.problemId() == ProblemReasons.NotFound))
                                                            foundProblem = methodBinding; // hold onto the first not visible/found error and keep the second not found if first is not visible
                                                }
                                          } 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);
                                          }
                                    }
                              }
                              insideTypeAnnotation = false;
                              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 :
                              break done;
                  }
                  scope = scope.parent;
            }

            if (insideStaticContext && compilerOptions().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) {
                              MethodBinding closestMatch = ((ProblemMethodBinding) foundProblem).closestMatch;
                              if (closestMatch != null && closestMatch.canBeSeenBy(invocationSite, this))
                                    return foundProblem; // visible method selectors take precedence
                        }
                  }

                  // at this point the scope is a compilation unit scope & need to check for imported static methods
                  CompilationUnitScope unitScope = (CompilationUnitScope) scope;
                  ImportBinding[] imports = unitScope.imports;
                  if (imports != null) {
                        ObjectVector visible = null;
                        boolean skipOnDemand = false; // set to true when matched static import of method name so stop looking for on demand methods
                        for (int i = 0, length = imports.length; i < length; i++) {
                              ImportBinding importBinding = imports[i];
                              if (importBinding.isStatic()) {
                                    Binding resolvedImport = importBinding.resolvedImport;
                                    MethodBinding possible = null;
                                    if (importBinding.onDemand) {
                                          if (!skipOnDemand && resolvedImport instanceof ReferenceBinding)
                                                // answers closest approximation, may not check argumentTypes or visibility
                                                possible = findMethod((ReferenceBinding) resolvedImport, selector, argumentTypes, invocationSite);
                                    } else {
                                          if (resolvedImport instanceof MethodBinding) {
                                                MethodBinding staticMethod = (MethodBinding) resolvedImport;
                                                if (CharOperation.equals(staticMethod.selector, selector))
                                                      // answers closest approximation, may not check argumentTypes or visibility
                                                      possible = findMethod(staticMethod.declaringClass, selector, argumentTypes, invocationSite);
                                          } else if (resolvedImport instanceof FieldBinding) {
                                                // check to see if there are also methods with the same name
                                                FieldBinding staticField = (FieldBinding) resolvedImport;
                                                if (CharOperation.equals(staticField.name, selector)) {
                                                      // must find the importRef's type again since the field can be from an inherited type
                                                      char[][] importName = importBinding.reference.tokens;
                                                      TypeBinding referencedType = getType(importName, importName.length - 1);
                                                      if (referencedType != null)
                                                            // answers closest approximation, may not check argumentTypes or visibility
                                                            possible = findMethod((ReferenceBinding) referencedType, selector, argumentTypes, invocationSite);
                                                }
                                          }
                                    }
                                    if (possible != null && possible != foundProblem) {
                                          if (!possible.isValidBinding()) {
                                                if (foundProblem == null)
                                                      foundProblem = possible; // answer as error case match
                                          } else if (possible.isStatic()) {
                                                MethodBinding compatibleMethod = computeCompatibleMethod(possible, argumentTypes, invocationSite);
                                                if (compatibleMethod != null) {
                                                      if (compatibleMethod.isValidBinding()) {
                                                            if (compatibleMethod.canBeSeenBy(unitScope.fPackage)) {
                                                                  if (visible == null || !visible.contains(compatibleMethod)) {
                                                                        ImportReference importReference = importBinding.reference;
                                                                        if (importReference != null) importReference.used = true;
                                                                        if (!skipOnDemand && !importBinding.onDemand) {
                                                                              visible = null; // forget previous matches from on demand imports
                                                                              skipOnDemand = true;
                                                                        }
                                                                        if (visible == null)
                                                                              visible = new ObjectVector(3);
                                                                        visible.add(compatibleMethod);
                                                                  }
                                                            } else if (foundProblem == null) {
                                                                  foundProblem = new ProblemMethodBinding(compatibleMethod, selector, compatibleMethod.parameters, ProblemReasons.NotVisible);
                                                            }
                                                      } else if (foundProblem == null) {
                                                            foundProblem = compatibleMethod;
                                                      }
                                                } else if (foundProblem == null) {
                                                      foundProblem = new ProblemMethodBinding(possible, selector, argumentTypes, ProblemReasons.NotFound);
                                                }
                                          }
                                    }
                              }
                        }
                        if (visible != null) {
                              MethodBinding[] temp = new MethodBinding[visible.size];
                              visible.copyInto(temp);
                              foundMethod = mostSpecificMethodBinding(temp, temp.length, argumentTypes, invocationSite, null);
                        }
                  }
            }

            if (foundMethod != null) {
                  invocationSite.setActualReceiverType(foundMethod.declaringClass);
                  return foundMethod;
            }
            if (foundProblem != null)
                  return foundProblem;

            return new ProblemMethodBinding(selector, argumentTypes, ProblemReasons.NotFound);
      }

      public final ReferenceBinding getJavaIoSerializable() {
            compilationUnitScope().recordQualifiedReference(JAVA_IO_SERIALIZABLE);
            return environment().getResolvedType(JAVA_IO_SERIALIZABLE, this);
      }
      
      public final ReferenceBinding getJavaLangAnnotationAnnotation() {
            compilationUnitScope().recordQualifiedReference(JAVA_LANG_ANNOTATION_ANNOTATION);
            return environment().getResolvedType(JAVA_LANG_ANNOTATION_ANNOTATION, this);
      }
      
      public final ReferenceBinding getJavaLangAssertionError() {
            compilationUnitScope().recordQualifiedReference(JAVA_LANG_ASSERTIONERROR);
            return environment().getResolvedType(JAVA_LANG_ASSERTIONERROR, this);
      }

      public final ReferenceBinding getJavaLangClass() {
            compilationUnitScope().recordQualifiedReference(JAVA_LANG_CLASS);
            return environment().getResolvedType(JAVA_LANG_CLASS, this);
      }

      public final ReferenceBinding getJavaLangCloneable() {
            compilationUnitScope().recordQualifiedReference(JAVA_LANG_CLONEABLE);
            return environment().getResolvedType(JAVA_LANG_CLONEABLE, this);
      }
      public final ReferenceBinding getJavaLangEnum() {
            compilationUnitScope().recordQualifiedReference(JAVA_LANG_ENUM);
            return environment().getResolvedType(JAVA_LANG_ENUM, this);
      }

      public final ReferenceBinding getJavaLangIterable() {
            compilationUnitScope().recordQualifiedReference(JAVA_LANG_ITERABLE);
            return environment().getResolvedType(JAVA_LANG_ITERABLE, this);
      }
      public final ReferenceBinding getJavaLangObject() {
            compilationUnitScope().recordQualifiedReference(JAVA_LANG_OBJECT);
            return environment().getResolvedType(JAVA_LANG_OBJECT, this);
      }

      public final ReferenceBinding getJavaLangString() {
            compilationUnitScope().recordQualifiedReference(JAVA_LANG_STRING);
            return environment().getResolvedType(JAVA_LANG_STRING, this);
      }

      public final ReferenceBinding getJavaLangThrowable() {
            compilationUnitScope().recordQualifiedReference(JAVA_LANG_THROWABLE);
            return environment().getResolvedType(JAVA_LANG_THROWABLE, this);
      }
      public final ReferenceBinding getJavaUtilIterator() {
            compilationUnitScope().recordQualifiedReference(JAVA_UTIL_ITERATOR);
            return environment().getResolvedType(JAVA_UTIL_ITERATOR, 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;
                  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;

                  // special treatment for Object.getClass() in 1.5 mode (substitute parameterized return type)
                  if (receiverType.id != T_JavaLangObject
                        && argumentTypes == Binding.NO_PARAMETERS
                      && CharOperation.equals(selector, GETCLASS)
                      && methodBinding.returnType.isParameterizedType()/*1.5*/) {
                              return ParameterizedMethodBinding.instantiateGetClass(receiverType, methodBinding, this);
                }             
                  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++]);
                  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),
                              null, // TODO should improve
                              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);
            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),
                        null, // TODO should improve
                        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++]); // 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),
                                    null, // TODO should improve
                                    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),
                              null, // TODO should improve
                              typeBinding.problemId());
                  }
            }
            return typeBinding;
      }
      
      /* Internal use only 
      */
      final Binding getTypeOrPackage(char[] name, int mask) {
            Scope scope = this;
            ReferenceBinding foundType = null;
            boolean insideStaticContext = false;
            boolean insideTypeAnnotation = 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;
                                    AbstractMethodDeclaration methodDecl = methodScope.referenceMethod();
                                    if (methodDecl != null && methodDecl.binding != null) {
                                          TypeVariableBinding typeVariable = methodDecl.binding.getTypeVariable(name);
                                          if (typeVariable != null)     return typeVariable;
                                    }
                                    insideStaticContext |= methodScope.isStatic;
                                    insideTypeAnnotation = methodScope.insideTypeAnnotation;
                              case BLOCK_SCOPE :
                                    ReferenceBinding localType = ((BlockScope) scope).findLocalType(name); // looks in this scope only
                                    if (localType != null) {
                                          if (foundType != null && foundType != localType)
                                                return new ProblemReferenceBinding(name, foundType, ProblemReasons.InheritedNameHidesEnclosingName);
                                          return localType;
                                    }
                                    break;
                              case CLASS_SCOPE :
                                    SourceTypeBinding sourceType = ((ClassScope) scope).referenceContext.binding;
                                    if (scope == this && (sourceType.tagBits & TagBits.TypeVariablesAreConnected) == 0) {
                                          // 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
                                          TypeVariableBinding typeVariable = sourceType.getTypeVariable(name);
                                          if (typeVariable != null)
                                                return typeVariable;
                                          if (CharOperation.equals(name, sourceType.sourceName))
                                                return sourceType;
                                          insideStaticContext |= sourceType.isStatic();
                                          break;
                                    }
                                    // type variables take precedence over member types
                                    TypeVariableBinding typeVariable = sourceType.getTypeVariable(name);
                                    if (typeVariable != null) {
                                          if (insideStaticContext) // do not consider this type modifiers: access is legite within same type
                                                return new ProblemReferenceBinding(name, typeVariable, ProblemReasons.NonStaticReferenceInStaticContext);
                                          return typeVariable;
                                    }
                                    if (!insideTypeAnnotation) {
                                          // 6.5.5.1 - member types have precedence over top-level type in same unit
                                          ReferenceBinding memberType = findMemberType(name, sourceType);
                                          if (memberType != null) { // skip it if we did not find anything
                                                if (memberType.problemId() == ProblemReasons.Ambiguous) {
                                                      if (foundType == null || foundType.problemId() == ProblemReasons.NotVisible)
                                                            // supercedes any potential InheritedNameHidesEnclosingName problem
                                                            return memberType;
                                                      // make the user qualify the type, likely wants the first inherited type
                                                      return new ProblemReferenceBinding(name, foundType, ProblemReasons.InheritedNameHidesEnclosingName);
                                                }
                                                if (memberType.isValidBinding()) {
                                                      if (sourceType == memberType.enclosingType() || compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) {
                                                            if (insideStaticContext && !memberType.isStatic() && sourceType.isGenericType())
                                                                  return new ProblemReferenceBinding(name, memberType, ProblemReasons.NonStaticReferenceInStaticContext);
                                                            // found a valid type in the 'immediate' scope (ie. not inherited)
                                                            // OR in 1.4 mode (inherited shadows enclosing)
                                                            if (foundType == null)
                                                                  return memberType; 
                                                            // if a valid type was found, complain when another is found in an 'immediate' enclosing type (ie. not inherited)
                                                            if (foundType.isValidBinding() && foundType != memberType)
                                                                  return new ProblemReferenceBinding(name, foundType, ProblemReasons.InheritedNameHidesEnclosingName);
                                                      }
                                                }
                                                if (foundType == null || (foundType.problemId() == ProblemReasons.NotVisible && memberType.problemId() != ProblemReasons.NotVisible))
                                                      // only remember the memberType if its the first one found or the previous one was not visible & memberType is...
                                                      foundType = memberType;
                                          }
                                    }
                                    insideTypeAnnotation = false;
                                    insideStaticContext |= sourceType.isStatic();
                                    if (CharOperation.equals(sourceType.sourceName, name)) {
                                          if (foundType != null && foundType != sourceType && foundType.problemId() != ProblemReasons.NotVisible)
                                                return new ProblemReferenceBinding(name, foundType, ProblemReasons.InheritedNameHidesEnclosingName);
                                          return sourceType;
                                    }
                                    break;
                              case COMPILATION_UNIT_SCOPE :
                                    break done;
                        }
                        scope = scope.parent;
                  }
                  if (foundType != null && foundType.problemId() != ProblemReasons.NotVisible)
                        return foundType;
            }

            // 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.used = true;
                              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 (foundType != null && foundType.problemId() != ProblemReasons.NotVisible && binding.problemId() != ProblemReasons.Ambiguous)
                                    return foundType; // problem type from above supercedes NotFound type but not Ambiguous import case
                              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) != 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.used = true;
                                                return resolvedImport; // already know its visible
                                          }
                                    }
                              }
                        }
                  }

                  // check if the name is in the current package, skip it if its a sub-package
                  PackageBinding currentPackage = unitScope.fPackage; 
                  unitScope.recordReference(currentPackage.compoundName, name);
                  Binding binding = currentPackage.getTypeOrPackage(name);
                  if (binding instanceof ReferenceBinding) {
                        if (typeOrPackageCache != null)
                              typeOrPackageCache.put(name, binding);
                        return binding; // type is always visible to its own package
                  }

                  // check on demand imports
                  if (imports != null) {
                        boolean foundInImport = false;
                        ReferenceBinding type = null;
                        for (int i = 0, length = imports.length; i < length; i++) {
                              ImportBinding someImport = imports[i];
                              if (someImport.onDemand) {
                                    Binding resolvedImport = someImport.resolvedImport;
                                    ReferenceBinding temp = null;
                                    if (resolvedImport instanceof PackageBinding) {
                                          temp = findType(name, (PackageBinding) resolvedImport, currentPackage);
                                    } else if (someImport.isStatic()) {
                                          temp = findMemberType(name, (ReferenceBinding) resolvedImport); // static imports are allowed to see inherited member types
                                          if (temp != null && !temp.isStatic())
                                                temp = null;
                                    } else {
                                          temp = findDirectMemberType(name, (ReferenceBinding) resolvedImport);
                                    }
                                    if (temp != type && temp != null) {
                                          if (temp.isValidBinding()) {
                                                ImportReference importReference = someImport.reference;
                                                if (importReference != null) importReference.used = true;
                                                if (foundInImport) {
                                                      // Answer error binding -- import on demand conflict; name found in two import on demand packages.
                                                      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) {
            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++]);
                        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),
                                    null, // TODO should improve
                                    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 = (ReferenceBinding) this.environment().convertToRawType(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),
                              null, // TODO should improve
                              typeBinding.problemId());
                  
                  if (typeBinding.isGenericType()) {
                        qualifiedType = this.environment().createRawType(typeBinding, qualifiedType);
                  } else {
                        qualifiedType = (qualifiedType != null && (qualifiedType.isRawType() || qualifiedType.isParameterizedType()))
                              ? this.environment().createParameterizedType(typeBinding, null, 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;
                        Set invalidInvocations = (Set) invocations.get(mec);
                        int invalidSize = invalidInvocations.size();
                        if (invalidSize > 1) {
                              TypeBinding[] collisions;
                              invalidInvocations.toArray(collisions = new TypeBinding[invalidSize]);
                              problemReporter().superinterfacesCollide(collisions[0].erasure(), typeRef, collisions[0], collisions[1]);
                              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.isRawType()) {
                              TypeBinding match = oneParam.findSuperTypeWithSameErasure(twoParam);
                              if (match != twoParam)
                                    return false;
                        }
                        if (!oneParam.isCompatibleWith(twoParam)) {
                              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).referenceContext.binding == 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) {
                                    type.initializeDeprecatedAnnotationTagBits(); // may not have been resolved until then
                                    if (type.isViewedAsDeprecated())
                                          return true;
                              }
                        }
                        break;
                  case Scope.CLASS_SCOPE :
                        ReferenceBinding context = ((ClassScope)this).referenceType().binding;
                        if (context != null) {
                              context.initializeDeprecatedAnnotationTagBits(); // may not have been resolved until then
                              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) {
                                    type.initializeDeprecatedAnnotationTagBits(); // may not have been resolved until then
                                    if (type.isViewedAsDeprecated())
                                          return true;
                              }
                        }
            }
            return false;
      }

      private TypeBinding leastContainingInvocation(TypeBinding mec, Set invocations, List lubStack) {
            if (invocations == null) return mec; // no alternate invocation
            int length = invocations.size();
            Iterator iter = invocations.iterator();
            if (length == 1) return (TypeBinding) iter.next();

            // if mec is an array type, intersect invocation leaf component types, then promote back to array
            int dim = mec.dimensions();
            mec = mec.leafComponentType();
            
            int argLength = mec.typeVariables().length;
            if (argLength == 0) return mec; // should be caught by no invocation check

            // infer proper parameterized type from invocations
            TypeBinding[] bestArguments = new TypeBinding[argLength];
            while (iter.hasNext()) {
                  TypeBinding invocation = ((TypeBinding)iter.next()).leafComponentType();
                  switch (invocation.kind()) {
                        case Binding.GENERIC_TYPE :
                              TypeVariableBinding[] invocationVariables = invocation.typeVariables();
                              for (int i = 0; i < argLength; i++) {
                                    TypeBinding bestArgument = leastContainingTypeArgument(bestArguments[i], invocationVariables[i], (ReferenceBinding) mec, i, lubStack);
                                    if (bestArgument == null) return null;
                                    bestArguments[i] = bestArgument;
                              }
                              break;
                        case Binding.PARAMETERIZED_TYPE :
                              ParameterizedTypeBinding parameterizedType = (ParameterizedTypeBinding)invocation;
                              for (int i = 0; i < argLength; i++) {
                                    TypeBinding bestArgument = leastContainingTypeArgument(bestArguments[i], parameterizedType.arguments[i], (ReferenceBinding) mec, i, lubStack);
                                    if (bestArgument == null) return null;
                                    bestArguments[i] = bestArgument;
                              }
                              break;
                        case Binding.RAW_TYPE :
                              return dim == 0 ? invocation : environment().createArrayType(invocation, dim); // raw type is taking precedence
                  }
            }
            TypeBinding least = environment().createParameterizedType((ReferenceBinding) mec.erasure(), bestArguments, mec.enclosingType());
            return dim == 0 ? least : environment().createArrayType(least, dim);
      }
      
      // JLS 15.12.2
      private TypeBinding leastContainingTypeArgument(TypeBinding u, TypeBinding v, ReferenceBinding genericType, int rank, List lubStack) {
            if (u == null) return v;
            if (u == v) return u;
            if (v.isWildcard()) {
                  WildcardBinding wildV = (WildcardBinding) v;
                  if (u.isWildcard()) {
                        WildcardBinding wildU = (WildcardBinding) u;
                        switch (wildU.boundKind) {
                              // ? extends U
                              case Wildcard.EXTENDS :
                                    switch(wildV.boundKind) {
                                          // ? extends U, ? extends V
                                          case Wildcard.EXTENDS :  
                                                TypeBinding lub = lowerUpperBound(new TypeBinding[]{wildU.bound,wildV.bound}, lubStack);
                                                if (lub == null) return null;
                                                // int is returned to denote cycle detected in lub computation - stop recursion by answering unbound wildcard
                                                if (lub == TypeBinding.INT) return environment().createWildcard(genericType, rank, null, null /*no extra bound*/, Wildcard.UNBOUND);
                                                return environment().createWildcard(genericType, rank, lub, null /*no extra bound*/, Wildcard.EXTENDS);     
                                          // ? extends U, ? SUPER V
                                          case Wildcard.SUPER : 
                                                if (wildU.bound == wildV.bound) return wildU.bound;
                                                return environment().createWildcard(genericType, rank, null, null /*no extra bound*/, Wildcard.UNBOUND);
                                    }
                                    break;
                                    // ? super U
                              case Wildcard.SUPER : 
                                    // ? super U, ? super V
                                    if (wildU.boundKind == Wildcard.SUPER) {
                                          TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{wildU.bound,wildV.bound});
                                          if (glb == null) return null;
                                          return environment().createWildcard(genericType, rank, glb[0], null /*no extra bound*/, Wildcard.SUPER);    // TODO (philippe) need to capture entire bounds
                                    }
                        }                       
                  } else {
                        switch (wildV.boundKind) {
                              // U, ? extends V
                              case Wildcard.EXTENDS :
                                    TypeBinding lub = lowerUpperBound(new TypeBinding[]{u,wildV.bound}, lubStack);
                                    if (lub == null) return null;
                                    // int is returned to denote cycle detected in lub computation - stop recursion by answering unbound wildcard
                                    if (lub == TypeBinding.INT) return environment().createWildcard(genericType, rank, null, null /*no extra bound*/, Wildcard.UNBOUND);
                                    return environment().createWildcard(genericType, rank, lub, null /*no extra bound*/, Wildcard.EXTENDS);     
                              // U, ? super V
                              case Wildcard.SUPER :
                                    TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{u,wildV.bound});
                                    if (glb == null) return null;
                                    return environment().createWildcard(genericType, rank, glb[0], null /*no extra bound*/, Wildcard.SUPER);    // TODO (philippe) need to capture entire bounds
                              case Wildcard.UNBOUND :
                        }
                  }
            } else if (u.isWildcard()) {
                  WildcardBinding wildU = (WildcardBinding) u;
                  switch (wildU.boundKind) {
                        // U, ? extends V
                        case Wildcard.EXTENDS :
                              TypeBinding lub = lowerUpperBound(new TypeBinding[]{wildU.bound, v}, lubStack);
                              if (lub == null) return null;
                              // int is returned to denote cycle detected in lub computation - stop recursion by answering unbound wildcard
                              if (lub == TypeBinding.INT) return environment().createWildcard(genericType, rank, null, null /*no extra bound*/, Wildcard.UNBOUND);
                              return environment().createWildcard(genericType, rank, lub, null /*no extra bound*/, Wildcard.EXTENDS);     
                        // U, ? super V
                        case Wildcard.SUPER :
                              TypeBinding[] glb = greaterLowerBound(new TypeBinding[]{wildU.bound, v});
                              if (glb == null) return null;
                              return environment().createWildcard(genericType, rank, glb[0], null /*no extra bound*/, Wildcard.SUPER); // TODO (philippe) need to capture entire bounds         
                        case Wildcard.UNBOUND :
                  }
            }
            TypeBinding lub = lowerUpperBound(new TypeBinding[]{u,v}, lubStack);
            if (lub == null) return null;
            // int is returned to denote cycle detected in lub computation - stop recursion by answering unbound wildcard
            if (lub == TypeBinding.INT) return environment().createWildcard(genericType, rank, null, null /*no extra bound*/, Wildcard.UNBOUND);
            return environment().createWildcard(genericType, rank, lub, null /*no extra bound*/, Wildcard.EXTENDS);
      }

      // 15.12.2
      /**
       * Returns VoidBinding if types have no intersection (e.g. 2 unrelated interfaces), or null if
       * no common supertype (e.g. List<String> and List<Exception>), or the intersection type if possible
       */
      public TypeBinding lowerUpperBound(TypeBinding[] types) {
            int typeLength = types.length;
            if (typeLength == 1) {
                  TypeBinding type = types[0];
                  return type == null ? TypeBinding.VOID : type;
            }           
            return lowerUpperBound(types, new ArrayList(1));
      }
      
      // 15.12.2
      private TypeBinding lowerUpperBound(TypeBinding[] types, List lubStack) {
            
            int typeLength = types.length;
            if (typeLength == 1) {
                  TypeBinding type = types[0];
                  return type == null ? TypeBinding.VOID : type;
            }
            // cycle detection
            int stackLength = lubStack.size();
            nextLubCheck: for (int i = 0; i < stackLength; i++) {
                  TypeBinding[] lubTypes = (TypeBinding[])lubStack.get(i);
                  int lubTypeLength = lubTypes.length;
                  if (lubTypeLength < typeLength) continue nextLubCheck;
                  nextTypeCheck:    for (int j = 0; j < typeLength; j++) {
                        TypeBinding type = types[j];
                        if (type == null) continue nextTypeCheck; // ignore
                        for (int k = 0; k < lubTypeLength; k++) {
                              TypeBinding lubType = lubTypes[k];
                              if (lubType == null) continue; // ignore
                              if (lubType == type || lubType.isEquivalentTo(type)) continue nextTypeCheck; // type found, jump to next one 
                        }
                        continue nextLubCheck; // type not found in current lubTypes
                  }
                  // all types are included in some lub, cycle detected - stop recursion by answering special value (int)
                  return TypeBinding.INT;
            }

            lubStack.add(types);
            Map invocations = new HashMap(1);
            TypeBinding[] mecs = minimalErasedCandidates(types, invocations);
            if (mecs == null) return null;
            int length = mecs.length;
            if (length == 0) return TypeBinding.VOID;
            int count = 0;
            TypeBinding firstBound = null;
            int commonDim = -1;
            for (int i = 0; i < length; i++) {
                  TypeBinding mec = mecs[i];
                  if (mec == null) continue;
                  mec = leastContainingInvocation(mec, (Set)invocations.get(mec), lubStack);
                  if (mec == null) return null;
                  int dim = mec.dimensions();
                  if (commonDim == -1) {
                        commonDim = dim;
                  } else if (dim != commonDim) { // not all types have same dimension
                        return null;
                  }
                  if (firstBound == null && !mec.leafComponentType().isInterface()) firstBound = mec.leafComponentType();
                  mecs[count++] = mec; // recompact them to the front
            }
            switch (count) {
                  case 0 : return TypeBinding.VOID;
                  case 1 : return mecs[0];
                  case 2 : 
                        if ((commonDim == 0 ? mecs[1].id : mecs[1].leafComponentType().id) == T_JavaLangObject) return mecs[0];
                        if ((commonDim == 0 ? mecs[0].id : mecs[0].leafComponentType().id) == T_JavaLangObject) return mecs[1];
            }
            TypeBinding[] otherBounds = new TypeBinding[count - 1];
            int rank = 0;
            for (int i = 0; i < count; i++) {
                  TypeBinding mec = commonDim == 0 ? mecs[i] : mecs[i].leafComponentType();
                  if (mec.isInterface()) {
                        otherBounds[rank++] = mec;
                  }
            }
            TypeBinding intersectionType = environment().createWildcard(null, 0, firstBound, otherBounds, Wildcard.EXTENDS);
            return commonDim == 0 ? intersectionType : environment().createArrayType(intersectionType, commonDim);
      }
      
      public final 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 = (leafType.isTypeVariable() || leafType.isWildcard()/*&& !leafType.isCapture()*/) ? firstType : firstType.erasure();
            if (firstErasure != firstType) {
                  Set someInvocations = new HashSet(1);
                  someInvocations.add(firstType);
                  allInvocations.put(firstErasure, someInvocations);
            }                                   
            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_byte:
                              case T_short:
                              case T_char:
                              case T_boolean:
                              case T_int:
                              case T_long:
                              case T_float:
                              case T_double:
                                    TypeBinding superType = getJavaIoSerializable();
                                    if (!typesToVisit.contains(superType)) {
                                          typesToVisit.add(superType);
                                          max++;
                                    }
                                    superType = getJavaLangCloneable();
                                    if (!typesToVisit.contains(superType)) {
                                          typesToVisit.add(superType);
                                          max++;
                                    }
                                    superType = getJavaLangObject();
                                    if (!typesToVisit.contains(superType)) {
                                          typesToVisit.add(superType);
                                          max++;
                                    }
                                    continue;
                              
                              default:
                        }
                        typeToVisit = leafType;
                  }
                  currentType = (ReferenceBinding) typeToVisit;
                  if (currentType.isCapture()) {
                        TypeBinding firstBound = ((CaptureBinding) currentType).firstBound;
                        if (firstBound != null && firstBound.isArrayType()) {
                              TypeBinding superType = dim == 0 ? firstBound : (TypeBinding)environment().createArrayType(firstBound, dim); // recreate array if needed
                              if (!typesToVisit.contains(superType)) {
                                    typesToVisit.add(superType);
                                    max++;
                                    TypeBinding superTypeErasure = (firstBound.isTypeVariable() || firstBound.isWildcard() /*&& !itsInterface.isCapture()*/) ? superType : superType.erasure();
                                    if (superTypeErasure != superType) {
                                          Set someInvocations = new HashSet(1);
                                          someInvocations.add(superType);
                                          allInvocations.put(superTypeErasure, someInvocations);
                                    }                                   
                              }
                              continue;
                        }
                  }
                  // inject super interfaces prior to superclass
                  ReferenceBinding[] itsInterfaces = currentType.superInterfaces();
                  if (itsInterfaces != null) { // can be null during code assist operations that use LookupEnvironment.completeTypeBindings(parsedUnit, buildFieldsAndMethods)
                        for (int j = 0, count = itsInterfaces.length; j < count; j++) {
                              TypeBinding itsInterface = itsInterfaces[j];
                              TypeBinding superType = dim == 0 ? itsInterface : (TypeBinding)environment().createArrayType(itsInterface, dim); // recreate array if needed
                              if (!typesToVisit.contains(superType)) {
                                    typesToVisit.add(superType);
                                    max++;
                                    TypeBinding superTypeErasure = (itsInterface.isTypeVariable() || itsInterface.isWildcard() /*&& !itsInterface.isCapture()*/) ? superType : superType.erasure();
                                    if (superTypeErasure != superType) {
                                          Set someInvocations = new HashSet(1);
                                          someInvocations.add(superType);
                                          allInvocations.put(superTypeErasure, someInvocations);
                                    }                                   
                              }
                        }
                  }
                  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 = (itsSuperclass.isTypeVariable() || itsSuperclass.isWildcard() /*&& !itsSuperclass.isCapture()*/) ? superType : superType.erasure();
                              if (superTypeErasure != superType) {
                                    Set someInvocations = new HashSet(1);
                                    someInvocations.add(superType);
                                    allInvocations.put(superTypeErasure, someInvocations);
                              }
                        }
                  }
            }
            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++] = (leafType.isTypeVariable() || leafType.isWildcard() /*&& !leafType.isCapture()*/) ? type : type.erasure();
            }
            // 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
                              Set someInvocations = (Set) allInvocations.get(erasedSuperType);
                              if (someInvocations == null) someInvocations = new HashSet(1);
                              someInvocations.add(match);
                              allInvocations.put(erasedSuperType, someInvocations);
                        }
                        continue nextOtherType;
                  }
                  nextSuperType: for (int j = 0; j < superLength; j++) {
                        TypeBinding erasedSuperType = erasedSuperTypes[j];
                        if (erasedSuperType == null) continue nextSuperType;
                        TypeBinding match;
                        if (erasedSuperType == otherType || erasedSuperType.id == T_JavaLangObject && otherType.isInterface()) {
                              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
                        Set someInvocations = (Set) allInvocations.get(erasedSuperType);
                        if (someInvocations == null) someInvocations = new HashSet(1);
                        someInvocations.add(match);
                        allInvocations.put(erasedSuperType, someInvocations);
                  }                       
            }
            // 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 (otherType.id == T_JavaLangObject && erasedSuperType.isInterface()) continue nextOtherType; // keep Object for an interface
                                    if (erasedSuperType.findSuperTypeWithSameErasure(otherType) != null) {
                                          erasedSuperTypes[j] = null; // discard non minimal supertype
                                          remaining--;
                                    }
                              } else if (erasedSuperType.isArrayType()) {
                              if (otherType.isArrayType() // keep Object[...] for an interface array (same dimensions)
                                          && otherType.leafComponentType().id == T_JavaLangObject
                                          && otherType.dimensions() == erasedSuperType.dimensions()
                                          && erasedSuperType.leafComponentType().isInterface()) continue nextOtherType;
                                    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 problemMethod = null;
            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;
                  }
                  compilationUnitScope().recordTypeReferences(method.thrownExceptions);
                  return method;
            }
            if (problemMethod == null)
                  return new ProblemMethodBinding(visible[0].selector, visible[0].parameters, ProblemReasons.Ambiguous);
            return problemMethod;
      }
      
      // Internal use only
      /* All methods in visible are acceptable matches for the method in question...
      * Since the receiver type is an interface, we ignore the possibility that 2 inherited
      * but unrelated superinterfaces may define the same method in acceptable but
      * not identical ways... we just take the best match that we find since any class which
      * implements the receiver interface MUST implement all signatures for the method...
      * in which case the best match is correct.
      *
      * NOTE: This is different than javac... in the following example, the message send of
      * bar(X) in class Y is supposed to be ambiguous. But any class which implements the
      * interface I MUST implement both signatures for bar. If this class was the receiver of
      * the message send instead of the interface I, then no problem would be reported.
      *
      interface I1 {
            void bar(J j);
      }
      interface I2 {
      //    void bar(J j);
            void bar(Object o);
      }
      interface I extends I1, I2 {}
      interface J {}
      
      class X implements J {}
      
      class Y extends X {
            public void foo(I i, X x) { i.bar(x); }
      }
      */
      protected final MethodBinding mostSpecificInterfaceMethodBinding(MethodBinding[] visible, int visibleSize, InvocationSite invocationSite) {
            MethodBinding problemMethod = null;
            nextVisible : for (int i = 0; i < visibleSize; i++) {
                  MethodBinding method = visible[i];
                  for (int j = 0; j < visibleSize; j++) {
                        if (i == j) continue;
                        if (!visible[j].areParametersCompatibleWith(method.parameters))
                              continue nextVisible;
                  }
                  compilationUnitScope().recordTypeReferences(method.thrownExceptions);
                  return method;
            }
            if (problemMethod == null)
                  return new ProblemMethodBinding(visible[0].selector, visible[0].parameters, ProblemReasons.Ambiguous);
            return problemMethod;
      }

      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);

            boolean useTiebreakMethod = invocationSite.genericTypeArguments() == null;
            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 = useTiebreakMethod ? current.tiebreakMethod() : current;
                        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;
                              if (next instanceof ParameterizedGenericMethodBinding) {
                                    ParameterizedGenericMethodBinding pNext = (ParameterizedGenericMethodBinding) next;
                                    if (pNext.isRaw) {
                                          // hold onto the raw substituted method
                                    } else {
                                          methodToTest = pNext.originalMethod;
                                    }
                              }
                              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) {
                              compilationUnitScope().recordTypeReferences(visible[i].thrownExceptions);
                              return visible[i];
                        }
                  }
            } else if (count == 0) {
                  return new ProblemMethodBinding(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???)
            if (receiverType != null)
                  receiverType = receiverType instanceof CaptureBinding ? receiverType : (ReferenceBinding) receiverType.erasure();
            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.erasure());
                                    if (superType == null)
                                          continue nextSpecific; // current's declaringClass is not a subtype of next's declaringClass
                                    if (current.hasSubstitutedParameters() || original.typeVariables != Binding.NO_TYPE_VARIABLES) {
                                          if (original2.declaringClass != superType) {
                                                // 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 (original.typeVariables != Binding.NO_TYPE_VARIABLES)
                                                original2 = original.computeSubstitutedMethod(original2, environment());
                                          if (original2 == null || !original.areParametersEqual(original2))
                                                continue nextSpecific; // current does not override next
                                    }
                              } else if (receiverType != null) { // should not be null if original isAbstract, but be safe
                                    TypeBinding superType = receiverType.findSuperTypeWithSameErasure(original.declaringClass.erasure());
                                    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.erasure());
                                    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 (original.typeVariables != Binding.NO_TYPE_VARIABLES)
                                          original2 = original.computeSubstitutedMethod(original2, environment());
                                    if (original2 == null || !original.areParameterErasuresEqual(original2))
                                          continue nextSpecific; // current does not override next
                                    if (!original.returnType.isCompatibleWith(original2.returnType)) // 15.12.2
                                          continue nextSpecific; // choose original2 instead
                                    if (original.thrownExceptions != original2.thrownExceptions) {
                                          if (mostSpecificExceptions == null)
                                                mostSpecificExceptions = original.thrownExceptions;
                                          if (possibleMethods == null)
                                                possibleMethods = new SimpleSet(3);
                                          int mostSpecificLength = mostSpecificExceptions.length;
                                          int original2Length = original2.thrownExceptions.length;
                                          SimpleSet temp = new SimpleSet(mostSpecificLength);
                                          nextException : for (int t = 0; t < mostSpecificLength; t++) {
                                                ReferenceBinding exception = mostSpecificExceptions[t];
                                                for (int s = 0; s < original2Length; s++) {
                                                      if (exception.isCompatibleWith(original2.thrownExceptions[s])) {
                                                            possibleMethods.add(current);
                                                            temp.add(exception);
                                                            continue nextException;
                                                      } else if (original2.thrownExceptions[s].isCompatibleWith(exception)) {
                                                            possibleMethods.add(next);
                                                            temp.add(original2.thrownExceptions[s]);
                                                            continue nextException;
                                                      }
                                                }
                                          }
                                          mostSpecificExceptions = temp.elementSize == 0 ? Binding.NO_EXCEPTIONS : new ReferenceBinding[temp.elementSize];
                                          temp.asArray(mostSpecificExceptions);
                                    }
                              }
                        }
                        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;
                                    ReferenceBinding[] itsExceptions = possible.thrownExceptions;
                                    if (itsExceptions.length == exceptionLength) {
                                          nextException : for (int e = 0; e < exceptionLength; e++) {
                                                ReferenceBinding exception = itsExceptions[e];
                                                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 MethodBinding(
//                                  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].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
      }

      protected 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;
      }
}

Generated by  Doxygen 1.6.0   Back to index