예제 #1
0
  public final boolean canBeSeenBy(
      TypeBinding receiverType, InvocationSite invocationSite, Scope scope) {
    if (isPublic()) return true;

    SourceTypeBinding invocationType = scope.enclosingSourceType();
    if (invocationType == this.declaringClass && invocationType == receiverType) return true;

    if (invocationType == null) // static import call
    return !isPrivate() && scope.getCurrentPackage() == this.declaringClass.fPackage;

    if (isProtected()) {
      // answer true if the invocationType is the declaringClass or they are in the same package
      // OR the invocationType is a subclass of the declaringClass
      //    AND the receiverType is the invocationType or its subclass
      //    OR the method is a static method accessed directly through a type
      //    OR previous assertions are true for one of the enclosing type
      if (invocationType == this.declaringClass) return true;
      if (invocationType.fPackage == this.declaringClass.fPackage) return true;

      ReferenceBinding currentType = invocationType;
      int depth = 0;
      ReferenceBinding receiverErasure = (ReferenceBinding) receiverType.erasure();
      ReferenceBinding declaringErasure = (ReferenceBinding) this.declaringClass.erasure();
      do {
        if (currentType.findSuperTypeOriginatingFrom(declaringErasure) != null) {
          if (invocationSite.isSuperAccess()) return true;
          // receiverType can be an array binding in one case... see if you can change it
          if (receiverType instanceof ArrayBinding) return false;
          if (isStatic()) {
            if (depth > 0) invocationSite.setDepth(depth);
            return true; // see 1FMEPDL - return invocationSite.isTypeAccess();
          }
          if (currentType == receiverErasure
              || receiverErasure.findSuperTypeOriginatingFrom(currentType) != null) {
            if (depth > 0) invocationSite.setDepth(depth);
            return true;
          }
        }
        depth++;
        currentType = currentType.enclosingType();
      } while (currentType != null);
      return false;
    }

    if (isPrivate()) {
      // answer true if the receiverType is the declaringClass
      // AND the invocationType and the declaringClass have a common enclosingType
      receiverCheck:
      {
        if (receiverType != this.declaringClass) {
          // special tolerance for type variable direct bounds
          if (receiverType.isTypeVariable()
              && ((TypeVariableBinding) receiverType)
                  .isErasureBoundTo(this.declaringClass.erasure())) break receiverCheck;
          return false;
        }
      }

      if (invocationType != this.declaringClass) {
        ReferenceBinding outerInvocationType = invocationType;
        ReferenceBinding temp = outerInvocationType.enclosingType();
        while (temp != null) {
          outerInvocationType = temp;
          temp = temp.enclosingType();
        }

        ReferenceBinding outerDeclaringClass = (ReferenceBinding) this.declaringClass.erasure();
        temp = outerDeclaringClass.enclosingType();
        while (temp != null) {
          outerDeclaringClass = temp;
          temp = temp.enclosingType();
        }
        if (outerInvocationType != outerDeclaringClass) return false;
      }
      return true;
    }

    // isDefault()
    PackageBinding declaringPackage = this.declaringClass.fPackage;
    if (invocationType.fPackage != declaringPackage) return false;

    // receiverType can be an array binding in one case... see if you can change it
    if (receiverType instanceof ArrayBinding) return false;
    TypeBinding originalDeclaringClass = this.declaringClass.original();
    ReferenceBinding currentType = (ReferenceBinding) receiverType;
    do {
      if (originalDeclaringClass == currentType.original()) return true;
      PackageBinding currentPackage = currentType.fPackage;
      // package could be null for wildcards/intersection types, ignore and recurse in superclass
      if (currentPackage != null && currentPackage != declaringPackage) return false;
    } while ((currentType = currentType.superclass()) != null);
    return false;
  }
  /** Perform inference of generic method type parameters and/or expected type */
  public static MethodBinding computeCompatibleMethod(
      MethodBinding originalMethod,
      TypeBinding[] arguments,
      Scope scope,
      InvocationSite invocationSite) {
    ParameterizedGenericMethodBinding methodSubstitute;
    TypeVariableBinding[] typeVariables = originalMethod.typeVariables;
    TypeBinding[] substitutes = invocationSite.genericTypeArguments();
    TypeBinding[] uncheckedArguments = null;
    computeSubstitutes:
    {
      if (substitutes != null) {
        // explicit type arguments got supplied
        if (substitutes.length != typeVariables.length) {
          // incompatible due to wrong arity
          return new ProblemMethodBinding(
              originalMethod,
              originalMethod.selector,
              substitutes,
              ProblemReasons.TypeParameterArityMismatch);
        }
        methodSubstitute =
            scope.environment().createParameterizedGenericMethod(originalMethod, substitutes);
        break computeSubstitutes;
      }
      // perform type argument inference (15.12.2.7)
      // initializes the map of substitutes (var --> type[][]{ equal, extends, super}
      TypeBinding[] parameters = originalMethod.parameters;
      InferenceContext inferenceContext = new InferenceContext(originalMethod);
      methodSubstitute =
          inferFromArgumentTypes(scope, originalMethod, arguments, parameters, inferenceContext);
      if (methodSubstitute == null) return null;

      // substitutes may hold null to denote unresolved vars, but null arguments got replaced with
      // respective original variable in param method
      // 15.12.2.8 - inferring unresolved type arguments
      if (inferenceContext.hasUnresolvedTypeArgument()) {
        if (inferenceContext.isUnchecked) { // only remember unchecked status post 15.12.2.7
          int length = inferenceContext.substitutes.length;
          System.arraycopy(
              inferenceContext.substitutes,
              0,
              uncheckedArguments = new TypeBinding[length],
              0,
              length);
        }
        if (methodSubstitute.returnType != TypeBinding.VOID) {
          TypeBinding expectedType = null;
          // if message invocation has expected type
          if (invocationSite instanceof MessageSend) {
            MessageSend message = (MessageSend) invocationSite;
            expectedType = message.expectedType;
          }
          if (expectedType != null) {
            // record it was explicit from context, as opposed to assumed by default (see below)
            inferenceContext.hasExplicitExpectedType = true;
          } else {
            expectedType = scope.getJavaLangObject(); // assume Object by default
          }
          inferenceContext.expectedType = expectedType;
        }
        methodSubstitute = methodSubstitute.inferFromExpectedType(scope, inferenceContext);
        if (methodSubstitute == null) return null;
      }
    }

    // bounds check
    for (int i = 0, length = typeVariables.length; i < length; i++) {
      TypeVariableBinding typeVariable = typeVariables[i];
      TypeBinding substitute = methodSubstitute.typeArguments[i];
      if (uncheckedArguments != null && uncheckedArguments[i] == null)
        continue; // only bound check if inferred through 15.12.2.6
      switch (typeVariable.boundCheck(methodSubstitute, substitute)) {
        case TypeConstants.MISMATCH:
          // incompatible due to bound check
          int argLength = arguments.length;
          TypeBinding[] augmentedArguments =
              new TypeBinding[argLength + 2]; // append offending substitute and typeVariable
          System.arraycopy(arguments, 0, augmentedArguments, 0, argLength);
          augmentedArguments[argLength] = substitute;
          augmentedArguments[argLength + 1] = typeVariable;
          return new ProblemMethodBinding(
              methodSubstitute,
              originalMethod.selector,
              augmentedArguments,
              ProblemReasons.ParameterBoundMismatch);
        case TypeConstants.UNCHECKED:
          // tolerate unchecked bounds
          methodSubstitute.tagBits |= TagBits.HasUncheckedTypeArgumentForBoundCheck;
          break;
      }
    }
    // check presence of unchecked argument conversion a posteriori (15.12.2.6)
    return methodSubstitute;
  }
  /** Perform inference of generic method type parameters and/or expected type */
  public static MethodBinding computeCompatibleMethod(
      MethodBinding originalMethod,
      TypeBinding[] arguments,
      Scope scope,
      InvocationSite invocationSite) {
    ParameterizedGenericMethodBinding methodSubstitute;
    TypeVariableBinding[] typeVariables = originalMethod.typeVariables;
    TypeBinding[] substitutes = invocationSite.genericTypeArguments();
    InferenceContext inferenceContext = null;
    TypeBinding[] uncheckedArguments = null;
    computeSubstitutes:
    {
      if (substitutes != null) {
        // explicit type arguments got supplied
        if (substitutes.length != typeVariables.length) {
          // incompatible due to wrong arity
          return new ProblemMethodBinding(
              originalMethod,
              originalMethod.selector,
              substitutes,
              ProblemReasons.TypeParameterArityMismatch);
        }
        methodSubstitute =
            scope.environment().createParameterizedGenericMethod(originalMethod, substitutes);
        break computeSubstitutes;
      }
      // perform type argument inference (15.12.2.7)
      // initializes the map of substitutes (var --> type[][]{ equal, extends, super}
      TypeBinding[] parameters = originalMethod.parameters;
      inferenceContext = new InferenceContext(originalMethod);
      methodSubstitute =
          inferFromArgumentTypes(scope, originalMethod, arguments, parameters, inferenceContext);
      if (methodSubstitute == null) return null;

      // substitutes may hold null to denote unresolved vars, but null arguments got replaced with
      // respective original variable in param method
      // 15.12.2.8 - inferring unresolved type arguments
      if (inferenceContext.hasUnresolvedTypeArgument()) {
        if (inferenceContext.isUnchecked) { // only remember unchecked status post 15.12.2.7
          int length = inferenceContext.substitutes.length;
          System.arraycopy(
              inferenceContext.substitutes,
              0,
              uncheckedArguments = new TypeBinding[length],
              0,
              length);
        }
        if (methodSubstitute.returnType != TypeBinding.VOID) {
          TypeBinding expectedType = invocationSite.expectedType();
          if (expectedType != null) {
            // record it was explicit from context, as opposed to assumed by default (see below)
            inferenceContext.hasExplicitExpectedType = true;
          } else {
            expectedType = scope.getJavaLangObject(); // assume Object by default
          }
          inferenceContext.expectedType = expectedType;
        }
        methodSubstitute = methodSubstitute.inferFromExpectedType(scope, inferenceContext);
        if (methodSubstitute == null) return null;
      }
    }

    /* bounds check: https://bugs.eclipse.org/bugs/show_bug.cgi?id=242159, Inferred types may contain self reference
       in formal bounds. If "T extends I<T>" is a original type variable and T was inferred to be I<T> due possibly
       to under constraints and resultant glb application per 15.12.2.8, using this.typeArguments to drive the bounds
       check against itself is doomed to fail. For, the variable T would after substitution be I<I<T>> and would fail
       bounds check against I<T>. Use the inferred types from the context directly - see that there is one round of
       extra substitution that has taken place to properly substitute a remaining unresolved variable which also appears
       in a formal bound  (So we really have a bounds mismatch between I<I<T>> and I<I<I<T>>>, in the absence of a fix.)
    */
    Substitution substitution = null;
    if (inferenceContext != null) {
      substitution =
          new LingeringTypeVariableEliminator(typeVariables, inferenceContext.substitutes, scope);
    } else {
      substitution = methodSubstitute;
    }
    for (int i = 0, length = typeVariables.length; i < length; i++) {
      TypeVariableBinding typeVariable = typeVariables[i];
      TypeBinding substitute = methodSubstitute.typeArguments[i]; // retain for diagnostics
      /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=375394, To avoid spurious bounds check failures due to circularity in formal bounds,
        we should eliminate only the lingering embedded type variable references after substitution, not alien type variable references
        that constitute the inference per se.
      */
      TypeBinding substituteForChecks;
      if (substitute instanceof TypeVariableBinding) {
        substituteForChecks = substitute;
      } else {
        substituteForChecks =
            Scope.substitute(
                new LingeringTypeVariableEliminator(typeVariables, null, scope),
                substitute); // while using this for bounds check
      }

      if (uncheckedArguments != null && uncheckedArguments[i] == null)
        continue; // only bound check if inferred through 15.12.2.6
      switch (typeVariable.boundCheck(substitution, substituteForChecks, scope)) {
        case TypeConstants.MISMATCH:
          // incompatible due to bound check
          int argLength = arguments.length;
          TypeBinding[] augmentedArguments =
              new TypeBinding[argLength + 2]; // append offending substitute and typeVariable
          System.arraycopy(arguments, 0, augmentedArguments, 0, argLength);
          augmentedArguments[argLength] = substitute;
          augmentedArguments[argLength + 1] = typeVariable;
          return new ProblemMethodBinding(
              methodSubstitute,
              originalMethod.selector,
              augmentedArguments,
              ProblemReasons.ParameterBoundMismatch);
        case TypeConstants.UNCHECKED:
          // tolerate unchecked bounds
          methodSubstitute.tagBits |= TagBits.HasUncheckedTypeArgumentForBoundCheck;
          break;
      }
    }
    // check presence of unchecked argument conversion a posteriori (15.12.2.6)
    return methodSubstitute;
  }