/** * If 'hasSignature' check the return type of the bound method against the declared return type. */ public boolean checkRoleReturnType(CallinCalloutScope scope, boolean isCallout) { TypeBinding methodReturn = boundMethodReturnType(); TypeBinding resolvedReturnType = this.returnType.resolvedType; TypeBinding firstBound = null; if (resolvedReturnType.isTypeVariable()) { firstBound = ((TypeVariableBinding) resolvedReturnType).firstBound; // if declared return type is a type variable, so must the return type of the resolved method: if (!isMethodReturnTypeVariable(this.resolvedMethod)) { scope.problemReporter().differentReturnInMethodSpec(this, /*isCallout*/ false); return false; } } // the role side of a callout may indeed refine the return type // of its inherited role method: if (isCallout) if (this.returnType.resolvedType.isCompatibleWith(methodReturn)) return true; // in other cases types have to be identical: if (!MethodModel.hasUnboundedReturnType( this.resolvedMethod) // unbounded type variable always matches && !TypeAnalyzer.isSameType(scope.enclosingSourceType(), resolvedReturnType, methodReturn) && !TypeAnalyzer.isSameType(scope.enclosingSourceType(), firstBound, methodReturn)) { scope .problemReporter() .differentReturnInMethodSpec( this, ((AbstractMethodMappingDeclaration) scope.referenceContext).isCallout()); return false; } return true; }
/** * If 'hasSignature' check the return type of the bound method against the declared return type. */ public boolean checkBaseReturnType(CallinCalloutScope scope, int bindDir) { TypeBinding methodReturn = boundMethodReturnType(); if (!TypeAnalyzer.isSameType( scope.enclosingSourceType(), this.returnType.resolvedType, methodReturn)) { if (RoleTypeCreator.isCompatibleViaBaseAnchor( scope, methodReturn, this.returnType.resolvedType, bindDir)) return true; if ((methodReturn.tagBits & TagBits.HasMissingType) == 0) scope .problemReporter() .differentReturnInMethodSpec( this, ((AbstractMethodMappingDeclaration) scope.referenceContext).isCallout()); return false; } return true; }
/** * 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; }
/** * Resolve the method or field (see FieldAccessSpec). * * @param receiverType receiver of the method call. * @param scope * @param callinExpected whether this method spec is the LHS of a replace callin. * @param isBaseSide whether this method spec is the RHS (any binding kind) * @param allowEnclosing whether a method may be found in an enclosing type of receiverType * @return the resolved method (may be problem method) or null */ public MethodBinding resolveFeature( ReferenceBinding receiverType, BlockScope scope, boolean callinExpected, boolean isBaseSide, boolean allowEnclosing) { // getRealClass() is used, because decapsulation needs to find private methods, // which for roles are found only in the class part. ReferenceBinding receiverClass = receiverType.getRealClass(); boolean isConstructorSpec = CharOperation.equals(this.selector, receiverClass.sourceName()); char[] realSelector = isConstructorSpec ? TypeConstants.INIT : this.selector; if (this.hasSignature) { TypeBinding[] enhancedParameters = this.parameters; // first chance: try enhanced: enhancedParameters = MethodSignatureEnhancer.enhanceParameters(scope, this.parameters); CompilationResult compilationResult = scope.referenceContext().compilationResult(); CheckPoint cp = compilationResult.getCheckPoint(scope.referenceContext()); this.resolvedMethod = TypeAnalyzer.findMethod( scope, receiverClass, realSelector, enhancedParameters, isBaseSide, isBaseSide ? this : null); if (!this.resolvedMethod.isValidBinding() && this.resolvedMethod.problemId() == ProblemReasons.NotFound) { // second+ chance: try plain: while (receiverClass != null) { compilationResult.rollBack(cp); MethodBinding plainMethod = TypeAnalyzer.findMethod( scope, receiverClass, realSelector, this.parameters, isBaseSide, isBaseSide ? this : null); if (!callinExpected) { this.resolvedMethod = plainMethod; } else { if (plainMethod != null && plainMethod.isValidBinding()) scope.problemReporter().replaceMappingToNonCallin(this, plainMethod); // mark the ProblemMethodBinding consistently to what we have been looking for last: this.resolvedMethod.modifiers |= ExtraCompilerModifiers.AccCallin | ClassFileConstants.AccStatic; } if (plainMethod != null && plainMethod.isValidBinding()) break; if (allowEnclosing) receiverClass = receiverClass.enclosingType(); else receiverClass = null; } } } else { CompilationResult compilationResult = scope.referenceContext().compilationResult(); CheckPoint cp = compilationResult.getCheckPoint(scope.referenceContext()); while (receiverClass != null) { this.resolvedMethod = receiverClass.getMethod(scope, realSelector); if (this.resolvedMethod != null && this.resolvedMethod.isValidBinding()) break; // good if (!allowEnclosing) break; // bad compilationResult.rollBack(cp); receiverClass = receiverClass.enclosingType(); } } if (this.resolvedMethod != null) { if (this.resolvedMethod.isValidBinding()) { // check visibility of role-side in callin: if (!isBaseSide && scope.referenceContext() instanceof CallinMappingDeclaration && !this.resolvedMethod.canBeSeenBy(this, scope)) { scope.problemReporter().invisibleMethod(this, this.resolvedMethod); this.resolvedMethod = new ProblemMethodBinding( this.resolvedMethod, this.selector, this.parameters, ProblemReasons.NotVisible); } } if (!this.resolvedMethod.isValidBinding() && this.resolvedMethod.declaringClass == null) this.resolvedMethod.declaringClass = receiverClass; // needed for computeUniqueKey (via // CallinCalloutBinding.computeUniqueKey) } return this.resolvedMethod; }