private static Expression ValidateOneArgument(ParameterInfo parameter, Expression expression) { RequiresCanRead(expression, nameof(expression)); var pType = parameter.ParameterType; // NB: No writeability check is performed; LINQ doesn't either, so you can pass e.g. // a constant by ref, causing the write-back to be discarded. We're just being // consistent here. if (pType.IsByRef) { pType = pType.GetElementType(); } TypeUtils.ValidateType(pType); if (!TypeUtils1.AreReferenceAssignable(pType, expression.Type)) { if (!TryQuote(pType, ref expression)) { throw Error.ExpressionTypeDoesNotMatchParameter(expression.Type, pType); } } return(expression); }
private static void VerifyRewrite(Result result, Expression node) { Debug.Assert(result.Node != null); // (result.Action == RewriteAction.None) if and only if (node == result.Node) Debug.Assert((result.Action == RewriteAction.None) ^ (node != result.Node), "rewrite action does not match node object identity"); #if LINQ // NB: For C#, we keep await nodes. Reduction happens in a separate step. // if the original node is an extension node, it should have been rewritten Debug.Assert(result.Node.NodeType != ExpressionType.Extension, "extension nodes must be rewritten"); #endif #if LINQ // The C# spiller never returns a Copy action // if we have Copy, then node type must match Debug.Assert( result.Action != RewriteAction.Copy || node.NodeType == result.Node.NodeType || node.CanReduce, "rewrite action does not match node object kind" ); #endif // New type must be reference assignable to the old type // (our rewrites preserve type exactly, but the rules for rewriting // an extension node are more lenient, see Expression.ReduceAndCheck()) Debug.Assert( TypeUtils1.AreReferenceAssignable(node.Type, result.Node.Type), "rewritten object must be reference assignable to the original type" ); }
private Expression ReduceStaticAssign(Expression lhs) { lhs = MakeWriteable(lhs); var rhs = Right.Expression; if (!TypeUtils1.AreReferenceAssignable(lhs.Type, rhs.Type)) { rhs = DynamicConvert(rhs, lhs.Type, CSharpBinderFlags.None, Context); } return(Expression.Assign(lhs, rhs)); }
public static ConditionalMemberCSharpExpression ConditionalField(Expression expression, FieldInfo field) { RequiresCanRead(expression, nameof(expression)); ContractUtils.RequiresNotNull(field, nameof(field)); if (field.IsStatic) { throw Error.ConditionalAccessRequiresNonStaticMember(); } var type = expression.Type.GetNonNullReceiverType(); if (!TypeUtils1.AreReferenceAssignable(field.DeclaringType, type)) { throw LinqError.FieldInfoNotDefinedForType(field.DeclaringType, field.Name, type); } return(ConditionalMemberCSharpExpression.Make(expression, field)); }
private static void ValidateAsyncLambdaArgs(Type delegateType, ref Expression body, ReadOnlyCollection <ParameterExpression> parameters) { //ContractUtils.RequiresNotNull(delegateType, nameof(delegateType)); //TODO: Verify //RequiresCanRead(body, nameof(body)); if (!typeof(MulticastDelegate).IsAssignableFrom(delegateType) || delegateType == typeof(MulticastDelegate)) { throw LinqError.LambdaTypeMustBeDerivedFromSystemDelegate(); } var count = parameters.Count; var method = delegateType.GetMethod("Invoke"); // TODO: use cache from LINQ var parametersCached = method.GetParametersCached(); if (parametersCached.Length != 0) { if (parametersCached.Length != count) { throw LinqError.IncorrectNumberOfLambdaDeclarationParameters(); } var set = new Set <ParameterExpression>(count); for (var i = 0; i < count; i++) { var parameter = parameters[i]; ValidateAsyncParameter(parameter); var parameterType = parametersCached[i].ParameterType; if (!TypeUtils1.AreReferenceAssignable(parameter.Type, parameterType)) { throw LinqError.ParameterExpressionNotValidAsDelegate(parameter.Type, parameterType); } if (set.Contains(parameter)) { throw LinqError.DuplicateVariable(parameter); } set.Add(parameter); } } else if (count > 0) { throw LinqError.IncorrectNumberOfLambdaDeclarationParameters(); } var returnType = method.ReturnType; if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task <>)) { var resultType = returnType.GetGenericArguments()[0]; if (!TypeUtils1.AreReferenceAssignable(resultType, body.Type) && !TryQuote(resultType, ref body)) { throw LinqError.ExpressionTypeDoesNotMatchReturn(body.Type, method.ReturnType); } } else if (returnType != typeof(void) && returnType != typeof(Task)) { throw Error.AsyncLambdaInvalidReturnType(returnType); } }
private static AssignBinaryCSharpExpression MakeBinaryAssignCore(CSharpExpressionType binaryType, Expression left, Expression right, MethodInfo method, LambdaExpression finalConversion, LambdaExpression leftConversion) { Helpers.RequiresCanWrite(left, nameof(left)); RequiresCanRead(right, nameof(right)); // NB: We could return a BinaryExpression in case the lhs is not one of our index nodes, but it'd change // the return type to Expression which isn't nice to consume. Also, the Update method would either // have to change to return Expression or we should have an AssignBinary node to hold a Binary node // underneath it. This said, a specialized layout for the case where the custom node trivially wraps // a LINQ node could be useful (just make Left virtual). if (binaryType != CSharpExpressionType.Assign) { var leftType = left.Type; var rightType = right.Type; if (leftType == typeof(string)) { if (method == null) { if (binaryType == CSharpExpressionType.AddAssign || binaryType == CSharpExpressionType.AddAssignChecked) { if (rightType == typeof(string)) { method = typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(string) }); } else { method = typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(object) }); if (!TypeUtils1.AreReferenceAssignable(typeof(object), rightType)) { // DESIGN: Should our factory do this our just reject the input? right = Expression.Convert(right, typeof(object)); } } } else { throw Error.InvalidCompoundAssignment(binaryType, typeof(string)); } } } else if (typeof(MulticastDelegate).IsAssignableFrom(leftType)) { if (leftType == typeof(MulticastDelegate)) { throw Error.InvalidCompoundAssignmentWithOperands(binaryType, leftType, rightType); } // NB: This checks for assignment with variance checks in mind, e.g. // // Action<string> s = ...; // Action<object> o = ...; // s += o; if (!TypeUtils1.AreReferenceAssignable(leftType, rightType)) { throw Error.InvalidCompoundAssignmentWithOperands(binaryType, leftType, rightType); } if (method == null) { if (binaryType == CSharpExpressionType.AddAssign || binaryType == CSharpExpressionType.AddAssignChecked) { method = typeof(Delegate).GetMethod(nameof(Delegate.Combine), new[] { typeof(Delegate), typeof(Delegate) }); } else if (binaryType == CSharpExpressionType.SubtractAssign || binaryType == CSharpExpressionType.SubtractAssignChecked) { method = typeof(Delegate).GetMethod(nameof(Delegate.Remove), new[] { typeof(Delegate), typeof(Delegate) }); } else { throw Error.InvalidCompoundAssignment(binaryType, leftType); } if (finalConversion == null) { var resultParameter = Expression.Parameter(typeof(Delegate), "__result"); var convertResult = Expression.Convert(resultParameter, leftType); finalConversion = Expression.Lambda(convertResult, resultParameter); } } } else if (IsCSharpSpecificCompoundNumeric(leftType)) { // NB: If any of these are passed, we'll assume the types all line up. The call to // the ValidateCustomBinaryAssign method below will check that's indeed the case. if (method == null && leftConversion == null && finalConversion == null) { var isChecked = IsCheckedBinary(binaryType); var isNullabeLeftType = leftType.IsNullableType(); var nonNullLeftType = leftType.GetNonNullableType(); var intermediateType = nonNullLeftType.IsEnum ? nonNullLeftType.GetEnumUnderlyingType() : typeof(int); var leftParameter = Expression.Parameter(leftType, "__left"); var convertType = isNullabeLeftType ? typeof(Nullable <>).MakeGenericType(intermediateType) : intermediateType; var convertLeft = isChecked ? Expression.ConvertChecked(leftParameter, convertType) : Expression.Convert(leftParameter, convertType); leftConversion = Expression.Lambda(convertLeft, leftParameter); var resultParameter = Expression.Parameter(convertType, "__result"); var convertResult = isChecked ? Expression.ConvertChecked(resultParameter, leftType) : Expression.Convert(resultParameter, leftType); finalConversion = Expression.Lambda(convertResult, resultParameter); if (rightType != convertType) { // DESIGN: Should our factory do this or just reject the input? On the one hand, // C# allows e.g. byte += byte, so if this is a C#-specific API it may be // reasonable for the user to expect such a tree can be built. On the // other hand, it's very unlike the expression tree API to insert nodes // on behalf of the user in the factories. Note that Roslyn often models // conversions as properties on a node using a `Conversion` objects // which would be handy to keep the shape from the tree isomorphic to the // bound nodes in the compiler. Note though that the RHS of a compound // assignment doesn't have such a conversion and the compiler will insert // a convert node in this case, so this is really just a convenience in // our factory method to mimic that behavior. right = Expression.Convert(right, convertType); } } } } return(AssignBinaryCSharpExpression.Make(binaryType, left, right, method, leftConversion, finalConversion)); }
public static NewMultidimensionalArrayInitCSharpExpression NewMultidimensionalArrayInit(Type type, IEnumerable <int> bounds, IEnumerable <Expression> initializers) { //ContractUtils.RequiresNotNull(type, nameof(type)); //ContractUtils.RequiresNotNull(bounds, nameof(bounds)); //ContractUtils.RequiresNotNull(initializers, nameof(initializers)); //if (type.Equals(typeof(void))) //{ // throw LinqError.ArgumentCannotBeOfTypeVoid(); //} var boundsList = bounds.ToReadOnly(); int dimensions = boundsList.Count; if (dimensions <= 0) { throw LinqError.BoundsCannotBeLessThanOne(); } var length = 1; foreach (var bound in boundsList) { if (bound < 0) { throw Error.BoundCannotBeLessThanZero(); } checked { length *= bound; } } var initializerList = initializers.ToReadOnly(); if (initializerList.Count != length) { throw Error.ArrayBoundsElementCountMismatch(); } var newList = default(Expression[]); for (int i = 0, n = initializerList.Count; i < n; i++) { var expr = initializerList[i]; //RequiresCanRead(expr, nameof(initializers)); if (!TypeUtils1.AreReferenceAssignable(type, expr.Type)) { if (!TryQuote(type, ref expr)) { throw LinqError.ExpressionTypeCannotInitializeArrayType(expr.Type, type); } if (newList == null) { newList = new Expression[initializerList.Count]; for (int j = 0; j < i; j++) { newList[j] = initializerList[j]; } } } if (newList != null) { newList[i] = expr; } } if (newList != null) { initializerList = new TrueReadOnlyCollection <Expression>(newList); } return(new NewMultidimensionalArrayInitCSharpExpression(type.MakeArrayType(boundsList.Count), boundsList, initializerList)); }