/** Generate invocation arguments, considering varargs methods */
  public void generateArguments(
      MethodBinding binding,
      Expression[] arguments,
      BlockScope currentScope,
      CodeStream codeStream) {
    if (binding.isVarargs()) {
      // 5 possibilities exist for a call to the vararg method foo(int i, int ... value) :
      //      foo(1), foo(1, null), foo(1, 2), foo(1, 2, 3, 4) & foo(1, new int[] {1, 2})
      TypeBinding[] params = binding.parameters;
      int paramLength = params.length;
      int varArgIndex = paramLength - 1;
      for (int i = 0; i < varArgIndex; i++) {
        arguments[i].generateCode(currentScope, codeStream, true);
      }
      ArrayBinding varArgsType =
          (ArrayBinding) params[varArgIndex]; // parameterType has to be an array type
      ArrayBinding codeGenVarArgsType = (ArrayBinding) binding.parameters[varArgIndex].erasure();
      int elementsTypeID = varArgsType.elementsType().id;
      int argLength = arguments == null ? 0 : arguments.length;

      if (argLength > paramLength) {
        // right number but not directly compatible or too many arguments - wrap extra into array
        // called with (argLength - lastIndex) elements : foo(1, 2) or foo(1, 2, 3, 4)
        // need to gen elements into an array, then gen each remaining element into created array
        codeStream.generateInlinedValue(argLength - varArgIndex);
        codeStream.newArray(null, codeGenVarArgsType); // create a mono-dimensional array
        for (int i = varArgIndex; i < argLength; i++) {
          codeStream.dup();
          codeStream.generateInlinedValue(i - varArgIndex);
          arguments[i].generateCode(currentScope, codeStream, true);
          codeStream.arrayAtPut(elementsTypeID, false);
        }
      } else if (argLength == paramLength) {
        // right number of arguments - could be inexact - pass argument as is
        TypeBinding lastType = arguments[varArgIndex].resolvedType;
        if (lastType == TypeBinding.NULL
            || (varArgsType.dimensions() == lastType.dimensions()
                && lastType.isCompatibleWith(varArgsType))) {
          // foo(1, new int[]{2, 3}) or foo(1, null) --> last arg is passed as-is
          arguments[varArgIndex].generateCode(currentScope, codeStream, true);
        } else {
          // right number but not directly compatible or too many arguments - wrap extra into array
          // need to gen elements into an array, then gen each remaining element into created array
          codeStream.generateInlinedValue(1);
          codeStream.newArray(null, codeGenVarArgsType); // create a mono-dimensional array
          codeStream.dup();
          codeStream.generateInlinedValue(0);
          arguments[varArgIndex].generateCode(currentScope, codeStream, true);
          codeStream.arrayAtPut(elementsTypeID, false);
        }
      } else { // not enough arguments - pass extra empty array
        // scenario: foo(1) --> foo(1, new int[0])
        // generate code for an empty array of parameterType
        codeStream.generateInlinedValue(0);
        codeStream.newArray(null, codeGenVarArgsType); // create a mono-dimensional array
      }
    } else if (arguments != null) { // standard generation for method arguments
      for (int i = 0, max = arguments.length; i < max; i++)
        arguments[i].generateCode(currentScope, codeStream, true);
    }
  }
 /** Analysing arguments of MessageSend, ExplicitConstructorCall, AllocationExpression. */
 protected void analyseArguments(
     BlockScope currentScope,
     FlowContext flowContext,
     FlowInfo flowInfo,
     MethodBinding methodBinding,
     Expression[] arguments) {
   // compare actual null-status against parameter annotations of the called method:
   if (arguments != null) {
     CompilerOptions compilerOptions = currentScope.compilerOptions();
     boolean considerTypeAnnotations =
         compilerOptions.sourceLevel >= ClassFileConstants.JDK1_8
             && compilerOptions.isAnnotationBasedNullAnalysisEnabled;
     boolean hasJDK15NullAnnotations = methodBinding.parameterNonNullness != null;
     int numParamsToCheck = methodBinding.parameters.length;
     if (considerTypeAnnotations || hasJDK15NullAnnotations) {
       // check if varargs need special treatment:
       boolean passThrough = false;
       if (methodBinding.isVarargs()) {
         int varArgPos = numParamsToCheck - 1;
         // this if-block essentially copied from generateArguments(..):
         if (numParamsToCheck == arguments.length) {
           TypeBinding varArgsType = methodBinding.parameters[varArgPos];
           TypeBinding lastType = arguments[varArgPos].resolvedType;
           if (lastType == TypeBinding.NULL
               || (varArgsType.dimensions() == lastType.dimensions()
                   && lastType.isCompatibleWith(varArgsType)))
             passThrough = true; // pass directly as-is
         }
         if (!passThrough)
           numParamsToCheck--; // with non-passthrough varargs last param is fed from individual
         // args -> don't check
       }
     }
     if (considerTypeAnnotations) {
       for (int i = 0; i < numParamsToCheck; i++) {
         TypeBinding expectedType = methodBinding.parameters[i];
         Expression argument = arguments[i];
         // prefer check based on type annotations:
         int severity = findNullTypeAnnotationMismatch(expectedType, argument.resolvedType);
         if (severity > 0) {
           // immediate reporting:
           currentScope
               .problemReporter()
               .nullityMismatchingTypeAnnotation(
                   argument,
                   argument.resolvedType,
                   expectedType,
                   severity == 1,
                   currentScope.environment());
           // next check flow-based null status against null JDK15-style annotations:
         } else if (hasJDK15NullAnnotations
             && methodBinding.parameterNonNullness[i] == Boolean.TRUE) {
           int nullStatus =
               argument.nullStatus(
                   flowInfo,
                   flowContext); // slight loss of precision: should also use the null info from
           // the receiver.
           if (nullStatus != FlowInfo.NON_NULL) // if required non-null is not provided
           flowContext.recordNullityMismatch(
                 currentScope, argument, argument.resolvedType, expectedType, nullStatus);
         }
       }
     } else if (hasJDK15NullAnnotations) {
       for (int i = 0; i < numParamsToCheck; i++) {
         if (methodBinding.parameterNonNullness[i] == Boolean.TRUE) {
           TypeBinding expectedType = methodBinding.parameters[i];
           Expression argument = arguments[i];
           int nullStatus =
               argument.nullStatus(
                   flowInfo,
                   flowContext); // slight loss of precision: should also use the null info from
           // the receiver.
           if (nullStatus != FlowInfo.NON_NULL) // if required non-null is not provided
           flowContext.recordNullityMismatch(
                 currentScope, argument, argument.resolvedType, expectedType, nullStatus);
         }
       }
     }
   }
 }
