Beispiel #1
0
 /**
  * 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
  }