public static bool isANYLOCAL_OK(this EXPR expr) { return(expr.isANYLOCAL() && expr.isOK()); }
public static bool isCONSTANT_OK(this EXPR expr) { return((expr == null) ? false : (expr.kind == ExpressionKind.EK_CONSTANT && expr.isOK())); }
//////////////////////////////////////////////////////////////////////////////// internal EXPR BindToProperty(EXPR pObject, PropWithType pwt, BindingFlag bindFlags, EXPR args, AggregateType pOtherType, EXPRMEMGRP pMemGroup) { Debug.Assert(pwt.Sym != null && pwt.Sym.IsPropertySymbol() && pwt.GetType() != null && pwt.Prop().getClass() == pwt.GetType().getAggregate()); Debug.Assert(pwt.Prop().Params.size == 0 || pwt.Prop().isIndexer()); Debug.Assert(pOtherType == null || !pwt.Prop().isIndexer() && pOtherType.getAggregate() == pwt.Prop().RetType.getAggregate()); bool fConstrained; MethWithType mwtGet; MethWithType mwtSet; EXPR pObjectThrough = null; // We keep track of the type of the pObject which we're doing the call through so that we can report // protection access errors later, either below when binding the get, or later when checking that // the setter is actually an lvalue. If we're actually doing a base.prop call then we do not // need to ensure that the left side of the dot is an instance of the derived class, otherwise // we save it away for later. if (0 == (bindFlags & BindingFlag.BIND_BASECALL)) { pObjectThrough = pObject; } bool bIsMatchingStatic; PostBindProperty((bindFlags & BindingFlag.BIND_BASECALL) != 0, pwt, pObject, out mwtGet, out mwtSet); if (mwtGet && (!mwtSet || mwtSet.GetType() == mwtGet.GetType() || GetSymbolLoader().HasBaseConversion(mwtGet.GetType(), mwtSet.GetType()) ) ) { pObject = AdjustMemberObject(mwtGet, pObject, out fConstrained, out bIsMatchingStatic); } else if (mwtSet) { pObject = AdjustMemberObject(mwtSet, pObject, out fConstrained, out bIsMatchingStatic); } else { pObject = AdjustMemberObject(pwt, pObject, out fConstrained, out bIsMatchingStatic); } pMemGroup.SetOptionalObject(pObject); CType pReturnType = GetTypes().SubstType(pwt.Prop().RetType, pwt.GetType()); Debug.Assert(pOtherType == pReturnType || pOtherType == null); if (pObject != null && !pObject.isOK()) { EXPRPROP pResult = GetExprFactory().CreateProperty(pReturnType, pObjectThrough, args, pMemGroup, pwt, null, null); if (!bIsMatchingStatic) { pResult.SetMismatchedStaticBit(); } pResult.SetError(); return pResult; } // if we are doing a get on this thing, and there is no get, and // most importantly, we are not leaving the arguments to be bound by the array index // then error... if ((bindFlags & BindingFlag.BIND_RVALUEREQUIRED) != 0) { if (!mwtGet) { if (pOtherType != null) { return GetExprFactory().MakeClass(pOtherType); } ErrorContext.ErrorRef(ErrorCode.ERR_PropertyLacksGet, pwt); } else if (((bindFlags & BindingFlag.BIND_BASECALL) != 0) && mwtGet.Meth().isAbstract) { // if the get exists, but is abstract, forbid the call as well... if (pOtherType != null) { return GetExprFactory().MakeClass(pOtherType); } ErrorContext.Error(ErrorCode.ERR_AbstractBaseCall, pwt); } else { CType type = null; if (pObjectThrough != null) { type = pObjectThrough.type; } ACCESSERROR error = SemanticChecker.CheckAccess2(mwtGet.Meth(), mwtGet.GetType(), ContextForMemberLookup(), type); if (error != ACCESSERROR.ACCESSERROR_NOERROR) { // if the get exists, but is not accessible, give an error. if (pOtherType != null) { return GetExprFactory().MakeClass(pOtherType); } if (error == ACCESSERROR.ACCESSERROR_NOACCESSTHRU) { ErrorContext.Error(ErrorCode.ERR_BadProtectedAccess, pwt, type, ContextForMemberLookup()); } else { ErrorContext.ErrorRef(ErrorCode.ERR_InaccessibleGetter, pwt); } } } } EXPRPROP result = GetExprFactory().CreateProperty(pReturnType, pObjectThrough, args, pMemGroup, pwt, mwtGet, mwtSet); if (!bIsMatchingStatic) { result.SetMismatchedStaticBit(); } Debug.Assert(EXPRFLAG.EXF_BASECALL == (EXPRFLAG)BindingFlag.BIND_BASECALL); if ((EXPRFLAG.EXF_BASECALL & (EXPRFLAG)bindFlags) != 0) { result.flags |= EXPRFLAG.EXF_BASECALL; } else if (fConstrained && pObject != null) { // Use the constrained prefix. result.flags |= EXPRFLAG.EXF_CONSTRAINED; } if (result.GetOptionalArguments() != null) { verifyMethodArgs(result, pObjectThrough != null ? pObjectThrough.type : null); } if (mwtSet && objectIsLvalue(result.GetMemberGroup().GetOptionalObject())) { result.flags |= EXPRFLAG.EXF_LVALUE; } if (pOtherType != null) { result.flags |= EXPRFLAG.EXF_SAMENAMETYPE; } return result; }
//////////////////////////////////////////////////////////////////////////////// internal EXPR BindToField(EXPR pOptionalObject, FieldWithType fwt, BindingFlag bindFlags, EXPR pOptionalLHS) { Debug.Assert(fwt.GetType() != null && fwt.Field().getClass() == fwt.GetType().getAggregate()); CType pFieldType = GetTypes().SubstType(fwt.Field().GetType(), fwt.GetType()); if (pOptionalObject != null && !pOptionalObject.isOK()) { EXPRFIELD pField = GetExprFactory().CreateField(0, pFieldType, pOptionalObject, 0, fwt, pOptionalLHS); pField.SetError(); return pField; } EXPR pOriginalObject = pOptionalObject; bool bIsMatchingStatic; bool pfConstrained; pOptionalObject = AdjustMemberObject(fwt, pOptionalObject, out pfConstrained, out bIsMatchingStatic); checkUnsafe(pFieldType); // added to the binder so we don't bind to pointer ops EXPRFIELD pResult; { bool isLValue = false; if ((pOptionalObject != null && pOptionalObject.type.IsPointerType()) || objectIsLvalue(pOptionalObject)) { isLValue = true; } // Exception: a readonly field is not an lvalue unless we're in the constructor/static constructor appropriate // for the field. if (RespectReadonly() && fwt.Field().isReadOnly) { if (ContainingAgg() == null || !InMethod() || !InConstructor() || fwt.Field().getClass() != ContainingAgg() || InStaticMethod() != fwt.Field().isStatic || (pOptionalObject != null && !isThisPointer(pOptionalObject)) || InAnonymousMethod()) { isLValue = false; } } pResult = GetExprFactory().CreateField(isLValue ? EXPRFLAG.EXF_LVALUE : 0, pFieldType, pOptionalObject, 0, fwt, pOptionalLHS); if (!bIsMatchingStatic) { pResult.SetMismatchedStaticBit(); } if (pFieldType.IsErrorType()) { pResult.SetError(); } Debug.Assert(BindingFlag.BIND_MEMBERSET == (BindingFlag)EXPRFLAG.EXF_MEMBERSET); pResult.flags |= (EXPRFLAG)(bindFlags & BindingFlag.BIND_MEMBERSET); } // If this field is the backing field of a WindowsRuntime event then we need to bind to its // invocationlist property which is a delegate containing all the handlers. if (pResult.isFIELD() && fwt.Field().isEvent && fwt.Field().getEvent(GetSymbolLoader()) != null && fwt.Field().getEvent(GetSymbolLoader()).IsWindowsRuntimeEvent) { CType fieldType = fwt.Field().GetType(); if (fieldType.IsAggregateType()) { // Access event backing field (EventRegistrationTokenTable<T>) using // EventRegistrationTokenTable<T>.GetOrCreateEventRegistrationTokenTable() // to ensure non-null pResult.setType(GetTypes().GetParameterModifier(pResult.type, false)); Name getOrCreateMethodName = GetSymbolLoader().GetNameManager().GetPredefName(PredefinedName.PN_GETORCREATEEVENTREGISTRATIONTOKENTABLE); GetSymbolLoader().RuntimeBinderSymbolTable.PopulateSymbolTableWithName(getOrCreateMethodName.Text, null, fieldType.AssociatedSystemType); MethodSymbol getOrCreateMethod = GetSymbolLoader().LookupAggMember(getOrCreateMethodName, fieldType.getAggregate(), symbmask_t.MASK_MethodSymbol).AsMethodSymbol(); MethPropWithInst getOrCreatempwi = new MethPropWithInst(getOrCreateMethod, fieldType.AsAggregateType()); EXPRMEMGRP getOrCreateGrp = GetExprFactory().CreateMemGroup(null, getOrCreatempwi); EXPR getOrCreateCall = BindToMethod(new MethWithInst(getOrCreatempwi), pResult, getOrCreateGrp, (MemLookFlags)MemLookFlags.None); AggregateSymbol fieldTypeSymbol = fieldType.AsAggregateType().GetOwningAggregate(); Name invocationListName = GetSymbolLoader().GetNameManager().GetPredefName(PredefinedName.PN_INVOCATIONLIST); // InvocationList might not be populated in the symbol table as no one would have called it. GetSymbolLoader().RuntimeBinderSymbolTable.PopulateSymbolTableWithName(invocationListName.Text, null, fieldType.AssociatedSystemType); PropertySymbol invocationList = GetSymbolLoader().LookupAggMember( invocationListName, fieldTypeSymbol, symbmask_t.MASK_PropertySymbol).AsPropertySymbol(); MethPropWithInst mpwi = new MethPropWithInst(invocationList, fieldType.AsAggregateType()); EXPRMEMGRP memGroup = GetExprFactory().CreateMemGroup(getOrCreateCall, mpwi); PropWithType pwt = new PropWithType(invocationList, fieldType.AsAggregateType()); EXPR propertyExpr = BindToProperty(getOrCreateCall, pwt, bindFlags, null, null, memGroup); return propertyExpr; } } return pResult; }
internal EXPR BindArrayIndexCore(BindingFlag bindFlags, EXPR pOp1, EXPR pOp2) { EXPR pExpr; bool bIsError = false; if (!pOp1.isOK() || !pOp2.isOK()) { bIsError = true; } CType pIntType = GetReqPDT(PredefinedType.PT_INT); // Array indexing must occur on an array type. if (!pOp1.type.IsArrayType()) { Debug.Assert(!pOp1.type.IsPointerType()); pExpr = bindIndexer(pOp1, pOp2, bindFlags); if (bIsError) { pExpr.SetError(); } return pExpr; } ArrayType pArrayType = pOp1.type.AsArrayType(); checkUnsafe(pArrayType.GetElementType()); // added to the binder so we don't bind to pointer ops // Check the rank of the array against the number of indices provided, and // convert the indexes to ints CType pDestType = chooseArrayIndexType(pOp2); if (null == pDestType) { // using int as the type will allow us to give a better error... pDestType = pIntType; } int rank = pArrayType.rank; int cIndices = 0; EXPR transformedIndices = pOp2.Map(GetExprFactory(), (EXPR x) => { cIndices++; EXPR pTemp = mustConvert(x, pDestType); if (pDestType == pIntType) return pTemp; #if CSEE EXPRFLAG flag = 0; #else EXPRFLAG flag = EXPRFLAG.EXF_INDEXEXPR; #endif EXPRCLASS exprType = GetExprFactory().MakeClass(pDestType); return GetExprFactory().CreateCast(flag, exprType, pTemp); }); if (cIndices != rank) { ErrorContext.Error(ErrorCode.ERR_BadIndexCount, rank); pExpr = GetExprFactory().CreateArrayIndex(pOp1, transformedIndices); pExpr.SetError(); return pExpr; } // Allocate a new expression, the type is the element type of the array. // Array index operations are always lvalues. pExpr = GetExprFactory().CreateArrayIndex(pOp1, transformedIndices); pExpr.flags |= EXPRFLAG.EXF_LVALUE | EXPRFLAG.EXF_ASSGOP; if (bIsError) { pExpr.SetError(); } return pExpr; }
//////////////////////////////////////////////////////////////////////////////// // Bind the simple assignment operator =. public EXPR bindAssignment(EXPR op1, EXPR op2, bool allowExplicit) { bool fOp2NotAddrOp = false; bool fOp2WasCast = false; if (!op1.isANYLOCAL_OK()) { if (!checkLvalue(op1, CheckLvalueKind.Assignment)) { EXPR rval = GetExprFactory().CreateAssignment(op1, op2); rval.SetError(); return rval; } } else { if (op2.type.IsArrayType()) { return BindPtrToArray(op1.asANYLOCAL(), op2); } if (op2.type == GetReqPDT(PredefinedType.PT_STRING)) { op2 = bindPtrToString(op2); } else if (op2.kind == ExpressionKind.EK_ADDR) { op2.flags |= EXPRFLAG.EXF_ADDRNOCONV; } else if (op2.isOK()) { fOp2NotAddrOp = true; fOp2WasCast = (op2.isCAST()); } } op2 = GenerateAssignmentConversion(op1, op2, allowExplicit); if (op2.isOK() && fOp2NotAddrOp) { // Only report these errors if the convert succeeded if (fOp2WasCast) { ErrorContext.Error(ErrorCode.ERR_BadCastInFixed); } else { ErrorContext.Error(ErrorCode.ERR_FixedNotNeeded); } } return GenerateOptimizedAssignment(op1, op2); }
//////////////////////////////////////////////////////////////////////////////// // A false return means not to process the expr any further - it's totally out // of place. For example - a method group or an anonymous method. internal bool checkLvalue(EXPR expr, CheckLvalueKind kind) { if (!expr.isOK()) return false; if (expr.isLvalue()) { if (expr.isPROP()) { CheckLvalueProp(expr.asPROP()); } markFieldAssigned(expr); return true; } switch (expr.kind) { case ExpressionKind.EK_PROP: if (kind == CheckLvalueKind.OutParameter) { // passing a property as ref or out ErrorContext.Error(ErrorCode.ERR_RefProperty); return true; } if (!expr.asPROP().mwtSet) { // Assigning to a property without a setter. // If we have // bool? b = true; (bool)b = false; // then this is realized immediately as // b.Value = false; // and no ExpressionKind.EK_CAST is generated. We'd rather not give a "you're writing // to a read-only property" error in the case where the property access // is not explicit in the source code. Fortunately in this case the // cast is still hanging around in the parse tree, so we can look for it. // POSSIBLE ERROR: It would be nice to also give this error for other situations // POSSIBLE ERROR: in which the user is attempting to assign to a value, such as // POSSIBLE ERROR: an explicit (bool)b.Value = false; // POSSIBLE ERROR: Unfortunately we cannot use this trick in that situation because // POSSIBLE ERROR: we've already discarded the OperatorKind.OP_CAST node. (This is an SyntaxKind.Dot). // SPEC VIOLATION: More generally: // SPEC VIOLATION: The spec states that the result of any cast is a value, not a // SPEC VIOLATION: variable. Unfortunately we do not correctly implement this // SPEC VIOLATION: and we probably should not start implementing it because this // SPEC VIOLATION: would be a breaking change. We currently discard "no op" casts // SPEC VIOLATION: very aggressively rather than generating an ExpressionKind.EK_CAST node. ErrorContext.Error(ErrorCode.ERR_AssgReadonlyProp, expr.asPROP().pwtSlot); return true; } break; case ExpressionKind.EK_ARRAYLENGTH: if (kind == CheckLvalueKind.OutParameter) { // passing a property as ref or out ErrorContext.Error(ErrorCode.ERR_RefProperty); } else { // Special case, the length property of an array ErrorContext.Error(ErrorCode.ERR_AssgReadonlyProp, GetSymbolLoader().getPredefinedMembers().GetProperty(PREDEFPROP.PP_ARRAY_LENGTH)); } return true; case ExpressionKind.EK_BOUNDLAMBDA: case ExpressionKind.EK_UNBOUNDLAMBDA: case ExpressionKind.EK_CONSTANT: ErrorContext.Error(GetStandardLvalueError(kind)); return false; case ExpressionKind.EK_MEMGRP: { ErrorCode err = (kind == CheckLvalueKind.OutParameter) ? ErrorCode.ERR_RefReadonlyLocalCause : ErrorCode.ERR_AssgReadonlyLocalCause; ErrorContext.Error(err, expr.asMEMGRP().name, new ErrArgIds(MessageID.MethodGroup)); return false; } default: break; } return !TryReportLvalueFailure(expr, kind); }