Ejemplo n.º 1
0
        protected internal override Expression VisitBinaryAssign(AssignBinaryCSharpExpression node)
        {
            var args = new List <object>();

            if (node.IsLifted)
            {
                args.Add(new XAttribute(nameof(node.IsLifted), node.IsLifted));
            }

            if (node.IsLiftedToNull)
            {
                args.Add(new XAttribute(nameof(node.IsLiftedToNull), node.IsLiftedToNull));
            }

            if (node.Method != null)
            {
                args.Add(new XAttribute(nameof(node.Method), node.Method));
            }

            args.Add(new XElement(nameof(node.Left), Visit(node.Left)));
            args.Add(new XElement(nameof(node.Right), Visit(node.Right)));

            if (node.LeftConversion != null)
            {
                args.Add(new XElement(nameof(node.LeftConversion), Visit(node.LeftConversion)));
            }

            if (node.FinalConversion != null)
            {
                args.Add(new XElement(nameof(node.FinalConversion), Visit(node.FinalConversion)));
            }

            return(Push(node, args));
        }
 public AssignBinaryCSharpExpressionProxy(AssignBinaryCSharpExpression node)
 {
     _node = node;
 }
 protected internal virtual Expression VisitBinaryAssign(AssignBinaryCSharpExpression node)
 {
     return(node.Update(Visit(node.Left), VisitAndConvert(node.LeftConversion, nameof(VisitBinaryAssign)), Visit(node.Right), VisitAndConvert(node.FinalConversion, nameof(VisitBinaryAssign))));
 }
        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 AssignBinaryCSharpExpressionProxy(AssignBinaryCSharpExpression node)
 {
     _node = node;
 }
 protected internal virtual Expression VisitBinaryAssign(AssignBinaryCSharpExpression node)
 {
     return node.Update(Visit(node.Left), VisitAndConvert(node.LeftConversion, nameof(VisitBinaryAssign)), Visit(node.Right), VisitAndConvert(node.FinalConversion, nameof(VisitBinaryAssign)));
 }