示例#3
0
  public TypeBinding resolveTypeExpecting(BlockScope scope, TypeBinding expectedType) {
    // Array initializers can only occur on the right hand side of an assignment
    // expression, therefore the expected type contains the valid information
    // concerning the type that must be enforced by the elements of the array initializer.

    // this method is recursive... (the test on isArrayType is the stop case)

    this.constant = Constant.NotAConstant;

    if (expectedType instanceof ArrayBinding) {
      // allow new List<?>[5]
      if ((this.bits & IsAnnotationDefaultValue)
          == 0) { // annotation default value need only to be commensurate JLS9.7
        // allow new List<?>[5] - only check for generic array when no initializer, since also
        // checked inside initializer resolution
        TypeBinding leafComponentType = expectedType.leafComponentType();
        if (!leafComponentType.isReifiable()) {
          scope.problemReporter().illegalGenericArray(leafComponentType, this);
        }
      }
      this.resolvedType = this.binding = (ArrayBinding) expectedType;
      if (this.expressions == null) return this.binding;
      TypeBinding elementType = this.binding.elementsType();
      for (int i = 0, length = this.expressions.length; i < length; i++) {
        Expression expression = this.expressions[i];
        expression.setExpectedType(elementType);
        TypeBinding expressionType =
            expression instanceof ArrayInitializer
                ? expression.resolveTypeExpecting(scope, elementType)
                : expression.resolveType(scope);
        if (expressionType == null) continue;

        // Compile-time conversion required?
        if (elementType
            != expressionType) // must call before computeConversion() and typeMismatchError()
        scope.compilationUnitScope().recordTypeConversion(elementType, expressionType);

        if (expression.isConstantValueOfTypeAssignableToType(expressionType, elementType)
            || expressionType.isCompatibleWith(elementType)) {
          expression.computeConversion(scope, elementType, expressionType);
        } else if (scope.isBoxingCompatibleWith(expressionType, elementType)
            || (expressionType.isBaseType() // narrowing then boxing ?
                && scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5 // autoboxing
                && !elementType.isBaseType()
                && expression.isConstantValueOfTypeAssignableToType(
                    expressionType, scope.environment().computeBoxingType(elementType)))) {
          expression.computeConversion(scope, elementType, expressionType);
        } else {
          scope.problemReporter().typeMismatchError(expressionType, elementType, expression, null);
        }
      }
      return this.binding;
    }

    // infer initializer type for error reporting based on first element
    TypeBinding leafElementType = null;
    int dim = 1;
    if (this.expressions == null) {
      leafElementType = scope.getJavaLangObject();
    } else {
      Expression expression = this.expressions[0];
      while (expression != null && expression instanceof ArrayInitializer) {
        dim++;
        Expression[] subExprs = ((ArrayInitializer) expression).expressions;
        if (subExprs == null) {
          leafElementType = scope.getJavaLangObject();
          expression = null;
          break;
        }
        expression = ((ArrayInitializer) expression).expressions[0];
      }
      if (expression != null) {
        leafElementType = expression.resolveType(scope);
      }
      // fault-tolerance - resolve other expressions as well
      for (int i = 1, length = this.expressions.length; i < length; i++) {
        expression = this.expressions[i];
        if (expression != null) {
          expression.resolveType(scope);
        }
      }
    }
    if (leafElementType != null) {
      this.resolvedType = scope.createArrayType(leafElementType, dim);
      if (expectedType != null)
        scope.problemReporter().typeMismatchError(this.resolvedType, expectedType, this, null);
    }
    return null;
  }