private static void checkAlternateBinding( BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] originalArgumentTypes, TypeBinding[] alternateArgumentTypes, final InvocationSite invocationSite) { InvocationSite fakeInvocationSite = new InvocationSite() { public TypeBinding[] genericTypeArguments() { return null; } public boolean isSuperAccess() { return invocationSite.isSuperAccess(); } public boolean isTypeAccess() { return invocationSite.isTypeAccess(); } public void setActualReceiverType(ReferenceBinding actualReceiverType) { /* ignore */ } public void setDepth(int depth) { /* ignore */ } public void setFieldIndex(int depth) { /* ignore */ } public int sourceStart() { return 0; } public int sourceEnd() { return 0; } }; MethodBinding bindingIfNoCast; if (binding.isConstructor()) { bindingIfNoCast = scope.getConstructor( (ReferenceBinding) receiverType, alternateArgumentTypes, fakeInvocationSite); } else { bindingIfNoCast = receiver.isImplicitThis() ? scope.getImplicitMethod( binding.selector, alternateArgumentTypes, fakeInvocationSite) : scope.getMethod( receiverType, binding.selector, alternateArgumentTypes, fakeInvocationSite); } if (bindingIfNoCast == binding) { int argumentLength = originalArgumentTypes.length; if (binding.isVarargs()) { int paramLength = binding.parameters.length; if (paramLength == argumentLength) { int varargsIndex = paramLength - 1; ArrayBinding varargsType = (ArrayBinding) binding.parameters[varargsIndex]; TypeBinding lastArgType = alternateArgumentTypes[varargsIndex]; // originalType may be compatible already, but cast mandated // to clarify between varargs/non-varargs call if (varargsType.dimensions != lastArgType.dimensions()) { return; } if (lastArgType.isCompatibleWith(varargsType.elementsType()) && lastArgType.isCompatibleWith(varargsType)) { return; } } } for (int i = 0; i < argumentLength; i++) { if (originalArgumentTypes[i] != alternateArgumentTypes[i]) { scope.problemReporter().unnecessaryCast((CastExpression) arguments[i]); } } } }
/** 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); } } } } }
public static boolean checkInvocationArguments( BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding method, Expression[] arguments, TypeBinding[] argumentTypes, boolean argsContainCast, InvocationSite invocationSite) { TypeBinding[] params = method.parameters; int paramLength = params.length; boolean isRawMemberInvocation = !method.isStatic() && !receiverType.isUnboundWildcard() && method.declaringClass.isRawType() && method.hasSubstitutedParameters(); boolean uncheckedBoundCheck = (method.tagBits & TagBits.HasUncheckedTypeArgumentForBoundCheck) != 0; MethodBinding rawOriginalGenericMethod = null; if (!isRawMemberInvocation) { if (method instanceof ParameterizedGenericMethodBinding) { ParameterizedGenericMethodBinding paramMethod = (ParameterizedGenericMethodBinding) method; if (paramMethod.isRaw && method.hasSubstitutedParameters()) { rawOriginalGenericMethod = method.original(); } } } int invocationStatus = INVOCATION_ARGUMENT_OK; if (arguments == null) { if (method.isVarargs()) { TypeBinding parameterType = ((ArrayBinding) params[paramLength - 1]) .elementsType(); // no element was supplied for vararg parameter if (!parameterType.isReifiable()) { scope .problemReporter() .unsafeGenericArrayForVarargs(parameterType, (ASTNode) invocationSite); } } } else { if (method.isVarargs()) { // 4 possibilities exist for a call to the vararg method foo(int i, long ... value) : // foo(1), foo(1, 2), foo(1, 2, 3, 4) & foo(1, new long[] {1, 2}) int lastIndex = paramLength - 1; for (int i = 0; i < lastIndex; i++) { TypeBinding originalRawParam = rawOriginalGenericMethod == null ? null : rawOriginalGenericMethod.parameters[i]; invocationStatus |= checkInvocationArgument( scope, arguments[i], params[i], argumentTypes[i], originalRawParam); } int argLength = arguments.length; if (lastIndex < argLength) { // vararg argument was provided TypeBinding parameterType = params[lastIndex]; TypeBinding originalRawParam = null; if (paramLength != argLength || parameterType.dimensions() != argumentTypes[lastIndex].dimensions()) { parameterType = ((ArrayBinding) parameterType) .elementsType(); // single element was provided for vararg parameter if (!parameterType.isReifiable()) { scope .problemReporter() .unsafeGenericArrayForVarargs(parameterType, (ASTNode) invocationSite); } originalRawParam = rawOriginalGenericMethod == null ? null : ((ArrayBinding) rawOriginalGenericMethod.parameters[lastIndex]) .elementsType(); } for (int i = lastIndex; i < argLength; i++) { invocationStatus |= checkInvocationArgument( scope, arguments[i], parameterType, argumentTypes[i], originalRawParam); } } if (paramLength == argLength) { // 70056 int varargsIndex = paramLength - 1; ArrayBinding varargsType = (ArrayBinding) params[varargsIndex]; TypeBinding lastArgType = argumentTypes[varargsIndex]; int dimensions; if (lastArgType == TypeBinding.NULL) { if (!(varargsType.leafComponentType().isBaseType() && varargsType.dimensions() == 1)) scope.problemReporter().varargsArgumentNeedCast(method, lastArgType, invocationSite); } else if (varargsType.dimensions <= (dimensions = lastArgType.dimensions())) { if (lastArgType.leafComponentType().isBaseType()) { dimensions--; } if (varargsType.dimensions < dimensions) { scope.problemReporter().varargsArgumentNeedCast(method, lastArgType, invocationSite); } else if (varargsType.dimensions == dimensions && lastArgType != varargsType && lastArgType.leafComponentType().erasure() != varargsType.leafComponentType.erasure() && lastArgType.isCompatibleWith(varargsType.elementsType()) && lastArgType.isCompatibleWith(varargsType)) { scope.problemReporter().varargsArgumentNeedCast(method, lastArgType, invocationSite); } } } } else { for (int i = 0; i < paramLength; i++) { TypeBinding originalRawParam = rawOriginalGenericMethod == null ? null : rawOriginalGenericMethod.parameters[i]; invocationStatus |= checkInvocationArgument( scope, arguments[i], params[i], argumentTypes[i], originalRawParam); } } if (argsContainCast) { CastExpression.checkNeedForArgumentCasts( scope, receiver, receiverType, method, arguments, argumentTypes, invocationSite); } } if ((invocationStatus & INVOCATION_ARGUMENT_WILDCARD) != 0) { scope .problemReporter() .wildcardInvocation((ASTNode) invocationSite, receiverType, method, argumentTypes); } else if (!method.isStatic() && !receiverType.isUnboundWildcard() && method.declaringClass.isRawType() && method.hasSubstitutedParameters()) { scope.problemReporter().unsafeRawInvocation((ASTNode) invocationSite, method); } else if (rawOriginalGenericMethod != null || uncheckedBoundCheck || ((invocationStatus & INVOCATION_ARGUMENT_UNCHECKED) != 0 && method instanceof ParameterizedGenericMethodBinding /*&& method.returnType != scope.environment().convertToRawType(method.returnType.erasure(), true)*/ )) { scope .problemReporter() .unsafeRawGenericMethodInvocation((ASTNode) invocationSite, method, argumentTypes); return true; } return false; }