/** Check OTJLD 4.4(b) "Callin parameter mapping / Restrictions for callin replace bindings" */ public void checkResultMapping() { // for replace callins, a "result" mapping is not allowed, // unless an expected result is otherwise missing. if (this.mappings == null) return; for (MethodSpec baseSpec : this.baseMethodSpecs) { for (int i = 0; i < this.mappings.length; i++) { if (CharOperation.equals(this.mappings[i].ident.token, IOTConstants.RESULT)) { this.isResultMapped = true; // OTJLD 4.4(b): "If the base method declares a result, then ... if (baseSpec.resolvedType() != TypeBinding.VOID) { // * if the role method also declares a result, if (this.roleMethodSpec.resolvedType() != TypeBinding.VOID) { Expression resultExpr = this.mappings[i].expression; // => result must be mapped to itself if (!(resultExpr instanceof ResultReference)) { this.scope.problemReporter().nonResultExpressionInReplaceResult(resultExpr); this.binding.tagBits |= TagBits.HasMappingIncompatibility; } } // no else because: // * if the role method does not declare a result, // an arbitrary expression may be mapped to result } else { this.scope .problemReporter() .resultMappingForVoidMethod(this, baseSpec, this.mappings[i]); this.binding.tagBits |= TagBits.HasMappingIncompatibility; } } } } }
public StringBuffer printShort(int indent, StringBuffer output, MethodSpec baseMethodSpec) { printIndent(indent, output); this.roleMethodSpec.print(0, output); output.append(getCallinModifier()); baseMethodSpec.print(0, output); return output; }
/** Get incoming arguments that are not used by the role side. */ public int[] getUnmappedBasePositions(MethodSpec baseSpec) { int baseArgCount = baseSpec.resolvedParameters().length; // role-as-base enhancement filtered out int[] result = new int[baseArgCount]; int idx = 0; for (int i = 0; i < baseArgCount; i++) { if (!isMapped(i)) result[idx++] = i; } if (idx < baseArgCount) { System.arraycopy(result, 0, result = new int[idx], 0, idx); } return result; }
/** * Get the expression that is being mapped to "result" Precondition: parameter mappings are * present. * * @return expression (in this expression "result" should be a legal name). */ public Expression getResultExpression( MethodSpec baseMethodSpec, boolean needBoxing, AstGenerator gen) { if (baseMethodSpec.resolvedType() == TypeBinding.VOID) // binding non-void to void: just return "result" which will be ignored any way. return new SingleNameReference( IOTConstants.RESULT, (((long) this.roleMethodSpec.sourceStart) << 32) + this.roleMethodSpec.sourceEnd); Expression resultExpr = null; for (int i = 0; i < this.mappings.length; i++) { if (this.mappings[i].isUsedFor(baseMethodSpec)) // already used mapping? continue; if (CharOperation.equals(this.mappings[i].ident.token, IOTConstants.RESULT)) { if (resultExpr != null) { this.scope .problemReporter() .duplicateParamMapping(this.mappings[i], IOTConstants.RESULT, /*isCallout*/ false); this.binding.tagBits |= TagBits.HasMappingIncompatibility; } else { resultExpr = this.mappings[i].expression; } } } if (resultExpr != null) { // check undefined 'result' expression: if (this.roleMethodSpec.resolvedType() == TypeBinding.VOID && (resultExpr instanceof ResultReference)) { this.scope .problemReporter() .resultNotDefinedForVoidMethod( resultExpr, this.roleMethodSpec.selector, false /*callout*/); this.binding.tagBits |= TagBits.HasMappingIncompatibility; } } if (resultExpr != null && !this.isResultMapped // in param-mapping role-side "result" already has boxed type && needBoxing) resultExpr = gen.createBoxing(resultExpr, (BaseTypeBinding) baseMethodSpec.resolvedType()); return resultExpr; }
public boolean hasStaticBaseMethod() { for (MethodSpec spec : this.baseMethodSpecs) if (spec.isStatic()) return true; return false; }
/** Check whether the baseSpec has a result compatible via replace. */ public void checkResultForReplace(MethodSpec baseSpec) { boolean typeIdentityRequired = true; // default unless return is type variable // covariant return requires a fresh type parameter for the role's return type: if (baseSpec.covariantReturn && this.roleMethodSpec.returnType != null) { TypeBinding resolvedRoleReturn = this.roleMethodSpec.returnType.resolvedType; if (resolvedRoleReturn != null) { if (!resolvedRoleReturn.isTypeVariable()) { this.scope .problemReporter() .covariantReturnRequiresTypeParameter(this.roleMethodSpec.returnType); this.binding.tagBits |= TagBits.HasMappingIncompatibility; } else { // is the type parameter "fresh"? for (Argument arg : this.roleMethodSpec.arguments) { if (typeUsesTypeVariable( arg.type.resolvedType.leafComponentType(), resolvedRoleReturn)) { this.scope .problemReporter() .duplicateUseOfTypeVariableInCallin( this.roleMethodSpec.returnType, resolvedRoleReturn); this.binding.tagBits |= TagBits.HasMappingIncompatibility; break; } } } } } TypeVariableBinding returnVariable = MethodModel.checkedGetReturnTypeVariable(this.roleMethodSpec.resolvedMethod); if (returnVariable != null) { // unbounded type variable always matches: if (returnVariable.firstBound == null) return; // in case of type variable only one-way compatibility is needed even for replace: typeIdentityRequired = false; } // now go for the actual type checking: TypeBinding baseReturn = baseSpec.resolvedMethod.returnType; TypeBinding roleReturn = MethodModel.getReturnType(this.roleMethodSpec.resolvedMethod); TypeBinding roleReturnLeaf = roleReturn != null ? roleReturn.leafComponentType() : null; if (roleReturnLeaf instanceof ReferenceBinding && ((ReferenceBinding) roleReturnLeaf).isRole()) { // strengthen: roleReturnLeaf = TeamModel.strengthenRoleType(this.scope.enclosingSourceType(), roleReturnLeaf); if (roleReturnLeaf == null) { // FIXME(SH): testcase and better handling String roleReturnName = roleReturn != null ? new String(roleReturn.readableName()) : "null return type"; //$NON-NLS-1$ throw new InternalCompilerError( "role strengthening for " + roleReturnName + " -> null"); // $NON-NLS-1$ //$NON-NLS-2$ } // bound roles use their topmost bound super: if (((ReferenceBinding) roleReturnLeaf).baseclass() != null) roleReturnLeaf = RoleModel.getTopmostBoundRole(this.scope, (ReferenceBinding) roleReturnLeaf); // need the RTB: if (!(roleReturnLeaf instanceof DependentTypeBinding)) roleReturnLeaf = RoleTypeCreator.maybeWrapUnqualifiedRoleType( roleReturnLeaf, this.scope.enclosingSourceType()); // array? int dims = roleReturn != null ? roleReturn.dimensions() : 0; if (dims == 0) { roleReturn = roleReturnLeaf; this.realRoleReturn = roleReturnLeaf; } else { roleReturn = ((DependentTypeBinding) roleReturnLeaf).getArrayType(dims); this.realRoleReturn = ((DependentTypeBinding) roleReturnLeaf).getArrayType(dims); } } if (baseReturn == null || baseReturn == TypeBinding.VOID) { // OTJLD 4.4(b): "A callin method bound with replace // to a base method returning void // must not declare a non-void result." if (!(roleReturn == null || roleReturn == TypeBinding.VOID)) { this.scope.problemReporter().callinIllegalRoleReturnReturn(baseSpec, this.roleMethodSpec); this.binding.tagBits |= TagBits.HasMappingIncompatibility; } } else { if (roleReturn == null || roleReturn == TypeBinding.VOID) { this.baseMethodNeedingResultFromBasecall = baseSpec; // will be reported in checkBaseResult(). return; } TypeBinding baseLeaf = baseReturn.leafComponentType(); if (baseLeaf instanceof DependentTypeBinding) { // instantiate relative to Role._OT$base: ReferenceBinding enclosingRole = this.scope.enclosingSourceType(); FieldBinding baseField = enclosingRole.getField(IOTConstants._OT_BASE, true); if (baseField != null && baseField.isValidBinding()) baseReturn = baseField.getRoleTypeBinding((ReferenceBinding) baseLeaf, baseReturn.dimensions()); } // check auto(un)boxing: if (this.scope.isBoxingCompatibleWith(roleReturn, baseReturn)) return; Config oldConfig = Config.createOrResetConfig(this); try { if (!roleReturn.isCompatibleWith(baseReturn)) { if (typeIdentityRequired) { this.scope .problemReporter() .callinIncompatibleReturnType(baseSpec, this.roleMethodSpec); this.binding.tagBits |= TagBits.HasMappingIncompatibility; return; } // else we still needed the lowering test } // callin replace requires two way compatibility: baseSpec.returnNeedsTranslation = Config.getLoweringRequired(); } finally { Config.removeOrRestore(oldConfig, this); } // from now on don't bother with arrays any more (dimensions have been checked): roleReturn = roleReturn.leafComponentType(); baseReturn = baseReturn.leafComponentType(); TypeBinding translatedReturn = baseSpec.returnNeedsTranslation ? ((ReferenceBinding) roleReturn).baseclass() : roleReturn; if (translatedReturn.isTypeVariable()) { TypeBinding firstBound = ((TypeVariableBinding) translatedReturn).firstBound; if (firstBound != null) translatedReturn = firstBound; } if (!baseReturn.isCompatibleWith(translatedReturn)) { this.scope .problemReporter() .callinIncompatibleReturnTypeBaseCall(baseSpec, this.roleMethodSpec); this.binding.tagBits |= TagBits.HasMappingIncompatibility; } } }