/** * If 'hasSignature' check the parameter types of the bound method against the declared parameter * types. */ public boolean checkParameterTypes(CallinCalloutScope scope, boolean isBase) { // retrieve (un-enhanced) parameters from the actual resolved method: TypeBinding[] realParameters = this.resolvedMethod.getSourceParameters(); for (int i = 0; i < realParameters.length; i++) { TypeReference specifiedArgType = this.arguments[i].type; TypeBinding realParameter = realParameters[i]; if (!realParameter.isValidBinding() || specifiedArgType.resolvedType == null) continue; ReferenceBinding baseclass = scope.enclosingReceiverType().baseclass(); if (isBase && baseclass != null && baseclass.isTeam() && realParameter.isRole()) realParameter = TeamModel.strengthenRoleType(baseclass, realParameter); if (!TypeAnalyzer.isSameType( scope.enclosingSourceType(), specifiedArgType.resolvedType, realParameter)) { scope .problemReporter() .differentParamInMethodSpec( this, specifiedArgType, realParameter, ((AbstractMethodMappingDeclaration) scope.referenceContext).isCallout()); return false; } } return true; }
/** * Check all parameters in methodSpec against the resolved role method. Also record which * parameters (including result) need translation (lifting/lowering). * * <p>Pre: not called if parameter mappings are present. * * @param methodSpec */ protected boolean internalCheckParametersCompatibility( MethodSpec methodSpec, TypeBinding[] roleParams, TypeBinding[] baseParams) { if (baseParams.length < roleParams.length) { this.scope .problemReporter() .tooFewArgumentsInMethodMapping(this.roleMethodSpec, methodSpec, false /*callout*/); this.binding.tagBits |= TagBits.HasMappingIncompatibility; return false; } else { // before modifying the parameters array copy it: System.arraycopy( this.roleMethodSpec.parameters, 0, this.roleMethodSpec.parameters = new TypeBinding[roleParams.length], 0, roleParams.length); for (int j = 0; j < roleParams.length; j++) { TypeBinding baseParam = baseParams[j]; TypeBinding roleParam = roleParams[j]; if (baseParam.dimensions() != roleParam.dimensions()) { this.scope .problemReporter() .incompatibleMappedArgument( baseParam, roleParam, this.roleMethodSpec, j, /*callout*/ false); this.binding.tagBits |= TagBits.HasMappingIncompatibility; continue; // no real type checking needed. } TypeBinding baseLeaf = baseParam.leafComponentType(); TypeBinding roleLeaf = roleParam.leafComponentType(); ASTNode location = (methodSpec.hasSignature) ? (ASTNode) methodSpec.arguments[j] : methodSpec; boolean compatibilityViaBaseAnchor = false; boolean hasReportedError = false; boolean isTypeVariable = false; try { // capture continue exits // unbound type variable matches everything: if (roleParam.isTypeVariable()) { TypeVariableBinding typeVariableBinding = (TypeVariableBinding) roleParam; if (typeVariableBinding.firstBound == null) continue; // use bound for type checking below, yet need not check two-way compatibility: isTypeVariable = true; roleLeaf = typeVariableBinding.firstBound.leafComponentType(); } int dimensions = roleParam.dimensions(); if (baseLeaf.isCompatibleWith(roleLeaf)) { this.roleMethodSpec.parameters[j] = roleParam; continue; } if (RoleTypeCreator.isCompatibleViaBaseAnchor( this.scope, baseLeaf, roleLeaf, TokenNameBINDIN)) { this.roleMethodSpec.parameters[j] = roleParam; compatibilityViaBaseAnchor = true; continue; } TypeBinding roleToLiftTo = null; if (isReplaceCallin()) { TypeBinding roleSideType = roleLeaf; if (roleSideType.isRole()) { ReferenceBinding roleRef = (ReferenceBinding) roleSideType; roleRef = (ReferenceBinding) TeamModel.strengthenRoleType(this.scope.enclosingReceiverType(), roleRef); if (TypeBinding.equalsEquals(roleRef.baseclass(), baseLeaf)) { if (dimensions > 0) { if (roleRef instanceof DependentTypeBinding) roleToLiftTo = ((DependentTypeBinding) roleRef).getArrayType(dimensions); else roleToLiftTo = this.scope.createArrayType(roleRef, dimensions); // FIXME(SH): is this OK? } else { roleToLiftTo = roleRef; } } } } else { // this uses OTJLD 2.3.3(a) adaptation which is not reversible, ie., not usable for // replace: roleToLiftTo = TeamModel.getRoleToLiftTo(this.scope, baseParam, roleParam, false, location); } if (roleToLiftTo != null) { // success by translation methodSpec.argNeedsTranslation[j] = true; this.roleMethodSpec.argNeedsTranslation[j] = true; this.roleMethodSpec.parameters[j] = roleToLiftTo; // this applies to all bindings // still need to check for ambiguity/abstract role: ReferenceBinding enclosingTeam = this.scope.enclosingSourceType().enclosingType(); int iProblem = enclosingTeam .getTeamModel() .canLiftingFail((ReferenceBinding) roleToLiftTo.leafComponentType()); if (iProblem > 0) addRoleLiftingProblem((ReferenceBinding) roleToLiftTo.leafComponentType(), iProblem); continue; } // check auto(un)boxing: if (this.scope.isBoxingCompatibleWith(baseLeaf, roleLeaf)) continue; if (roleParam instanceof ReferenceBinding) { ReferenceBinding roleRef = (ReferenceBinding) roleParam; if (roleRef.isRole() && roleRef.baseclass() != null) { this.scope .problemReporter() .typeMismatchErrorPotentialLift( location, baseParam, roleParam, roleRef.baseclass()); hasReportedError = true; continue; } } // no compatibility detected: this.scope .problemReporter() .incompatibleMappedArgument( baseParam, roleParam, this.roleMethodSpec, j, /*callout*/ false); hasReportedError = true; } finally { if (hasReportedError) this.binding.tagBits |= TagBits.HasMappingIncompatibility; // regardless of continue, check this last because it is the least precise message: if (!hasReportedError && baseLeaf.isCompatibleWith(roleLeaf)) { if (isReplaceCallin() && !isTypeVariable) { boolean twowayCompatible = compatibilityViaBaseAnchor ? RoleTypeCreator.isCompatibleViaBaseAnchor( this.scope, baseLeaf, roleLeaf, TokenNameBINDOUT) : roleLeaf.isCompatibleWith(baseLeaf); if (!twowayCompatible) { // requires two-way compatibility (see additional paragraph in 4.5(d)) this.scope .problemReporter() .typesNotTwowayCompatibleInReplace(baseParam, roleParam, location, j); } } } } } } return true; // unused in the callin case }