internal AssignBinaryDynamicCSharpExpression(Type context, CSharpBinderFlags binderFlags, CSharpExpressionType binaryType, DynamicCSharpArgument left, DynamicCSharpArgument right) : base(context, binderFlags) { OperationNodeType = binaryType; Left = left; Right = right; }
private static AssignUnaryCSharpExpression MakeUnaryAssignCore(CSharpExpressionType unaryType, Expression operand, MethodInfo method) { RequiresCanRead(operand, nameof(operand)); Helpers.RequiresCanWrite(operand, nameof(operand)); return(AssignUnaryCSharpExpression.Make(unaryType, operand, method)); }
internal WithConversions(CSharpExpressionType binaryType, Expression left, Expression right, MethodInfo method, LambdaExpression leftConversion, LambdaExpression finalConversion) : base(binaryType, left, right) { Method = method; LeftConversion = leftConversion; FinalConversion = finalConversion; }
internal EventAssignCSharpExpression(CSharpExpressionType type, Expression @object, EventInfo @event, Expression handler) { CSharpNodeType = type; Object = @object; Event = @event; Handler = handler; }
/// <summary> /// Creates an expression representing a unary assignment operation. /// </summary> /// <param name="unaryType">The type of assignment represented.</param> /// <param name="operand">The operand of the assignment operation, i.e. the assignment target.</param> /// <param name="method">The method implementing the assignment operation.</param> /// <returns>A new <see cref="AssignUnaryCSharpExpression"/> instance representing the unary assignment.</returns> public static AssignUnaryCSharpExpression MakeUnaryAssign(CSharpExpressionType unaryType, Expression operand, MethodInfo method) { switch (unaryType) { case CSharpExpressionType.PreIncrementAssign: return(PreIncrementAssign(operand, method)); case CSharpExpressionType.PreIncrementAssignChecked: return(PreIncrementAssignChecked(operand, method)); case CSharpExpressionType.PreDecrementAssign: return(PreDecrementAssign(operand, method)); case CSharpExpressionType.PreDecrementAssignChecked: return(PreDecrementAssignChecked(operand, method)); case CSharpExpressionType.PostIncrementAssign: return(PostIncrementAssign(operand, method)); case CSharpExpressionType.PostIncrementAssignChecked: return(PostIncrementAssignChecked(operand, method)); case CSharpExpressionType.PostDecrementAssign: return(PostDecrementAssign(operand, method)); case CSharpExpressionType.PostDecrementAssignChecked: return(PostDecrementAssignChecked(operand, method)); } throw LinqError.UnhandledUnary(unaryType); }
private static void ValidateCustomUnaryAssign(CSharpExpressionType unaryType, Expression operand, ref MethodInfo method) { var operandType = operand.Type; // NB: Just leverage LINQ to do the dirty work to check everything.This could produce mysterious error // messages. (TODO: Review what's most appropriate here.) var resultType = operandType; if (!IsCSharpSpecificUnaryAssignNumeric(operandType)) { var operandDummy = Expression.Parameter(operandType, "__operand"); var functionalOp = FunctionalOp(unaryType, operandDummy, method); if (method == null) { method = functionalOp.Method; } resultType = functionalOp.Type; } if (!TypeUtils.AreEquivalent(resultType, operand.Type)) { throw Error.InvalidUnaryAssignmentWithOperands(unaryType, operand.Type); } }
internal static AssignUnaryCSharpExpression Make(CSharpExpressionType unaryType, Expression operand, MethodInfo method) { ValidateCustomUnaryAssign(unaryType, operand, ref method); // TODO: Add optimized layouts return(new Custom(unaryType, operand, method)); }
internal static AssignBinaryCSharpExpression Make(CSharpExpressionType binaryType, Expression left, Expression right, MethodInfo method, LambdaExpression leftConversion, LambdaExpression finalConversion) { ValidateCustomBinaryAssign(binaryType, left, right, ref method, leftConversion, finalConversion); // TODO: Add optimized layouts return(new WithConversions(binaryType, left, right, method, leftConversion, finalConversion)); }
private static bool IsCheckedBinary(CSharpExpressionType type) { switch (type) { case CSharpExpressionType.AddAssignChecked: case CSharpExpressionType.MultiplyAssignChecked: case CSharpExpressionType.SubtractAssignChecked: return(true); } return(false); }
/// <summary> /// Creates an expression representing a binary assignment operation. /// </summary> /// <param name="binaryType">The type of assignment represented.</param> /// <param name="left">The left operand of the assignment operation, i.e. the assignment target.</param> /// <param name="right">The right operation of the assignment operation.</param> /// <param name="method">The method implementing the assignment operation.</param> /// <param name="leftConversion">The conversion function used to convert the left hand side of the compound assignment prior to use by the underlying operation.</param> /// <param name="finalConversion">The conversion function used to convert the result of the underlying operation prior to assignment to the left hand side of the compound assignment operation.</param> /// <returns>A new <see cref="AssignBinaryCSharpExpression"/> instance representing the binary assignment.</returns> public static AssignBinaryCSharpExpression MakeBinaryAssign(CSharpExpressionType binaryType, Expression left, Expression right, MethodInfo method, LambdaExpression leftConversion, LambdaExpression finalConversion) { switch (binaryType) { case CSharpExpressionType.Assign: return(Assign(left, right)); case CSharpExpressionType.AddAssign: return(AddAssign(left, right, method, leftConversion, finalConversion)); case CSharpExpressionType.AndAssign: return(AndAssign(left, right, method, leftConversion, finalConversion)); case CSharpExpressionType.DivideAssign: return(DivideAssign(left, right, method, leftConversion, finalConversion)); case CSharpExpressionType.ExclusiveOrAssign: return(ExclusiveOrAssign(left, right, method, leftConversion, finalConversion)); case CSharpExpressionType.LeftShiftAssign: return(LeftShiftAssign(left, right, method, leftConversion, finalConversion)); case CSharpExpressionType.ModuloAssign: return(ModuloAssign(left, right, method, leftConversion, finalConversion)); case CSharpExpressionType.MultiplyAssign: return(MultiplyAssign(left, right, method, leftConversion, finalConversion)); case CSharpExpressionType.OrAssign: return(OrAssign(left, right, method, leftConversion, finalConversion)); case CSharpExpressionType.RightShiftAssign: return(RightShiftAssign(left, right, method, leftConversion, finalConversion)); case CSharpExpressionType.SubtractAssign: return(SubtractAssign(left, right, method, leftConversion, finalConversion)); case CSharpExpressionType.AddAssignChecked: return(AddAssignChecked(left, right, method, leftConversion, finalConversion)); case CSharpExpressionType.MultiplyAssignChecked: return(MultiplyAssignChecked(left, right, method, leftConversion, finalConversion)); case CSharpExpressionType.SubtractAssignChecked: return(SubtractAssignChecked(left, right, method, leftConversion, finalConversion)); } throw LinqError.UnhandledBinary(binaryType); }
internal static BinaryExpression FunctionalOp(CSharpExpressionType binaryType, Expression left, Expression right, MethodInfo method) { switch (binaryType) { case CSharpExpressionType.Assign: return(Expression.Assign(left, right)); case CSharpExpressionType.AddAssign: return(Expression.Add(left, right, method)); case CSharpExpressionType.AndAssign: return(Expression.And(left, right, method)); case CSharpExpressionType.DivideAssign: return(Expression.Divide(left, right, method)); case CSharpExpressionType.ExclusiveOrAssign: return(Expression.ExclusiveOr(left, right, method)); case CSharpExpressionType.LeftShiftAssign: return(Expression.LeftShift(left, right, method)); case CSharpExpressionType.ModuloAssign: return(Expression.Modulo(left, right, method)); case CSharpExpressionType.MultiplyAssign: return(Expression.Multiply(left, right, method)); case CSharpExpressionType.OrAssign: return(Expression.Or(left, right, method)); case CSharpExpressionType.RightShiftAssign: return(Expression.RightShift(left, right, method)); case CSharpExpressionType.SubtractAssign: return(Expression.Subtract(left, right, method)); case CSharpExpressionType.AddAssignChecked: return(Expression.AddChecked(left, right, method)); case CSharpExpressionType.MultiplyAssignChecked: return(Expression.MultiplyChecked(left, right, method)); case CSharpExpressionType.SubtractAssignChecked: return(Expression.SubtractChecked(left, right, method)); } throw LinqError.UnhandledBinary(binaryType); }
private static ExpressionType ConvertNodeType(CSharpExpressionType nodeType) { switch (nodeType) { case CSharpExpressionType.PreIncrementAssign: return ExpressionType.PreIncrementAssign; case CSharpExpressionType.PreIncrementAssignChecked: return ExpressionType.PreIncrementAssign; case CSharpExpressionType.PreDecrementAssign: return ExpressionType.PreDecrementAssign; case CSharpExpressionType.PreDecrementAssignChecked: return ExpressionType.PreDecrementAssign; case CSharpExpressionType.PostIncrementAssign: return ExpressionType.PostIncrementAssign; case CSharpExpressionType.PostIncrementAssignChecked: return ExpressionType.PostIncrementAssign; case CSharpExpressionType.PostDecrementAssign: return ExpressionType.PostDecrementAssign; case CSharpExpressionType.PostDecrementAssignChecked: return ExpressionType.PostDecrementAssign; default: throw ContractUtils.Unreachable; } }
private static Type ValidateConversion(CSharpExpressionType nodeType, Type inputType, LambdaExpression conversion) { var invoke = conversion.Type.GetMethod("Invoke"); var invokeParameters = invoke.GetParametersCached(); if (invokeParameters.Length != 1) { throw LinqError.IncorrectNumberOfMethodCallArguments(conversion); } if (!TypeUtils.AreEquivalent(invokeParameters[0].ParameterType, inputType)) { throw LinqError.OperandTypesDoNotMatchParameters(nodeType, conversion.ToString()); } return(invoke.ReturnType); }
public static AssignBinaryDynamicCSharpExpression MakeDynamicBinaryAssign(CSharpExpressionType binaryType, DynamicCSharpArgument left, DynamicCSharpArgument right, CSharpBinderFlags binderFlags, Type context) { ContractUtils.RequiresNotNull(left, nameof(left)); ContractUtils.RequiresNotNull(right, nameof(right)); RequiresCanWrite(left.Expression, nameof(left)); RequiresCanRead(right.Expression, nameof(right)); CheckBinaryAssign(binaryType); switch (binaryType) { case CSharpExpressionType.AddAssignChecked: case CSharpExpressionType.SubtractAssignChecked: case CSharpExpressionType.MultiplyAssignChecked: binderFlags |= CSharpBinderFlags.CheckedContext; break; } return(new AssignBinaryDynamicCSharpExpression(context, binderFlags, binaryType, left, right)); }
public static AssignUnaryDynamicCSharpExpression MakeDynamicUnaryAssign(CSharpExpressionType unaryType, DynamicCSharpArgument operand, CSharpBinderFlags binderFlags, Type context) { RequiresNotNull(operand, nameof(operand)); RequiresCanRead(operand.Expression, nameof(operand)); RequiresCanWrite(operand.Expression, nameof(operand)); CheckUnaryAssign(unaryType); switch (unaryType) { case CSharpExpressionType.PreIncrementAssignChecked: case CSharpExpressionType.PreDecrementAssignChecked: case CSharpExpressionType.PostIncrementAssignChecked: case CSharpExpressionType.PostDecrementAssignChecked: binderFlags |= CSharpBinderFlags.CheckedContext; break; } return(new AssignUnaryDynamicCSharpExpression(context, binderFlags, unaryType, operand)); }
private static void ValidateCustomBinaryAssign(CSharpExpressionType binaryType, Expression left, Expression right, ref MethodInfo method, LambdaExpression leftConversion, LambdaExpression finalConversion) { var leftType = left.Type; var rightType = right.Type; if (leftConversion != null) { leftType = ValidateConversion(binaryType, leftType, leftConversion); } // NB: Just leverage LINQ to do the dirty work to check everything. Note that this assumes that the // left conversion performed widening if that's required for the underlying operation. However, // the use of FunctionalOp below will widen the right operand to match the left operand's type, // which is what we do during Reduce as well (see remarks in FunctionalOp). This could produce // mysterious error messages. (TODO: Review what's most appropriate here.) // // NB: We can't have it check the final conversion, because it doesn't allow these without the use of // a custom method, so we check that ourselves further down. var leftDummy = Expression.Parameter(leftType, "__left"); var rightDummy = Expression.Parameter(rightType, "__right"); var functionalOp = FunctionalOp(binaryType, leftDummy, rightDummy, method); if (method == null) { method = functionalOp.Method; } var resultType = functionalOp.Type; if (finalConversion != null) { resultType = ValidateConversion(binaryType, resultType, finalConversion); } if (!TypeUtils.AreEquivalent(resultType, left.Type)) { throw Error.InvalidCompoundAssignmentWithOperands(binaryType, left.Type, right.Type); } }
private static UnaryExpression FunctionalOp(CSharpExpressionType unaryType, Expression operand, MethodInfo method) { switch (unaryType) { case CSharpExpressionType.PreDecrementAssign: case CSharpExpressionType.PreDecrementAssignChecked: return(Expression.PreDecrementAssign(operand, method)); case CSharpExpressionType.PreIncrementAssign: case CSharpExpressionType.PreIncrementAssignChecked: return(Expression.PreIncrementAssign(operand, method)); case CSharpExpressionType.PostDecrementAssign: case CSharpExpressionType.PostDecrementAssignChecked: return(Expression.PostDecrementAssign(operand, method)); case CSharpExpressionType.PostIncrementAssign: case CSharpExpressionType.PostIncrementAssignChecked: return(Expression.PostIncrementAssign(operand, method)); } throw LinqError.UnhandledUnary(unaryType); }
private static ExpressionType ConvertNodeType(CSharpExpressionType nodeType) { // NB: Only used for ToCSharp pretty printing; maybe we can remove it? switch (nodeType) { case CSharpExpressionType.Assign: return ExpressionType.Assign; case CSharpExpressionType.AddAssign: return ExpressionType.AddAssign; case CSharpExpressionType.SubtractAssign: return ExpressionType.SubtractAssign; case CSharpExpressionType.MultiplyAssign: return ExpressionType.MultiplyAssign; case CSharpExpressionType.DivideAssign: return ExpressionType.DivideAssign; case CSharpExpressionType.ModuloAssign: return ExpressionType.ModuloAssign; case CSharpExpressionType.AndAssign: return ExpressionType.AndAssign; case CSharpExpressionType.OrAssign: return ExpressionType.OrAssign; case CSharpExpressionType.ExclusiveOrAssign: return ExpressionType.ExclusiveOrAssign; case CSharpExpressionType.LeftShiftAssign: return ExpressionType.LeftShiftAssign; case CSharpExpressionType.RightShiftAssign: return ExpressionType.RightShiftAssign; case CSharpExpressionType.AddAssignChecked: return ExpressionType.AddAssignChecked; case CSharpExpressionType.MultiplyAssignChecked: return ExpressionType.MultiplyAssignChecked; case CSharpExpressionType.SubtractAssignChecked: return ExpressionType.SubtractAssignChecked; default: throw ContractUtils.Unreachable; } }
private static ExpressionType ConvertNodeType(CSharpExpressionType nodeType) { switch (nodeType) { case CSharpExpressionType.PreIncrementAssign: return(ExpressionType.PreIncrementAssign); case CSharpExpressionType.PreIncrementAssignChecked: return(ExpressionType.PreIncrementAssign); case CSharpExpressionType.PreDecrementAssign: return(ExpressionType.PreDecrementAssign); case CSharpExpressionType.PreDecrementAssignChecked: return(ExpressionType.PreDecrementAssign); case CSharpExpressionType.PostIncrementAssign: return(ExpressionType.PostIncrementAssign); case CSharpExpressionType.PostIncrementAssignChecked: return(ExpressionType.PostIncrementAssign); case CSharpExpressionType.PostDecrementAssign: return(ExpressionType.PostDecrementAssign); case CSharpExpressionType.PostDecrementAssignChecked: return(ExpressionType.PostDecrementAssign); default: throw ContractUtils.Unreachable; } }
private static UnaryExpression FunctionalOp(CSharpExpressionType unaryType, Expression operand, MethodInfo method) { switch (unaryType) { case CSharpExpressionType.PreDecrementAssign: case CSharpExpressionType.PreDecrementAssignChecked: return Expression.PreDecrementAssign(operand, method); case CSharpExpressionType.PreIncrementAssign: case CSharpExpressionType.PreIncrementAssignChecked: return Expression.PreIncrementAssign(operand, method); case CSharpExpressionType.PostDecrementAssign: case CSharpExpressionType.PostDecrementAssignChecked: return Expression.PostDecrementAssign(operand, method); case CSharpExpressionType.PostIncrementAssign: case CSharpExpressionType.PostIncrementAssignChecked: return Expression.PostIncrementAssign(operand, method); } throw LinqError.UnhandledUnary(unaryType); }
/// <summary> /// Creates an expression representing an event assignment operation. /// </summary> /// <param name="type">The type of assignment represented.</param> /// <param name="object">The instance on which to access the event, or <c>null</c> if the event is static.</param> /// <param name="event">The event to add or remove a handler on.</param> /// <param name="handler">The handler to add to or remove from the event.</param> /// <returns>A new <see cref="EventAssignCSharpExpression"/> instance representing the event assignment.</returns> public static EventAssignCSharpExpression MakeEventAssign(CSharpExpressionType type, Expression @object, EventInfo @event, Expression handler) => type switch {
/// <summary> /// Creates a new expression representing a dynamically bound binary assignment operation with the specified binder flags. /// </summary> /// <param name="binaryType">The type of the binary operation to perform.</param> /// <param name="left">The dynamic argument representing the left operand of the operation.</param> /// <param name="right">The dynamic argument representing the right operand of the operation.</param> /// <param name="binderFlags">The binder flags to use for the dynamic operation.</param> /// <returns>A new expression representing a dynamically bound binary operation.</returns> public static AssignBinaryDynamicCSharpExpression MakeDynamicBinaryAssign(CSharpExpressionType binaryType, DynamicCSharpArgument left, DynamicCSharpArgument right, CSharpBinderFlags binderFlags) { return MakeDynamicBinaryAssign(binaryType, left, right, binderFlags, null); }
/// <summary> /// Creates an expression representing a unary assignment operation. /// </summary> /// <param name="unaryType">The type of assignment represented.</param> /// <param name="operand">The operand of the assignment operation, i.e. the assignment target.</param> /// <param name="method">The method implementing the assignment operation.</param> /// <returns>A new <see cref="AssignUnaryCSharpExpression"/> instance representing the unary assignment.</returns> public static AssignUnaryCSharpExpression MakeUnaryAssign(CSharpExpressionType unaryType, Expression operand, MethodInfo method) { switch (unaryType) { case CSharpExpressionType.PreIncrementAssign: return PreIncrementAssign(operand, method); case CSharpExpressionType.PreIncrementAssignChecked: return PreIncrementAssignChecked(operand, method); case CSharpExpressionType.PreDecrementAssign: return PreDecrementAssign(operand, method); case CSharpExpressionType.PreDecrementAssignChecked: return PreDecrementAssignChecked(operand, method); case CSharpExpressionType.PostIncrementAssign: return PostIncrementAssign(operand, method); case CSharpExpressionType.PostIncrementAssignChecked: return PostIncrementAssignChecked(operand, method); case CSharpExpressionType.PostDecrementAssign: return PostDecrementAssign(operand, method); case CSharpExpressionType.PostDecrementAssignChecked: return PostDecrementAssignChecked(operand, method); } throw LinqError.UnhandledUnary(unaryType); }
/// <summary> /// Creates a new expression representing a dynamically bound unary assignment operation with the specified binder flags. /// </summary> /// <param name="unaryType">The type of the unary operation to perform.</param> /// <param name="operand">The dynamic argument representing the operand of the operation.</param> /// <param name="binderFlags">The binder flags to use for the dynamic operation.</param> /// <returns>A new expression representing a dynamically bound unary assignment operation.</returns> public static AssignUnaryDynamicCSharpExpression MakeDynamicUnaryAssign(CSharpExpressionType unaryType, DynamicCSharpArgument operand, CSharpBinderFlags binderFlags) { return(MakeDynamicUnaryAssign(unaryType, operand, binderFlags, null)); }
private static AssignUnaryCSharpExpression MakeUnaryAssignCore(CSharpExpressionType unaryType, Expression operand, MethodInfo method) { RequiresCanRead(operand, nameof(operand)); Helpers.RequiresCanWrite(operand, nameof(operand)); return AssignUnaryCSharpExpression.Make(unaryType, operand, method); }
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 (!TypeUtils.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 (!TypeUtils.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 our 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); }
private AssignBinaryCSharpExpression(CSharpExpressionType binaryType, Expression left, Expression right) : base(left, right) { CSharpNodeType = binaryType; }
private static Type ValidateConversion(CSharpExpressionType nodeType, Type inputType, LambdaExpression conversion) { var invoke = conversion.Type.GetMethod("Invoke"); var invokeParameters = invoke.GetParametersCached(); if (invokeParameters.Length != 1) { throw LinqError.IncorrectNumberOfMethodCallArguments(conversion); } if (!TypeUtils.AreEquivalent(invokeParameters[0].ParameterType, inputType)) { throw LinqError.OperandTypesDoNotMatchParameters(nodeType, conversion.ToString()); } return invoke.ReturnType; }
public Custom(CSharpExpressionType unaryType, Expression operand, MethodInfo method) : base(unaryType, operand) { Method = method; }
/// <summary> /// Creates an expression representing a binary assignment operation. /// </summary> /// <param name="binaryType">The type of assignment represented.</param> /// <param name="left">The left operand of the assignment operation, i.e. the assignment target.</param> /// <param name="right">The right operation of the assignment operation.</param> /// <param name="method">The method implementing the assignment operation.</param> /// <param name="leftConversion">The conversion function used to convert the left hand side of the compound assignment prior to use by the underlying operation.</param> /// <param name="finalConversion">The conversion function used to convert the result of the underlying operation prior to assignment to the left hand side of the compound assignment operation.</param> /// <returns>A new <see cref="AssignBinaryCSharpExpression"/> instance representing the binary assignment.</returns> public static AssignBinaryCSharpExpression MakeBinaryAssign(CSharpExpressionType binaryType, Expression left, Expression right, MethodInfo method, LambdaExpression leftConversion, LambdaExpression finalConversion) { switch (binaryType) { case CSharpExpressionType.Assign: return Assign(left, right); case CSharpExpressionType.AddAssign: return AddAssign(left, right, method, leftConversion, finalConversion); case CSharpExpressionType.AndAssign: return AndAssign(left, right, method, leftConversion, finalConversion); case CSharpExpressionType.DivideAssign: return DivideAssign(left, right, method, leftConversion, finalConversion); case CSharpExpressionType.ExclusiveOrAssign: return ExclusiveOrAssign(left, right, method, leftConversion, finalConversion); case CSharpExpressionType.LeftShiftAssign: return LeftShiftAssign(left, right, method, leftConversion, finalConversion); case CSharpExpressionType.ModuloAssign: return ModuloAssign(left, right, method, leftConversion, finalConversion); case CSharpExpressionType.MultiplyAssign: return MultiplyAssign(left, right, method, leftConversion, finalConversion); case CSharpExpressionType.OrAssign: return OrAssign(left, right, method, leftConversion, finalConversion); case CSharpExpressionType.RightShiftAssign: return RightShiftAssign(left, right, method, leftConversion, finalConversion); case CSharpExpressionType.SubtractAssign: return SubtractAssign(left, right, method, leftConversion, finalConversion); case CSharpExpressionType.AddAssignChecked: return AddAssignChecked(left, right, method, leftConversion, finalConversion); case CSharpExpressionType.MultiplyAssignChecked: return MultiplyAssignChecked(left, right, method, leftConversion, finalConversion); case CSharpExpressionType.SubtractAssignChecked: return SubtractAssignChecked(left, right, method, leftConversion, finalConversion); } throw LinqError.UnhandledBinary(binaryType); }
/// <summary> /// Creates a new expression representing a dynamically bound binary assignment operation with the specified binder flags. /// </summary> /// <param name="binaryType">The type of the binary operation to perform.</param> /// <param name="left">The dynamic argument representing the left operand of the operation.</param> /// <param name="right">The dynamic argument representing the right operand of the operation.</param> /// <param name="binderFlags">The binder flags to use for the dynamic operation.</param> /// <returns>A new expression representing a dynamically bound binary operation.</returns> public static AssignBinaryDynamicCSharpExpression MakeDynamicBinaryAssign(CSharpExpressionType binaryType, DynamicCSharpArgument left, DynamicCSharpArgument right, CSharpBinderFlags binderFlags) { return(MakeDynamicBinaryAssign(binaryType, left, right, binderFlags, null)); }
/// <summary> /// Creates a new expression representing a dynamically bound unary assignment operation. /// </summary> /// <param name="unaryType">The type of the unary operation to perform.</param> /// <param name="operand">The expression representing the operand of the operation.</param> /// <returns>A new expression representing a dynamically bound unary assignment operation.</returns> public static AssignUnaryDynamicCSharpExpression MakeDynamicUnaryAssign(CSharpExpressionType unaryType, Expression operand) => MakeDynamicUnaryAssign(unaryType, DynamicArgument(operand), CSharpBinderFlags.None, context: null);
internal static AssignBinaryCSharpExpression Make(CSharpExpressionType binaryType, Expression left, Expression right, MethodInfo method, LambdaExpression leftConversion, LambdaExpression finalConversion) { ValidateCustomBinaryAssign(binaryType, left, right, ref method, leftConversion, finalConversion); // TODO: Add optimized layouts return new WithConversions(binaryType, left, right, method, leftConversion, finalConversion); }
internal static AssignUnaryCSharpExpression Make(CSharpExpressionType unaryType, Expression operand, MethodInfo method) { ValidateCustomUnaryAssign(unaryType, operand, ref method); // TODO: Add optimized layouts return new Custom(unaryType, operand, method); }
/// <summary> /// Creates a new expression representing a dynamically bound unary assignment operation. /// </summary> /// <param name="unaryType">The type of the unary operation to perform.</param> /// <param name="operand">The expression representing the operand of the operation.</param> /// <returns>A new expression representing a dynamically bound unary assignment operation.</returns> public static AssignUnaryDynamicCSharpExpression MakeDynamicUnaryAssign(CSharpExpressionType unaryType, Expression operand) { return(MakeDynamicUnaryAssign(unaryType, DynamicArgument(operand), CSharpBinderFlags.None, null)); }
internal static BinaryExpression FunctionalOp(CSharpExpressionType binaryType, Expression left, Expression right, MethodInfo method) { switch (binaryType) { case CSharpExpressionType.Assign: return Expression.Assign(left, right); case CSharpExpressionType.AddAssign: return Expression.Add(left, right, method); case CSharpExpressionType.AndAssign: return Expression.And(left, right, method); case CSharpExpressionType.DivideAssign: return Expression.Divide(left, right, method); case CSharpExpressionType.ExclusiveOrAssign: return Expression.ExclusiveOr(left, right, method); case CSharpExpressionType.LeftShiftAssign: return Expression.LeftShift(left, right, method); case CSharpExpressionType.ModuloAssign: return Expression.Modulo(left, right, method); case CSharpExpressionType.MultiplyAssign: return Expression.Multiply(left, right, method); case CSharpExpressionType.OrAssign: return Expression.Or(left, right, method); case CSharpExpressionType.RightShiftAssign: return Expression.RightShift(left, right, method); case CSharpExpressionType.SubtractAssign: return Expression.Subtract(left, right, method); case CSharpExpressionType.AddAssignChecked: return Expression.AddChecked(left, right, method); case CSharpExpressionType.MultiplyAssignChecked: return Expression.MultiplyChecked(left, right, method); case CSharpExpressionType.SubtractAssignChecked: return Expression.SubtractChecked(left, right, method); } throw LinqError.UnhandledBinary(binaryType); }
private static ExpressionType ConvertNodeType(CSharpExpressionType nodeType) => nodeType switch {
public static AssignUnaryDynamicCSharpExpression MakeDynamicUnaryAssign(CSharpExpressionType unaryType, DynamicCSharpArgument operand, CSharpBinderFlags binderFlags, Type context) { ContractUtils.RequiresNotNull(operand, nameof(operand)); RequiresCanRead(operand.Expression, nameof(operand)); RequiresCanWrite(operand.Expression, nameof(operand)); CheckUnaryAssign(unaryType); switch (unaryType) { case CSharpExpressionType.PreIncrementAssignChecked: case CSharpExpressionType.PreDecrementAssignChecked: case CSharpExpressionType.PostIncrementAssignChecked: case CSharpExpressionType.PostDecrementAssignChecked: binderFlags |= CSharpBinderFlags.CheckedContext; break; } return new AssignUnaryDynamicCSharpExpression(context, binderFlags, unaryType, operand); }
/// <summary> /// Creates a new expression representing a dynamically bound binary assignment operation. /// </summary> /// <param name="binaryType">The type of the binary operation to perform.</param> /// <param name="left">The expression representing the left operand of the operation.</param> /// <param name="right">The expression representing the right operand of the operation.</param> /// <returns>A new expression representing a dynamically bound binary operation.</returns> public static AssignBinaryDynamicCSharpExpression MakeDynamicBinaryAssign(CSharpExpressionType binaryType, Expression left, Expression right) { return MakeDynamicBinaryAssign(binaryType, DynamicArgument(left), DynamicArgument(right), CSharpBinderFlags.None, null); }
internal AssignUnaryDynamicCSharpExpression(Type context, CSharpBinderFlags binderFlags, CSharpExpressionType unaryType, DynamicCSharpArgument operand) : base(context, binderFlags) { OperationNodeType = unaryType; Operand = operand; }
public static AssignBinaryDynamicCSharpExpression MakeDynamicBinaryAssign(CSharpExpressionType binaryType, DynamicCSharpArgument left, DynamicCSharpArgument right, CSharpBinderFlags binderFlags, Type context) { ContractUtils.RequiresNotNull(left, nameof(left)); ContractUtils.RequiresNotNull(right, nameof(right)); RequiresCanWrite(left.Expression, nameof(left)); RequiresCanRead(right.Expression, nameof(right)); CheckBinaryAssign(binaryType); switch (binaryType) { case CSharpExpressionType.AddAssignChecked: case CSharpExpressionType.SubtractAssignChecked: case CSharpExpressionType.MultiplyAssignChecked: binderFlags |= CSharpBinderFlags.CheckedContext; break; } return new AssignBinaryDynamicCSharpExpression(context, binderFlags, binaryType, left, right); }
/// <summary> /// Creates a new expression representing a dynamically bound unary assignment operation with the specified binder flags. /// </summary> /// <param name="unaryType">The type of the unary operation to perform.</param> /// <param name="operand">The dynamic argument representing the operand of the operation.</param> /// <param name="binderFlags">The binder flags to use for the dynamic operation.</param> /// <returns>A new expression representing a dynamically bound unary assignment operation.</returns> public static AssignUnaryDynamicCSharpExpression MakeDynamicUnaryAssign(CSharpExpressionType unaryType, DynamicCSharpArgument operand, CSharpBinderFlags binderFlags) { return MakeDynamicUnaryAssign(unaryType, operand, binderFlags, null); }
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)); }
/// <summary> /// Creates a new expression representing a dynamically bound binary assignment operation. /// </summary> /// <param name="binaryType">The type of the binary operation to perform.</param> /// <param name="left">The expression representing the left operand of the operation.</param> /// <param name="right">The expression representing the right operand of the operation.</param> /// <returns>A new expression representing a dynamically bound binary operation.</returns> public static AssignBinaryDynamicCSharpExpression MakeDynamicBinaryAssign(CSharpExpressionType binaryType, Expression left, Expression right) { return(MakeDynamicBinaryAssign(binaryType, DynamicArgument(left), DynamicArgument(right), CSharpBinderFlags.None, null)); }
/// <summary> /// Creates a new expression representing a dynamically bound unary assignment operation. /// </summary> /// <param name="unaryType">The type of the unary operation to perform.</param> /// <param name="operand">The expression representing the operand of the operation.</param> /// <returns>A new expression representing a dynamically bound unary assignment operation.</returns> public static AssignUnaryDynamicCSharpExpression MakeDynamicUnaryAssign(CSharpExpressionType unaryType, Expression operand) { return MakeDynamicUnaryAssign(unaryType, DynamicArgument(operand), CSharpBinderFlags.None, null); }
internal AssignUnaryCSharpExpression(CSharpExpressionType unaryType, Expression operand) : base(operand) { CSharpNodeType = unaryType; }
private static void CheckBinaryAssign(CSharpExpressionType binaryType) { switch (binaryType) { case CSharpExpressionType.Assign: case CSharpExpressionType.AddAssign: case CSharpExpressionType.AndAssign: case CSharpExpressionType.DivideAssign: case CSharpExpressionType.ExclusiveOrAssign: case CSharpExpressionType.LeftShiftAssign: case CSharpExpressionType.ModuloAssign: case CSharpExpressionType.MultiplyAssign: case CSharpExpressionType.OrAssign: case CSharpExpressionType.RightShiftAssign: case CSharpExpressionType.SubtractAssign: case CSharpExpressionType.AddAssignChecked: case CSharpExpressionType.MultiplyAssignChecked: case CSharpExpressionType.SubtractAssignChecked: break; default: throw LinqError.NotSupported(); } }
private static bool IsCheckedBinary(CSharpExpressionType type) { switch (type) { case CSharpExpressionType.AddAssignChecked: case CSharpExpressionType.MultiplyAssignChecked: case CSharpExpressionType.SubtractAssignChecked: return true; } return false; }
/// <summary> /// Creates a new expression representing a dynamically bound unary assignment operation with the specified binder flags. /// </summary> /// <param name="unaryType">The type of the unary operation to perform.</param> /// <param name="operand">The dynamic argument representing the operand of the operation.</param> /// <param name="binderFlags">The binder flags to use for the dynamic operation.</param> /// <returns>A new expression representing a dynamically bound unary assignment operation.</returns> public static AssignUnaryDynamicCSharpExpression MakeDynamicUnaryAssign(CSharpExpressionType unaryType, DynamicCSharpArgument operand, CSharpBinderFlags binderFlags) => MakeDynamicUnaryAssign(unaryType, operand, binderFlags, context: null);
private static void CheckUnaryAssign(CSharpExpressionType unaryType) { switch (unaryType) { case CSharpExpressionType.PreIncrementAssign: case CSharpExpressionType.PreDecrementAssign: case CSharpExpressionType.PostIncrementAssign: case CSharpExpressionType.PostDecrementAssign: case CSharpExpressionType.PreIncrementAssignChecked: case CSharpExpressionType.PreDecrementAssignChecked: case CSharpExpressionType.PostIncrementAssignChecked: case CSharpExpressionType.PostDecrementAssignChecked: break; default: throw LinqError.NotSupported(); } }