DebugViewWriter.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Core / Microsoft / Scripting / Ast / DebugViewWriter.cs / 1305376 / DebugViewWriter.cs

                            /* **************************************************************************** 
 *
 * Copyright (c) Microsoft Corporation.
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 * 
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/ 

using System.Collections.Generic; 
using System.Diagnostics; 
using System.Dynamic;
using System.Dynamic.Utils; 
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices; 
using System.Collections.ObjectModel;
 
namespace System.Linq.Expressions { 
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
    internal sealed class DebugViewWriter : ExpressionVisitor { 
        [Flags]
        private enum Flow {
            None,
            Space, 
            NewLine,
 
            Break = 0x8000      // newline if column > MaxColumn 
        };
 
        private const int Tab = 4;
        private const int MaxColumn = 120;

        private TextWriter _out; 
        private int _column;
 
        private Stack _stack = new Stack(); 
        private int _delta;
        private Flow _flow; 

        // All the unique lambda expressions in the ET, will be used for displaying all
        // the lambda definitions.
        private Queue _lambdas; 

        // Associate every unique anonymous LambdaExpression in the tree with an integer. 
        // The id is used to create a name for the anonymous lambda. 
        //
        private Dictionary _lambdaIds; 

        // Associate every unique anonymous parameter or variable in the tree with an integer.
        // The id is used to create a name for the anonymous parameter or variable.
        // 
        private Dictionary _paramIds;
 
        // Associate every unique anonymous LabelTarget in the tree with an integer. 
        // The id is used to create a name for the anonymous LabelTarget.
        // 
        private Dictionary _labelIds;

        private DebugViewWriter(TextWriter file) {
            _out = file; 
        }
 
        private int Base { 
            get {
                return _stack.Count > 0 ? _stack.Peek() : 0; 
            }
        }

        private int Delta { 
            get { return _delta; }
        } 
 
        private int Depth {
            get { return Base + Delta; } 
        }

        private void Indent() {
            _delta += Tab; 
        }
        private void Dedent() { 
            _delta -= Tab; 
        }
 
        private void NewLine() {
            _flow = Flow.NewLine;
        }
 
        private static int GetId(T e, ref Dictionary ids) {
            if (ids == null) { 
                ids = new Dictionary(); 
                ids.Add(e, 1);
                return 1; 
            } else {
                int id;
                if (!ids.TryGetValue(e, out id)) {
                    // e is met the first time 
                    id = ids.Count + 1;
                    ids.Add(e, id); 
                } 
                return id;
            } 
        }

        private int GetLambdaId(LambdaExpression le) {
            Debug.Assert(String.IsNullOrEmpty(le.Name)); 
            return GetId(le, ref _lambdaIds);
        } 
 
        private int GetParamId(ParameterExpression p) {
            Debug.Assert(String.IsNullOrEmpty(p.Name)); 
            return GetId(p, ref _paramIds);
        }

        private int GetLabelTargetId(LabelTarget target) { 
            Debug.Assert(String.IsNullOrEmpty(target.Name));
            return GetId(target, ref _labelIds); 
        } 

        ///  
        /// Write out the given AST
        /// 
        internal static void WriteTo(Expression node, TextWriter writer) {
            Debug.Assert(node != null); 
            Debug.Assert(writer != null);
 
            new DebugViewWriter(writer).WriteTo(node); 
        }
 
        private void WriteTo(Expression node) {
            var lambda = node as LambdaExpression;
            if (lambda != null) {
                WriteLambda(lambda); 
            } else {
                Visit(node); 
                Debug.Assert(_stack.Count == 0); 
            }
 
            //
            // Output all lambda expression definitions.
            // in the order of their appearances in the tree.
            // 
            while (_lambdas != null && _lambdas.Count > 0) {
                WriteLine(); 
                WriteLine(); 
                WriteLambda(_lambdas.Dequeue());
            } 
        }

        #region The printing code
 
        private void Out(string s) {
            Out(Flow.None, s, Flow.None); 
        } 

        private void Out(Flow before, string s) { 
            Out(before, s, Flow.None);
        }

        private void Out(string s, Flow after) { 
            Out(Flow.None, s, after);
        } 
 
        private void Out(Flow before, string s, Flow after) {
            switch (GetFlow(before)) { 
                case Flow.None:
                    break;
                case Flow.Space:
                    Write(" "); 
                    break;
                case Flow.NewLine: 
                    WriteLine(); 
                    Write(new String(' ', Depth));
                    break; 
            }
            Write(s);
            _flow = after;
        } 

        private void WriteLine() { 
            _out.WriteLine(); 
            _column = 0;
        } 
        private void Write(string s) {
            _out.Write(s);
            _column += s.Length;
        } 

        private Flow GetFlow(Flow flow) { 
            Flow last; 

            last = CheckBreak(_flow); 
            flow = CheckBreak(flow);

            // Get the biggest flow that is requested None < Space < NewLine
            return (Flow)System.Math.Max((int)last, (int)flow); 
        }
 
        private Flow CheckBreak(Flow flow) { 
            if ((flow & Flow.Break) != 0) {
                if (_column > (MaxColumn + Depth)) { 
                    flow = Flow.NewLine;
                } else {
                    flow &= ~Flow.Break;
                } 
            }
            return flow; 
        } 

        #endregion 

        #region The AST Output

        // More proper would be to make this a virtual method on Action 
        private static string FormatBinder(CallSiteBinder binder) {
            ConvertBinder convert; 
            GetMemberBinder getMember; 
            SetMemberBinder setMember;
            DeleteMemberBinder deleteMember; 
            GetIndexBinder getIndex;
            SetIndexBinder setIndex;
            DeleteIndexBinder deleteIndex;
            InvokeMemberBinder call; 
            InvokeBinder invoke;
            CreateInstanceBinder create; 
            UnaryOperationBinder unary; 
            BinaryOperationBinder binary;
 
            if ((convert = binder as ConvertBinder) != null) {
                return "Convert " + convert.Type.ToString();
            } else if ((getMember = binder as GetMemberBinder) != null) {
                return "GetMember " + getMember.Name; 
            } else if ((setMember = binder as SetMemberBinder) != null) {
                return "SetMember " + setMember.Name; 
            } else if ((deleteMember = binder as DeleteMemberBinder) != null) { 
                return "DeleteMember " + deleteMember.Name;
            } else if ((getIndex = binder as GetIndexBinder) != null) { 
                return "GetIndex";
            } else if ((setIndex = binder as SetIndexBinder) != null) {
                return "SetIndex";
            } else if ((deleteIndex = binder as DeleteIndexBinder) != null) { 
                return "DeleteIndex";
            } else if ((call = binder as InvokeMemberBinder) != null) { 
                return "Call " + call.Name; 
            } else if ((invoke = binder as InvokeBinder) != null) {
                return "Invoke"; 
            } else if ((create = binder as CreateInstanceBinder) != null) {
                return "Create";
            } else if ((unary = binder as UnaryOperationBinder) != null) {
                return "UnaryOperation " + unary.Operation; 
            } else if ((binary = binder as BinaryOperationBinder) != null) {
                return "BinaryOperation " + binary.Operation; 
            } else { 
                return binder.ToString();
            } 
        }

        private void VisitExpressions(char open, IList expressions) where T : Expression {
            VisitExpressions(open, ',', expressions); 
        }
 
        private void VisitExpressions(char open, char separator, IList expressions) where T : Expression { 
            VisitExpressions(open, separator, expressions, e => Visit(e));
        } 

        private void VisitDeclarations(IList expressions) {
            VisitExpressions('(', ',', expressions, variable =>
            { 
                Out(variable.Type.ToString());
                if (variable.IsByRef) { 
                    Out("&"); 
                }
                Out(" "); 
                VisitParameter(variable);
            });
        }
 
        private void VisitExpressions(char open, char separator, IList expressions, Action visit) {
            Out(open.ToString()); 
 
            if (expressions != null) {
                Indent(); 
                bool isFirst = true;
                foreach (T e in expressions) {
                    if (isFirst) {
                        if (open == '{' || expressions.Count > 1) { 
                            NewLine();
                        } 
                        isFirst = false; 
                    } else {
                        Out(separator.ToString(), Flow.NewLine); 
                    }
                    visit(e);
                }
                Dedent(); 
            }
 
            char close; 
            switch (open) {
                case '(': close = ')'; break; 
                case '{': close = '}'; break;
                case '[': close = ']'; break;
                case '<': close = '>'; break;
                default: throw ContractUtils.Unreachable; 
            }
 
            if (open == '{') { 
                NewLine();
            } 
            Out(close.ToString(), Flow.Break);
        }

        protected internal override Expression VisitDynamic(DynamicExpression node) { 
            Out(".Dynamic", Flow.Space);
            Out(FormatBinder(node.Binder)); 
            VisitExpressions('(', node.Arguments); 
            return node;
        } 

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        protected internal override Expression VisitBinary(BinaryExpression node) {
            if (node.NodeType == ExpressionType.ArrayIndex) { 
                ParenthesizedVisit(node, node.Left);
                Out("["); 
                Visit(node.Right); 
                Out("]");
            } else { 
                bool parenthesizeLeft = NeedsParentheses(node, node.Left);
                bool parenthesizeRight = NeedsParentheses(node, node.Right);

                string op; 
                bool isChecked = false;
                Flow beforeOp = Flow.Space; 
                switch (node.NodeType) { 
                    case ExpressionType.Assign: op = "="; break;
                    case ExpressionType.Equal: op = "=="; break; 
                    case ExpressionType.NotEqual: op = "!="; break;
                    case ExpressionType.AndAlso: op = "&&"; beforeOp = Flow.Break | Flow.Space; break;
                    case ExpressionType.OrElse: op = "||"; beforeOp = Flow.Break | Flow.Space; break;
                    case ExpressionType.GreaterThan: op = ">"; break; 
                    case ExpressionType.LessThan: op = "<"; break;
                    case ExpressionType.GreaterThanOrEqual: op = ">="; break; 
                    case ExpressionType.LessThanOrEqual: op = "<="; break; 
                    case ExpressionType.Add: op = "+"; break;
                    case ExpressionType.AddAssign: op = "+="; break; 
                    case ExpressionType.AddAssignChecked: op = "+="; isChecked = true; break;
                    case ExpressionType.AddChecked: op = "+"; isChecked = true; break;
                    case ExpressionType.Subtract: op = "-"; break;
                    case ExpressionType.SubtractAssign: op = "-="; break; 
                    case ExpressionType.SubtractAssignChecked: op = "-="; isChecked = true; break;
                    case ExpressionType.SubtractChecked: op = "-"; isChecked = true; break; 
                    case ExpressionType.Divide: op = "/"; break; 
                    case ExpressionType.DivideAssign: op = "/="; break;
                    case ExpressionType.Modulo: op = "%"; break; 
                    case ExpressionType.ModuloAssign: op = "%="; break;
                    case ExpressionType.Multiply: op = "*"; break;
                    case ExpressionType.MultiplyAssign: op = "*="; break;
                    case ExpressionType.MultiplyAssignChecked: op = "*="; isChecked = true; break; 
                    case ExpressionType.MultiplyChecked: op = "*"; isChecked = true; break;
                    case ExpressionType.LeftShift: op = "<<"; break; 
                    case ExpressionType.LeftShiftAssign: op = "<<="; break; 
                    case ExpressionType.RightShift: op = ">>"; break;
                    case ExpressionType.RightShiftAssign: op = ">>="; break; 
                    case ExpressionType.And: op = "&"; break;
                    case ExpressionType.AndAssign: op = "&="; break;
                    case ExpressionType.Or: op = "|"; break;
                    case ExpressionType.OrAssign: op = "|="; break; 
                    case ExpressionType.ExclusiveOr: op = "^"; break;
                    case ExpressionType.ExclusiveOrAssign: op = "^="; break; 
                    case ExpressionType.Power: op = "**"; break; 
                    case ExpressionType.PowerAssign: op = "**="; break;
                    case ExpressionType.Coalesce: op = "??"; break; 

                    default:
                        throw new InvalidOperationException();
                } 

                if (parenthesizeLeft) { 
                    Out("(", Flow.None); 
                }
 
                Visit(node.Left);
                if (parenthesizeLeft) {
                    Out(Flow.None, ")", Flow.Break);
                } 

                // prepend # to the operator to represent checked op 
                if (isChecked) { 
                    op = String.Format(
                            CultureInfo.CurrentCulture, 
                            "#{0}",
                            op
                    );
                } 
                Out(beforeOp, op, Flow.Space | Flow.Break);
 
                if (parenthesizeRight) { 
                    Out("(", Flow.None);
                } 
                Visit(node.Right);
                if (parenthesizeRight) {
                    Out(Flow.None, ")", Flow.Break);
                } 
            }
            return node; 
        } 

        protected internal override Expression VisitParameter(ParameterExpression node) { 
            // Have '$' for the DebugView of ParameterExpressions
            Out("$");
            if (String.IsNullOrEmpty(node.Name)) {
                // If no name if provided, generate a name as $var1, $var2. 
                // No guarantee for not having name conflicts with user provided variable names.
                // 
                int id = GetParamId(node); 
                Out("var" + id);
            } else { 
                Out(GetDisplayName(node.Name));
            }
            return node;
        } 

        protected internal override Expression VisitLambda(Expression node) { 
            Out( 
                String.Format(CultureInfo.CurrentCulture,
                    "{0} {1}<{2}>", 
                    ".Lambda",
                    GetLambdaName(node),
                    node.Type.ToString()
                ) 
            );
 
            if (_lambdas == null) { 
                _lambdas = new Queue();
            } 

            // N^2 performance, for keeping the order of the lambdas.
            if (!_lambdas.Contains(node)) {
                _lambdas.Enqueue(node); 
            }
 
            return node; 
        }
 
        private static bool IsSimpleExpression(Expression node) {
            var binary = node as BinaryExpression;
            if (binary != null) {
                return !(binary.Left is BinaryExpression || binary.Right is BinaryExpression); 
            }
 
            return false; 
        }
 
        protected internal override Expression VisitConditional(ConditionalExpression node) {
            if (IsSimpleExpression(node.Test)) {
                Out(".If (");
                Visit(node.Test); 
                Out(") {", Flow.NewLine);
            } else { 
                Out(".If (", Flow.NewLine); 
                Indent();
                Visit(node.Test); 
                Dedent();
                Out(Flow.NewLine, ") {", Flow.NewLine);
            }
            Indent(); 
            Visit(node.IfTrue);
            Dedent(); 
            Out(Flow.NewLine, "} .Else {", Flow.NewLine); 
            Indent();
            Visit(node.IfFalse); 
            Dedent();
            Out(Flow.NewLine, "}");
            return node;
        } 

        protected internal override Expression VisitConstant(ConstantExpression node) { 
            object value = node.Value; 

            if (value == null) { 
                Out("null");
            } else if ((value is string) && node.Type == typeof(string)) {
                Out(String.Format(
                    CultureInfo.CurrentCulture, 
                    "\"{0}\"",
                    value)); 
            } else if ((value is char) && node.Type == typeof(char)) { 
                    Out(String.Format(
                        CultureInfo.CurrentCulture, 
                        "'{0}'",
                        value));
            } else if ((value is int) && node.Type == typeof(int)
                || (value is bool) && node.Type == typeof(bool)) { 
                Out(value.ToString());
            } else { 
                string suffix = GetConstantValueSuffix(node.Type); 
                if (suffix != null) {
                    Out(value.ToString()); 
                    Out(suffix);
                } else {
                    Out(String.Format(
                        CultureInfo.CurrentCulture, 
                        ".Constant<{0}>({1})",
                        node.Type.ToString(), 
                        value)); 
                }
            } 
            return node;
        }

        private static string GetConstantValueSuffix(Type type) { 
            if (type == typeof(UInt32)) {
                return "U"; 
            } 
            if (type == typeof(Int64)) {
                return "L"; 
            }
            if (type == typeof(UInt64)) {
                return "UL";
            } 
            if (type == typeof(Double)) {
                return "D"; 
            } 
            if (type == typeof(Single)) {
                return "F"; 
            }
            if (type == typeof(Decimal)) {
                return "M";
            } 
            return null;
        } 
 
        protected internal override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) {
            Out(".RuntimeVariables"); 
            VisitExpressions('(', node.Variables);
            return node;
        }
 
        // Prints ".instanceField" or "declaringType.staticField"
        private void OutMember(Expression node, Expression instance, MemberInfo member) { 
            if (instance != null) { 
                ParenthesizedVisit(node, instance);
                Out("." + member.Name); 
            } else {
                // For static members, include the type name
                Out(member.DeclaringType.ToString() + "." + member.Name);
            } 
        }
 
        protected internal override Expression VisitMember(MemberExpression node) { 
            OutMember(node, node.Expression, node.Member);
            return node; 
        }

        protected internal override Expression VisitInvocation(InvocationExpression node) {
            Out(".Invoke "); 
            ParenthesizedVisit(node, node.Expression);
            VisitExpressions('(', node.Arguments); 
            return node; 
        }
 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        private static bool NeedsParentheses(Expression parent, Expression child) {
            Debug.Assert(parent != null);
            if (child == null) { 
                return false;
            } 
 
            // Some nodes always have parentheses because of how they are
            // displayed, for example: ".Unbox(obj.Foo)" 
            switch (parent.NodeType) {
                case ExpressionType.Increment:
                case ExpressionType.Decrement:
                case ExpressionType.IsTrue: 
                case ExpressionType.IsFalse:
                case ExpressionType.Unbox: 
                    return true; 
            }
 
            int childOpPrec = GetOperatorPrecedence(child);
            int parentOpPrec = GetOperatorPrecedence(parent);

            if (childOpPrec == parentOpPrec) { 
                // When parent op and child op has the same precedence,
                // we want to be a little conservative to have more clarity. 
                // Parentheses are not needed if 
                // 1) Both ops are &&, ||, &, |, or ^, all of them are the only
                // op that has the precedence. 
                // 2) Parent op is + or *, e.g. x + (y - z) can be simplified to
                // x + y - z.
                // 3) Parent op is -, / or %, and the child is the left operand.
                // In this case, if left and right operand are the same, we don't 
                // remove parenthesis, e.g. (x + y) - (x + y)
                // 
                switch (parent.NodeType) { 
                    case ExpressionType.AndAlso:
                    case ExpressionType.OrElse: 
                    case ExpressionType.And:
                    case ExpressionType.Or:
                    case ExpressionType.ExclusiveOr:
                        // Since these ops are the only ones on their precedence, 
                        // the child op must be the same.
                        Debug.Assert(child.NodeType == parent.NodeType); 
                        // We remove the parenthesis, e.g. x && y && z 
                        return false;
                    case ExpressionType.Add: 
                    case ExpressionType.AddChecked:
                    case ExpressionType.Multiply:
                    case ExpressionType.MultiplyChecked:
                        return false; 
                    case ExpressionType.Subtract:
                    case ExpressionType.SubtractChecked: 
                    case ExpressionType.Divide: 
                    case ExpressionType.Modulo:
                        BinaryExpression binary = parent as BinaryExpression; 
                        Debug.Assert(binary != null);
                        // Need to have parenthesis for the right operand.
                        return child == binary.Right;
                } 
                return true;
            } 
 
            // Special case: negate of a constant needs parentheses, to
            // disambiguate it from a negative constant. 
            if (child != null && child.NodeType == ExpressionType.Constant &&
                (parent.NodeType == ExpressionType.Negate || parent.NodeType == ExpressionType.NegateChecked)) {
                return true;
            } 

            // If the parent op has higher precedence, need parentheses for the child. 
            return childOpPrec < parentOpPrec; 
        }
 
        // the greater the higher
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        private static int GetOperatorPrecedence(Expression node) {
 
            // Roughly matches C# operator precedence, with some additional
            // operators. Also things which are not binary/unary expressions, 
            // such as conditional and type testing, don't use this mechanism. 
            switch (node.NodeType) {
                // Assignment 
                case ExpressionType.Assign:
                case ExpressionType.ExclusiveOrAssign:
                case ExpressionType.AddAssign:
                case ExpressionType.AddAssignChecked: 
                case ExpressionType.SubtractAssign:
                case ExpressionType.SubtractAssignChecked: 
                case ExpressionType.DivideAssign: 
                case ExpressionType.ModuloAssign:
                case ExpressionType.MultiplyAssign: 
                case ExpressionType.MultiplyAssignChecked:
                case ExpressionType.LeftShiftAssign:
                case ExpressionType.RightShiftAssign:
                case ExpressionType.AndAssign: 
                case ExpressionType.OrAssign:
                case ExpressionType.PowerAssign: 
                case ExpressionType.Coalesce: 
                    return 1;
 
                // Conditional (?:) would go here

                // Conditional OR
                case ExpressionType.OrElse: 
                    return 2;
 
                // Conditional AND 
                case ExpressionType.AndAlso:
                    return 3; 

                // Logical OR
                case ExpressionType.Or:
                    return 4; 

                // Logical XOR 
                case ExpressionType.ExclusiveOr: 
                    return 5;
 
                // Logical AND
                case ExpressionType.And:
                    return 6;
 
                // Equality
                case ExpressionType.Equal: 
                case ExpressionType.NotEqual: 
                    return 7;
 
                // Relational, type testing
                case ExpressionType.GreaterThan:
                case ExpressionType.LessThan:
                case ExpressionType.GreaterThanOrEqual: 
                case ExpressionType.LessThanOrEqual:
                case ExpressionType.TypeAs: 
                case ExpressionType.TypeIs: 
                case ExpressionType.TypeEqual:
                    return 8; 

                // Shift
                case ExpressionType.LeftShift:
                case ExpressionType.RightShift: 
                    return 9;
 
                // Additive 
                case ExpressionType.Add:
                case ExpressionType.AddChecked: 
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                    return 10;
 
                // Multiplicative
                case ExpressionType.Divide: 
                case ExpressionType.Modulo: 
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked: 
                    return 11;

                // Unary
                case ExpressionType.Negate: 
                case ExpressionType.NegateChecked:
                case ExpressionType.UnaryPlus: 
                case ExpressionType.Not: 
                case ExpressionType.Convert:
                case ExpressionType.ConvertChecked: 
                case ExpressionType.PreIncrementAssign:
                case ExpressionType.PreDecrementAssign:
                case ExpressionType.OnesComplement:
                case ExpressionType.Increment: 
                case ExpressionType.Decrement:
                case ExpressionType.IsTrue: 
                case ExpressionType.IsFalse: 
                case ExpressionType.Unbox:
                case ExpressionType.Throw: 
                    return 12;

                // Power, which is not in C#
                // But VB/Python/Ruby put it here, above unary. 
                case ExpressionType.Power:
                    return 13; 
 
                // Primary, which includes all other node types:
                //   member access, calls, indexing, new. 
                case ExpressionType.PostIncrementAssign:
                case ExpressionType.PostDecrementAssign:
                default:
                    return 14; 

                // These aren't expressions, so never need parentheses: 
                //   constants, variables 
                case ExpressionType.Constant:
                case ExpressionType.Parameter: 
                    return 15;
            }
        }
 
        private void ParenthesizedVisit(Expression parent, Expression nodeToVisit) {
            if (NeedsParentheses(parent, nodeToVisit)) { 
                Out("("); 
                Visit(nodeToVisit);
                Out(")"); 
            } else {
                Visit(nodeToVisit);
            }
        } 

        protected internal override Expression VisitMethodCall(MethodCallExpression node) { 
            Out(".Call "); 
            if (node.Object != null) {
                ParenthesizedVisit(node, node.Object); 
            } else if (node.Method.DeclaringType != null) {
                Out(node.Method.DeclaringType.ToString());
            } else {
                Out(""); 
            }
            Out("."); 
            Out(node.Method.Name); 
            VisitExpressions('(', node.Arguments);
            return node; 
        }

        protected internal override Expression VisitNewArray(NewArrayExpression node) {
            if (node.NodeType == ExpressionType.NewArrayBounds) { 
                // .NewArray MyType[expr1, expr2]
                Out(".NewArray " + node.Type.GetElementType().ToString()); 
                VisitExpressions('[', node.Expressions); 
            } else {
                // .NewArray MyType {expr1, expr2} 
                Out(".NewArray " + node.Type.ToString(), Flow.Space);
                VisitExpressions('{', node.Expressions);
            }
            return node; 
        }
 
        protected internal override Expression VisitNew(NewExpression node) { 
            Out(".New " + node.Type.ToString());
            VisitExpressions('(', node.Arguments); 
            return node;
        }

        protected override ElementInit VisitElementInit(ElementInit node) { 
            if (node.Arguments.Count == 1) {
                Visit(node.Arguments[0]); 
            } else { 
                VisitExpressions('{', node.Arguments);
            } 
            return node;
        }

        protected internal override Expression VisitListInit(ListInitExpression node) { 
            Visit(node.NewExpression);
            VisitExpressions('{', ',', node.Initializers, e => VisitElementInit(e)); 
            return node; 
        }
 
        protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment) {
            Out(assignment.Member.Name);
            Out(Flow.Space, "=", Flow.Space);
            Visit(assignment.Expression); 
            return assignment;
        } 
 
        protected override MemberListBinding VisitMemberListBinding(MemberListBinding binding) {
            Out(binding.Member.Name); 
            Out(Flow.Space, "=", Flow.Space);
            VisitExpressions('{', ',', binding.Initializers, e => VisitElementInit(e));
            return binding;
        } 

        protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding) { 
            Out(binding.Member.Name); 
            Out(Flow.Space, "=", Flow.Space);
            VisitExpressions('{', ',', binding.Bindings, e => VisitMemberBinding(e)); 
            return binding;
        }

        protected internal override Expression VisitMemberInit(MemberInitExpression node) { 
            Visit(node.NewExpression);
            VisitExpressions('{', ',', node.Bindings, e => VisitMemberBinding(e)); 
            return node; 
        }
 
        protected internal override Expression VisitTypeBinary(TypeBinaryExpression node) {
            ParenthesizedVisit(node, node.Expression);
            switch (node.NodeType) {
                case ExpressionType.TypeIs: 
                    Out(Flow.Space, ".Is", Flow.Space);
                    break; 
                case ExpressionType.TypeEqual: 
                    Out(Flow.Space, ".TypeEqual", Flow.Space);
                    break; 
            }
            Out(node.TypeOperand.ToString());
            return node;
        } 

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] 
        protected internal override Expression VisitUnary(UnaryExpression node) { 
            bool parenthesize = NeedsParentheses(node, node.Operand);
 
            switch (node.NodeType) {
                case ExpressionType.Convert:
                    Out("(" + node.Type.ToString() + ")");
                    break; 
                case ExpressionType.ConvertChecked:
                    Out("#(" + node.Type.ToString() + ")"); 
                    break; 
                case ExpressionType.TypeAs:
                    break; 
                case ExpressionType.Not:
                    Out(node.Type == typeof(bool) ? "!" : "~");
                    break;
                case ExpressionType.OnesComplement: 
                    Out("~");
                    break; 
                case ExpressionType.Negate: 
                    Out("-");
                    break; 
                case ExpressionType.NegateChecked:
                    Out("#-");
                    break;
                case ExpressionType.UnaryPlus: 
                    Out("+");
                    break; 
                case ExpressionType.ArrayLength: 
                    break;
                case ExpressionType.Quote: 
                    Out("'");
                    break;
                case ExpressionType.Throw:
                    if (node.Operand == null) { 
                        Out(".Rethrow");
                    } else { 
                        Out(".Throw", Flow.Space); 
                    }
                    break; 
                case ExpressionType.IsFalse:
                    Out(".IsFalse");
                    break;
                case ExpressionType.IsTrue: 
                    Out(".IsTrue");
                    break; 
                case ExpressionType.Decrement: 
                    Out(".Decrement");
                    break; 
                case ExpressionType.Increment:
                    Out(".Increment");
                    break;
                case ExpressionType.PreDecrementAssign: 
                    Out("--");
                    break; 
                case ExpressionType.PreIncrementAssign: 
                    Out("++");
                    break; 
                case ExpressionType.Unbox:
                    Out(".Unbox");
                    break;
            } 

            ParenthesizedVisit(node, node.Operand); 
 
            switch (node.NodeType) {
                case ExpressionType.TypeAs: 
                    Out(Flow.Space, ".As", Flow.Space | Flow.Break);
                    Out(node.Type.ToString());
                    break;
 
                case ExpressionType.ArrayLength:
                    Out(".Length"); 
                    break; 

                case ExpressionType.PostDecrementAssign: 
                    Out("--");
                    break;

                case ExpressionType.PostIncrementAssign: 
                    Out("++");
                    break; 
            } 
            return node;
        } 

        protected internal override Expression VisitBlock(BlockExpression node) {
            Out(".Block");
 
            // Display  if the type of the BlockExpression is different from the
            // last expression's type in the block. 
            if (node.Type != node.GetExpression(node.ExpressionCount - 1).Type) { 
                Out(String.Format(CultureInfo.CurrentCulture, "<{0}>", node.Type.ToString()));
            } 

            VisitDeclarations(node.Variables);
            Out(" ");
            // Use ; to separate expressions in the block 
            VisitExpressions('{', ';', node.Expressions);
 
            return node; 
        }
 
        protected internal override Expression VisitDefault(DefaultExpression node) {
            Out(".Default(" + node.Type.ToString() + ")");
            return node;
        } 

        protected internal override Expression VisitLabel(LabelExpression node) { 
            Out(".Label", Flow.NewLine); 
            Indent();
            Visit(node.DefaultValue); 
            Dedent();
            NewLine();
            DumpLabel(node.Target);
            return node; 
        }
 
        protected internal override Expression VisitGoto(GotoExpression node) { 
            Out("." + node.Kind.ToString(), Flow.Space);
            Out(GetLabelTargetName(node.Target), Flow.Space); 
            Out("{", Flow.Space);
            Visit(node.Value);
            Out(Flow.Space, "}");
            return node; 
        }
 
        protected internal override Expression VisitLoop(LoopExpression node) { 
            Out(".Loop", Flow.Space);
            if (node.ContinueLabel != null) { 
                DumpLabel(node.ContinueLabel);
            }
            Out(" {", Flow.NewLine);
            Indent(); 
            Visit(node.Body);
            Dedent(); 
            Out(Flow.NewLine, "}"); 
            if (node.BreakLabel != null) {
                Out("", Flow.NewLine); 
                DumpLabel(node.BreakLabel);
            }
            return node;
        } 

        protected override SwitchCase VisitSwitchCase(SwitchCase node) { 
            foreach (var test in node.TestValues) { 
                Out(".Case (");
                Visit(test); 
                Out("):", Flow.NewLine);
            }
            Indent(); Indent();
            Visit(node.Body); 
            Dedent(); Dedent();
            NewLine(); 
            return node; 
        }
 
        protected internal override Expression VisitSwitch(SwitchExpression node) {
            Out(".Switch ");
            Out("(");
            Visit(node.SwitchValue); 
            Out(") {", Flow.NewLine);
            Visit(node.Cases, VisitSwitchCase); 
            if (node.DefaultBody != null) { 
                Out(".Default:", Flow.NewLine);
                Indent(); Indent(); 
                Visit(node.DefaultBody);
                Dedent(); Dedent();
                NewLine();
            } 
            Out("}");
            return node; 
        } 

        protected override CatchBlock VisitCatchBlock(CatchBlock node) { 
            Out(Flow.NewLine, "} .Catch (" + node.Test.ToString());
            if (node.Variable != null) {
                Out(Flow.Space, "");
                VisitParameter(node.Variable); 
            }
            if (node.Filter != null) { 
                Out(") .If (", Flow.Break); 
                Visit(node.Filter);
            } 
            Out(") {", Flow.NewLine);
            Indent();
            Visit(node.Body);
            Dedent(); 
            return node;
        } 
 
        protected internal override Expression VisitTry(TryExpression node) {
            Out(".Try {", Flow.NewLine); 
            Indent();
            Visit(node.Body);
            Dedent();
            Visit(node.Handlers, VisitCatchBlock); 
            if (node.Finally != null) {
                Out(Flow.NewLine, "} .Finally {", Flow.NewLine); 
                Indent(); 
                Visit(node.Finally);
                Dedent(); 
            } else if (node.Fault != null) {
                Out(Flow.NewLine, "} .Fault {", Flow.NewLine);
                Indent();
                Visit(node.Fault); 
                Dedent();
            } 
 
            Out(Flow.NewLine, "}");
            return node; 
        }

        protected internal override Expression VisitIndex(IndexExpression node) {
            if (node.Indexer != null) { 
                OutMember(node, node.Object, node.Indexer);
            } else { 
                ParenthesizedVisit(node, node.Object); 
            }
 
            VisitExpressions('[', node.Arguments);
            return node;
        }
 
        protected internal override Expression VisitExtension(Expression node) {
            Out(String.Format(CultureInfo.CurrentCulture, ".Extension<{0}>", node.GetType().ToString())); 
 
            if (node.CanReduce) {
                Out(Flow.Space, "{", Flow.NewLine); 
                Indent();
                Visit(node.Reduce());
                Dedent();
                Out(Flow.NewLine, "}"); 
            }
 
            return node; 
        }
 
        protected internal override Expression VisitDebugInfo(DebugInfoExpression node) {
            Out(String.Format(
                CultureInfo.CurrentCulture,
                ".DebugInfo({0}: {1}, {2} - {3}, {4})", 
                node.Document.FileName,
                node.StartLine, 
                node.StartColumn, 
                node.EndLine,
                node.EndColumn) 
            );
            return node;
        }
 

        private void DumpLabel(LabelTarget target) { 
            Out(String.Format(CultureInfo.CurrentCulture, ".LabelTarget {0}:", GetLabelTargetName(target))); 
        }
 
        private string GetLabelTargetName(LabelTarget target) {
            if (string.IsNullOrEmpty(target.Name)) {
                // Create the label target name as #Label1, #Label2, etc.
                return String.Format(CultureInfo.CurrentCulture, "#Label{0}", GetLabelTargetId(target)); 
            } else {
                return GetDisplayName(target.Name); 
            } 
        }
 
        private void WriteLambda(LambdaExpression lambda) {
            Out(
                String.Format(
                    CultureInfo.CurrentCulture, 
                    ".Lambda {0}<{1}>",
                    GetLambdaName(lambda), 
                    lambda.Type.ToString()) 
            );
 
            VisitDeclarations(lambda.Parameters);

            Out(Flow.Space, "{", Flow.NewLine);
            Indent(); 
            Visit(lambda.Body);
            Dedent(); 
            Out(Flow.NewLine, "}"); 
            Debug.Assert(_stack.Count == 0);
        } 

        private string GetLambdaName(LambdaExpression lambda) {
            if (String.IsNullOrEmpty(lambda.Name)) {
                return "#Lambda" + GetLambdaId(lambda); 
            }
            return GetDisplayName(lambda.Name); 
        } 

        ///  
        /// Return true if the input string contains any whitespace character.
        /// Otherwise false.
        /// 
        private static bool ContainsWhiteSpace(string name) { 
            foreach (char c in name) {
                if (Char.IsWhiteSpace(c)) { 
                    return true; 
                }
            } 
            return false;
        }

        private static string QuoteName(string name) { 
            return String.Format(CultureInfo.CurrentCulture, "'{0}'", name);
        } 
 
        private static string GetDisplayName(string name) {
            if (ContainsWhiteSpace(name)) { 
                // if name has whitespaces in it, quote it
                return QuoteName(name);
            } else {
                return name; 
            }
        } 
 
        #endregion
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
/* **************************************************************************** 
 *
 * Copyright (c) Microsoft Corporation.
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 * 
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/ 

using System.Collections.Generic; 
using System.Diagnostics; 
using System.Dynamic;
using System.Dynamic.Utils; 
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices; 
using System.Collections.ObjectModel;
 
namespace System.Linq.Expressions { 
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
    internal sealed class DebugViewWriter : ExpressionVisitor { 
        [Flags]
        private enum Flow {
            None,
            Space, 
            NewLine,
 
            Break = 0x8000      // newline if column > MaxColumn 
        };
 
        private const int Tab = 4;
        private const int MaxColumn = 120;

        private TextWriter _out; 
        private int _column;
 
        private Stack _stack = new Stack(); 
        private int _delta;
        private Flow _flow; 

        // All the unique lambda expressions in the ET, will be used for displaying all
        // the lambda definitions.
        private Queue _lambdas; 

        // Associate every unique anonymous LambdaExpression in the tree with an integer. 
        // The id is used to create a name for the anonymous lambda. 
        //
        private Dictionary _lambdaIds; 

        // Associate every unique anonymous parameter or variable in the tree with an integer.
        // The id is used to create a name for the anonymous parameter or variable.
        // 
        private Dictionary _paramIds;
 
        // Associate every unique anonymous LabelTarget in the tree with an integer. 
        // The id is used to create a name for the anonymous LabelTarget.
        // 
        private Dictionary _labelIds;

        private DebugViewWriter(TextWriter file) {
            _out = file; 
        }
 
        private int Base { 
            get {
                return _stack.Count > 0 ? _stack.Peek() : 0; 
            }
        }

        private int Delta { 
            get { return _delta; }
        } 
 
        private int Depth {
            get { return Base + Delta; } 
        }

        private void Indent() {
            _delta += Tab; 
        }
        private void Dedent() { 
            _delta -= Tab; 
        }
 
        private void NewLine() {
            _flow = Flow.NewLine;
        }
 
        private static int GetId(T e, ref Dictionary ids) {
            if (ids == null) { 
                ids = new Dictionary(); 
                ids.Add(e, 1);
                return 1; 
            } else {
                int id;
                if (!ids.TryGetValue(e, out id)) {
                    // e is met the first time 
                    id = ids.Count + 1;
                    ids.Add(e, id); 
                } 
                return id;
            } 
        }

        private int GetLambdaId(LambdaExpression le) {
            Debug.Assert(String.IsNullOrEmpty(le.Name)); 
            return GetId(le, ref _lambdaIds);
        } 
 
        private int GetParamId(ParameterExpression p) {
            Debug.Assert(String.IsNullOrEmpty(p.Name)); 
            return GetId(p, ref _paramIds);
        }

        private int GetLabelTargetId(LabelTarget target) { 
            Debug.Assert(String.IsNullOrEmpty(target.Name));
            return GetId(target, ref _labelIds); 
        } 

        ///  
        /// Write out the given AST
        /// 
        internal static void WriteTo(Expression node, TextWriter writer) {
            Debug.Assert(node != null); 
            Debug.Assert(writer != null);
 
            new DebugViewWriter(writer).WriteTo(node); 
        }
 
        private void WriteTo(Expression node) {
            var lambda = node as LambdaExpression;
            if (lambda != null) {
                WriteLambda(lambda); 
            } else {
                Visit(node); 
                Debug.Assert(_stack.Count == 0); 
            }
 
            //
            // Output all lambda expression definitions.
            // in the order of their appearances in the tree.
            // 
            while (_lambdas != null && _lambdas.Count > 0) {
                WriteLine(); 
                WriteLine(); 
                WriteLambda(_lambdas.Dequeue());
            } 
        }

        #region The printing code
 
        private void Out(string s) {
            Out(Flow.None, s, Flow.None); 
        } 

        private void Out(Flow before, string s) { 
            Out(before, s, Flow.None);
        }

        private void Out(string s, Flow after) { 
            Out(Flow.None, s, after);
        } 
 
        private void Out(Flow before, string s, Flow after) {
            switch (GetFlow(before)) { 
                case Flow.None:
                    break;
                case Flow.Space:
                    Write(" "); 
                    break;
                case Flow.NewLine: 
                    WriteLine(); 
                    Write(new String(' ', Depth));
                    break; 
            }
            Write(s);
            _flow = after;
        } 

        private void WriteLine() { 
            _out.WriteLine(); 
            _column = 0;
        } 
        private void Write(string s) {
            _out.Write(s);
            _column += s.Length;
        } 

        private Flow GetFlow(Flow flow) { 
            Flow last; 

            last = CheckBreak(_flow); 
            flow = CheckBreak(flow);

            // Get the biggest flow that is requested None < Space < NewLine
            return (Flow)System.Math.Max((int)last, (int)flow); 
        }
 
        private Flow CheckBreak(Flow flow) { 
            if ((flow & Flow.Break) != 0) {
                if (_column > (MaxColumn + Depth)) { 
                    flow = Flow.NewLine;
                } else {
                    flow &= ~Flow.Break;
                } 
            }
            return flow; 
        } 

        #endregion 

        #region The AST Output

        // More proper would be to make this a virtual method on Action 
        private static string FormatBinder(CallSiteBinder binder) {
            ConvertBinder convert; 
            GetMemberBinder getMember; 
            SetMemberBinder setMember;
            DeleteMemberBinder deleteMember; 
            GetIndexBinder getIndex;
            SetIndexBinder setIndex;
            DeleteIndexBinder deleteIndex;
            InvokeMemberBinder call; 
            InvokeBinder invoke;
            CreateInstanceBinder create; 
            UnaryOperationBinder unary; 
            BinaryOperationBinder binary;
 
            if ((convert = binder as ConvertBinder) != null) {
                return "Convert " + convert.Type.ToString();
            } else if ((getMember = binder as GetMemberBinder) != null) {
                return "GetMember " + getMember.Name; 
            } else if ((setMember = binder as SetMemberBinder) != null) {
                return "SetMember " + setMember.Name; 
            } else if ((deleteMember = binder as DeleteMemberBinder) != null) { 
                return "DeleteMember " + deleteMember.Name;
            } else if ((getIndex = binder as GetIndexBinder) != null) { 
                return "GetIndex";
            } else if ((setIndex = binder as SetIndexBinder) != null) {
                return "SetIndex";
            } else if ((deleteIndex = binder as DeleteIndexBinder) != null) { 
                return "DeleteIndex";
            } else if ((call = binder as InvokeMemberBinder) != null) { 
                return "Call " + call.Name; 
            } else if ((invoke = binder as InvokeBinder) != null) {
                return "Invoke"; 
            } else if ((create = binder as CreateInstanceBinder) != null) {
                return "Create";
            } else if ((unary = binder as UnaryOperationBinder) != null) {
                return "UnaryOperation " + unary.Operation; 
            } else if ((binary = binder as BinaryOperationBinder) != null) {
                return "BinaryOperation " + binary.Operation; 
            } else { 
                return binder.ToString();
            } 
        }

        private void VisitExpressions(char open, IList expressions) where T : Expression {
            VisitExpressions(open, ',', expressions); 
        }
 
        private void VisitExpressions(char open, char separator, IList expressions) where T : Expression { 
            VisitExpressions(open, separator, expressions, e => Visit(e));
        } 

        private void VisitDeclarations(IList expressions) {
            VisitExpressions('(', ',', expressions, variable =>
            { 
                Out(variable.Type.ToString());
                if (variable.IsByRef) { 
                    Out("&"); 
                }
                Out(" "); 
                VisitParameter(variable);
            });
        }
 
        private void VisitExpressions(char open, char separator, IList expressions, Action visit) {
            Out(open.ToString()); 
 
            if (expressions != null) {
                Indent(); 
                bool isFirst = true;
                foreach (T e in expressions) {
                    if (isFirst) {
                        if (open == '{' || expressions.Count > 1) { 
                            NewLine();
                        } 
                        isFirst = false; 
                    } else {
                        Out(separator.ToString(), Flow.NewLine); 
                    }
                    visit(e);
                }
                Dedent(); 
            }
 
            char close; 
            switch (open) {
                case '(': close = ')'; break; 
                case '{': close = '}'; break;
                case '[': close = ']'; break;
                case '<': close = '>'; break;
                default: throw ContractUtils.Unreachable; 
            }
 
            if (open == '{') { 
                NewLine();
            } 
            Out(close.ToString(), Flow.Break);
        }

        protected internal override Expression VisitDynamic(DynamicExpression node) { 
            Out(".Dynamic", Flow.Space);
            Out(FormatBinder(node.Binder)); 
            VisitExpressions('(', node.Arguments); 
            return node;
        } 

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        protected internal override Expression VisitBinary(BinaryExpression node) {
            if (node.NodeType == ExpressionType.ArrayIndex) { 
                ParenthesizedVisit(node, node.Left);
                Out("["); 
                Visit(node.Right); 
                Out("]");
            } else { 
                bool parenthesizeLeft = NeedsParentheses(node, node.Left);
                bool parenthesizeRight = NeedsParentheses(node, node.Right);

                string op; 
                bool isChecked = false;
                Flow beforeOp = Flow.Space; 
                switch (node.NodeType) { 
                    case ExpressionType.Assign: op = "="; break;
                    case ExpressionType.Equal: op = "=="; break; 
                    case ExpressionType.NotEqual: op = "!="; break;
                    case ExpressionType.AndAlso: op = "&&"; beforeOp = Flow.Break | Flow.Space; break;
                    case ExpressionType.OrElse: op = "||"; beforeOp = Flow.Break | Flow.Space; break;
                    case ExpressionType.GreaterThan: op = ">"; break; 
                    case ExpressionType.LessThan: op = "<"; break;
                    case ExpressionType.GreaterThanOrEqual: op = ">="; break; 
                    case ExpressionType.LessThanOrEqual: op = "<="; break; 
                    case ExpressionType.Add: op = "+"; break;
                    case ExpressionType.AddAssign: op = "+="; break; 
                    case ExpressionType.AddAssignChecked: op = "+="; isChecked = true; break;
                    case ExpressionType.AddChecked: op = "+"; isChecked = true; break;
                    case ExpressionType.Subtract: op = "-"; break;
                    case ExpressionType.SubtractAssign: op = "-="; break; 
                    case ExpressionType.SubtractAssignChecked: op = "-="; isChecked = true; break;
                    case ExpressionType.SubtractChecked: op = "-"; isChecked = true; break; 
                    case ExpressionType.Divide: op = "/"; break; 
                    case ExpressionType.DivideAssign: op = "/="; break;
                    case ExpressionType.Modulo: op = "%"; break; 
                    case ExpressionType.ModuloAssign: op = "%="; break;
                    case ExpressionType.Multiply: op = "*"; break;
                    case ExpressionType.MultiplyAssign: op = "*="; break;
                    case ExpressionType.MultiplyAssignChecked: op = "*="; isChecked = true; break; 
                    case ExpressionType.MultiplyChecked: op = "*"; isChecked = true; break;
                    case ExpressionType.LeftShift: op = "<<"; break; 
                    case ExpressionType.LeftShiftAssign: op = "<<="; break; 
                    case ExpressionType.RightShift: op = ">>"; break;
                    case ExpressionType.RightShiftAssign: op = ">>="; break; 
                    case ExpressionType.And: op = "&"; break;
                    case ExpressionType.AndAssign: op = "&="; break;
                    case ExpressionType.Or: op = "|"; break;
                    case ExpressionType.OrAssign: op = "|="; break; 
                    case ExpressionType.ExclusiveOr: op = "^"; break;
                    case ExpressionType.ExclusiveOrAssign: op = "^="; break; 
                    case ExpressionType.Power: op = "**"; break; 
                    case ExpressionType.PowerAssign: op = "**="; break;
                    case ExpressionType.Coalesce: op = "??"; break; 

                    default:
                        throw new InvalidOperationException();
                } 

                if (parenthesizeLeft) { 
                    Out("(", Flow.None); 
                }
 
                Visit(node.Left);
                if (parenthesizeLeft) {
                    Out(Flow.None, ")", Flow.Break);
                } 

                // prepend # to the operator to represent checked op 
                if (isChecked) { 
                    op = String.Format(
                            CultureInfo.CurrentCulture, 
                            "#{0}",
                            op
                    );
                } 
                Out(beforeOp, op, Flow.Space | Flow.Break);
 
                if (parenthesizeRight) { 
                    Out("(", Flow.None);
                } 
                Visit(node.Right);
                if (parenthesizeRight) {
                    Out(Flow.None, ")", Flow.Break);
                } 
            }
            return node; 
        } 

        protected internal override Expression VisitParameter(ParameterExpression node) { 
            // Have '$' for the DebugView of ParameterExpressions
            Out("$");
            if (String.IsNullOrEmpty(node.Name)) {
                // If no name if provided, generate a name as $var1, $var2. 
                // No guarantee for not having name conflicts with user provided variable names.
                // 
                int id = GetParamId(node); 
                Out("var" + id);
            } else { 
                Out(GetDisplayName(node.Name));
            }
            return node;
        } 

        protected internal override Expression VisitLambda(Expression node) { 
            Out( 
                String.Format(CultureInfo.CurrentCulture,
                    "{0} {1}<{2}>", 
                    ".Lambda",
                    GetLambdaName(node),
                    node.Type.ToString()
                ) 
            );
 
            if (_lambdas == null) { 
                _lambdas = new Queue();
            } 

            // N^2 performance, for keeping the order of the lambdas.
            if (!_lambdas.Contains(node)) {
                _lambdas.Enqueue(node); 
            }
 
            return node; 
        }
 
        private static bool IsSimpleExpression(Expression node) {
            var binary = node as BinaryExpression;
            if (binary != null) {
                return !(binary.Left is BinaryExpression || binary.Right is BinaryExpression); 
            }
 
            return false; 
        }
 
        protected internal override Expression VisitConditional(ConditionalExpression node) {
            if (IsSimpleExpression(node.Test)) {
                Out(".If (");
                Visit(node.Test); 
                Out(") {", Flow.NewLine);
            } else { 
                Out(".If (", Flow.NewLine); 
                Indent();
                Visit(node.Test); 
                Dedent();
                Out(Flow.NewLine, ") {", Flow.NewLine);
            }
            Indent(); 
            Visit(node.IfTrue);
            Dedent(); 
            Out(Flow.NewLine, "} .Else {", Flow.NewLine); 
            Indent();
            Visit(node.IfFalse); 
            Dedent();
            Out(Flow.NewLine, "}");
            return node;
        } 

        protected internal override Expression VisitConstant(ConstantExpression node) { 
            object value = node.Value; 

            if (value == null) { 
                Out("null");
            } else if ((value is string) && node.Type == typeof(string)) {
                Out(String.Format(
                    CultureInfo.CurrentCulture, 
                    "\"{0}\"",
                    value)); 
            } else if ((value is char) && node.Type == typeof(char)) { 
                    Out(String.Format(
                        CultureInfo.CurrentCulture, 
                        "'{0}'",
                        value));
            } else if ((value is int) && node.Type == typeof(int)
                || (value is bool) && node.Type == typeof(bool)) { 
                Out(value.ToString());
            } else { 
                string suffix = GetConstantValueSuffix(node.Type); 
                if (suffix != null) {
                    Out(value.ToString()); 
                    Out(suffix);
                } else {
                    Out(String.Format(
                        CultureInfo.CurrentCulture, 
                        ".Constant<{0}>({1})",
                        node.Type.ToString(), 
                        value)); 
                }
            } 
            return node;
        }

        private static string GetConstantValueSuffix(Type type) { 
            if (type == typeof(UInt32)) {
                return "U"; 
            } 
            if (type == typeof(Int64)) {
                return "L"; 
            }
            if (type == typeof(UInt64)) {
                return "UL";
            } 
            if (type == typeof(Double)) {
                return "D"; 
            } 
            if (type == typeof(Single)) {
                return "F"; 
            }
            if (type == typeof(Decimal)) {
                return "M";
            } 
            return null;
        } 
 
        protected internal override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) {
            Out(".RuntimeVariables"); 
            VisitExpressions('(', node.Variables);
            return node;
        }
 
        // Prints ".instanceField" or "declaringType.staticField"
        private void OutMember(Expression node, Expression instance, MemberInfo member) { 
            if (instance != null) { 
                ParenthesizedVisit(node, instance);
                Out("." + member.Name); 
            } else {
                // For static members, include the type name
                Out(member.DeclaringType.ToString() + "." + member.Name);
            } 
        }
 
        protected internal override Expression VisitMember(MemberExpression node) { 
            OutMember(node, node.Expression, node.Member);
            return node; 
        }

        protected internal override Expression VisitInvocation(InvocationExpression node) {
            Out(".Invoke "); 
            ParenthesizedVisit(node, node.Expression);
            VisitExpressions('(', node.Arguments); 
            return node; 
        }
 
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        private static bool NeedsParentheses(Expression parent, Expression child) {
            Debug.Assert(parent != null);
            if (child == null) { 
                return false;
            } 
 
            // Some nodes always have parentheses because of how they are
            // displayed, for example: ".Unbox(obj.Foo)" 
            switch (parent.NodeType) {
                case ExpressionType.Increment:
                case ExpressionType.Decrement:
                case ExpressionType.IsTrue: 
                case ExpressionType.IsFalse:
                case ExpressionType.Unbox: 
                    return true; 
            }
 
            int childOpPrec = GetOperatorPrecedence(child);
            int parentOpPrec = GetOperatorPrecedence(parent);

            if (childOpPrec == parentOpPrec) { 
                // When parent op and child op has the same precedence,
                // we want to be a little conservative to have more clarity. 
                // Parentheses are not needed if 
                // 1) Both ops are &&, ||, &, |, or ^, all of them are the only
                // op that has the precedence. 
                // 2) Parent op is + or *, e.g. x + (y - z) can be simplified to
                // x + y - z.
                // 3) Parent op is -, / or %, and the child is the left operand.
                // In this case, if left and right operand are the same, we don't 
                // remove parenthesis, e.g. (x + y) - (x + y)
                // 
                switch (parent.NodeType) { 
                    case ExpressionType.AndAlso:
                    case ExpressionType.OrElse: 
                    case ExpressionType.And:
                    case ExpressionType.Or:
                    case ExpressionType.ExclusiveOr:
                        // Since these ops are the only ones on their precedence, 
                        // the child op must be the same.
                        Debug.Assert(child.NodeType == parent.NodeType); 
                        // We remove the parenthesis, e.g. x && y && z 
                        return false;
                    case ExpressionType.Add: 
                    case ExpressionType.AddChecked:
                    case ExpressionType.Multiply:
                    case ExpressionType.MultiplyChecked:
                        return false; 
                    case ExpressionType.Subtract:
                    case ExpressionType.SubtractChecked: 
                    case ExpressionType.Divide: 
                    case ExpressionType.Modulo:
                        BinaryExpression binary = parent as BinaryExpression; 
                        Debug.Assert(binary != null);
                        // Need to have parenthesis for the right operand.
                        return child == binary.Right;
                } 
                return true;
            } 
 
            // Special case: negate of a constant needs parentheses, to
            // disambiguate it from a negative constant. 
            if (child != null && child.NodeType == ExpressionType.Constant &&
                (parent.NodeType == ExpressionType.Negate || parent.NodeType == ExpressionType.NegateChecked)) {
                return true;
            } 

            // If the parent op has higher precedence, need parentheses for the child. 
            return childOpPrec < parentOpPrec; 
        }
 
        // the greater the higher
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
        private static int GetOperatorPrecedence(Expression node) {
 
            // Roughly matches C# operator precedence, with some additional
            // operators. Also things which are not binary/unary expressions, 
            // such as conditional and type testing, don't use this mechanism. 
            switch (node.NodeType) {
                // Assignment 
                case ExpressionType.Assign:
                case ExpressionType.ExclusiveOrAssign:
                case ExpressionType.AddAssign:
                case ExpressionType.AddAssignChecked: 
                case ExpressionType.SubtractAssign:
                case ExpressionType.SubtractAssignChecked: 
                case ExpressionType.DivideAssign: 
                case ExpressionType.ModuloAssign:
                case ExpressionType.MultiplyAssign: 
                case ExpressionType.MultiplyAssignChecked:
                case ExpressionType.LeftShiftAssign:
                case ExpressionType.RightShiftAssign:
                case ExpressionType.AndAssign: 
                case ExpressionType.OrAssign:
                case ExpressionType.PowerAssign: 
                case ExpressionType.Coalesce: 
                    return 1;
 
                // Conditional (?:) would go here

                // Conditional OR
                case ExpressionType.OrElse: 
                    return 2;
 
                // Conditional AND 
                case ExpressionType.AndAlso:
                    return 3; 

                // Logical OR
                case ExpressionType.Or:
                    return 4; 

                // Logical XOR 
                case ExpressionType.ExclusiveOr: 
                    return 5;
 
                // Logical AND
                case ExpressionType.And:
                    return 6;
 
                // Equality
                case ExpressionType.Equal: 
                case ExpressionType.NotEqual: 
                    return 7;
 
                // Relational, type testing
                case ExpressionType.GreaterThan:
                case ExpressionType.LessThan:
                case ExpressionType.GreaterThanOrEqual: 
                case ExpressionType.LessThanOrEqual:
                case ExpressionType.TypeAs: 
                case ExpressionType.TypeIs: 
                case ExpressionType.TypeEqual:
                    return 8; 

                // Shift
                case ExpressionType.LeftShift:
                case ExpressionType.RightShift: 
                    return 9;
 
                // Additive 
                case ExpressionType.Add:
                case ExpressionType.AddChecked: 
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                    return 10;
 
                // Multiplicative
                case ExpressionType.Divide: 
                case ExpressionType.Modulo: 
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked: 
                    return 11;

                // Unary
                case ExpressionType.Negate: 
                case ExpressionType.NegateChecked:
                case ExpressionType.UnaryPlus: 
                case ExpressionType.Not: 
                case ExpressionType.Convert:
                case ExpressionType.ConvertChecked: 
                case ExpressionType.PreIncrementAssign:
                case ExpressionType.PreDecrementAssign:
                case ExpressionType.OnesComplement:
                case ExpressionType.Increment: 
                case ExpressionType.Decrement:
                case ExpressionType.IsTrue: 
                case ExpressionType.IsFalse: 
                case ExpressionType.Unbox:
                case ExpressionType.Throw: 
                    return 12;

                // Power, which is not in C#
                // But VB/Python/Ruby put it here, above unary. 
                case ExpressionType.Power:
                    return 13; 
 
                // Primary, which includes all other node types:
                //   member access, calls, indexing, new. 
                case ExpressionType.PostIncrementAssign:
                case ExpressionType.PostDecrementAssign:
                default:
                    return 14; 

                // These aren't expressions, so never need parentheses: 
                //   constants, variables 
                case ExpressionType.Constant:
                case ExpressionType.Parameter: 
                    return 15;
            }
        }
 
        private void ParenthesizedVisit(Expression parent, Expression nodeToVisit) {
            if (NeedsParentheses(parent, nodeToVisit)) { 
                Out("("); 
                Visit(nodeToVisit);
                Out(")"); 
            } else {
                Visit(nodeToVisit);
            }
        } 

        protected internal override Expression VisitMethodCall(MethodCallExpression node) { 
            Out(".Call "); 
            if (node.Object != null) {
                ParenthesizedVisit(node, node.Object); 
            } else if (node.Method.DeclaringType != null) {
                Out(node.Method.DeclaringType.ToString());
            } else {
                Out(""); 
            }
            Out("."); 
            Out(node.Method.Name); 
            VisitExpressions('(', node.Arguments);
            return node; 
        }

        protected internal override Expression VisitNewArray(NewArrayExpression node) {
            if (node.NodeType == ExpressionType.NewArrayBounds) { 
                // .NewArray MyType[expr1, expr2]
                Out(".NewArray " + node.Type.GetElementType().ToString()); 
                VisitExpressions('[', node.Expressions); 
            } else {
                // .NewArray MyType {expr1, expr2} 
                Out(".NewArray " + node.Type.ToString(), Flow.Space);
                VisitExpressions('{', node.Expressions);
            }
            return node; 
        }
 
        protected internal override Expression VisitNew(NewExpression node) { 
            Out(".New " + node.Type.ToString());
            VisitExpressions('(', node.Arguments); 
            return node;
        }

        protected override ElementInit VisitElementInit(ElementInit node) { 
            if (node.Arguments.Count == 1) {
                Visit(node.Arguments[0]); 
            } else { 
                VisitExpressions('{', node.Arguments);
            } 
            return node;
        }

        protected internal override Expression VisitListInit(ListInitExpression node) { 
            Visit(node.NewExpression);
            VisitExpressions('{', ',', node.Initializers, e => VisitElementInit(e)); 
            return node; 
        }
 
        protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment) {
            Out(assignment.Member.Name);
            Out(Flow.Space, "=", Flow.Space);
            Visit(assignment.Expression); 
            return assignment;
        } 
 
        protected override MemberListBinding VisitMemberListBinding(MemberListBinding binding) {
            Out(binding.Member.Name); 
            Out(Flow.Space, "=", Flow.Space);
            VisitExpressions('{', ',', binding.Initializers, e => VisitElementInit(e));
            return binding;
        } 

        protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding) { 
            Out(binding.Member.Name); 
            Out(Flow.Space, "=", Flow.Space);
            VisitExpressions('{', ',', binding.Bindings, e => VisitMemberBinding(e)); 
            return binding;
        }

        protected internal override Expression VisitMemberInit(MemberInitExpression node) { 
            Visit(node.NewExpression);
            VisitExpressions('{', ',', node.Bindings, e => VisitMemberBinding(e)); 
            return node; 
        }
 
        protected internal override Expression VisitTypeBinary(TypeBinaryExpression node) {
            ParenthesizedVisit(node, node.Expression);
            switch (node.NodeType) {
                case ExpressionType.TypeIs: 
                    Out(Flow.Space, ".Is", Flow.Space);
                    break; 
                case ExpressionType.TypeEqual: 
                    Out(Flow.Space, ".TypeEqual", Flow.Space);
                    break; 
            }
            Out(node.TypeOperand.ToString());
            return node;
        } 

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] 
        protected internal override Expression VisitUnary(UnaryExpression node) { 
            bool parenthesize = NeedsParentheses(node, node.Operand);
 
            switch (node.NodeType) {
                case ExpressionType.Convert:
                    Out("(" + node.Type.ToString() + ")");
                    break; 
                case ExpressionType.ConvertChecked:
                    Out("#(" + node.Type.ToString() + ")"); 
                    break; 
                case ExpressionType.TypeAs:
                    break; 
                case ExpressionType.Not:
                    Out(node.Type == typeof(bool) ? "!" : "~");
                    break;
                case ExpressionType.OnesComplement: 
                    Out("~");
                    break; 
                case ExpressionType.Negate: 
                    Out("-");
                    break; 
                case ExpressionType.NegateChecked:
                    Out("#-");
                    break;
                case ExpressionType.UnaryPlus: 
                    Out("+");
                    break; 
                case ExpressionType.ArrayLength: 
                    break;
                case ExpressionType.Quote: 
                    Out("'");
                    break;
                case ExpressionType.Throw:
                    if (node.Operand == null) { 
                        Out(".Rethrow");
                    } else { 
                        Out(".Throw", Flow.Space); 
                    }
                    break; 
                case ExpressionType.IsFalse:
                    Out(".IsFalse");
                    break;
                case ExpressionType.IsTrue: 
                    Out(".IsTrue");
                    break; 
                case ExpressionType.Decrement: 
                    Out(".Decrement");
                    break; 
                case ExpressionType.Increment:
                    Out(".Increment");
                    break;
                case ExpressionType.PreDecrementAssign: 
                    Out("--");
                    break; 
                case ExpressionType.PreIncrementAssign: 
                    Out("++");
                    break; 
                case ExpressionType.Unbox:
                    Out(".Unbox");
                    break;
            } 

            ParenthesizedVisit(node, node.Operand); 
 
            switch (node.NodeType) {
                case ExpressionType.TypeAs: 
                    Out(Flow.Space, ".As", Flow.Space | Flow.Break);
                    Out(node.Type.ToString());
                    break;
 
                case ExpressionType.ArrayLength:
                    Out(".Length"); 
                    break; 

                case ExpressionType.PostDecrementAssign: 
                    Out("--");
                    break;

                case ExpressionType.PostIncrementAssign: 
                    Out("++");
                    break; 
            } 
            return node;
        } 

        protected internal override Expression VisitBlock(BlockExpression node) {
            Out(".Block");
 
            // Display  if the type of the BlockExpression is different from the
            // last expression's type in the block. 
            if (node.Type != node.GetExpression(node.ExpressionCount - 1).Type) { 
                Out(String.Format(CultureInfo.CurrentCulture, "<{0}>", node.Type.ToString()));
            } 

            VisitDeclarations(node.Variables);
            Out(" ");
            // Use ; to separate expressions in the block 
            VisitExpressions('{', ';', node.Expressions);
 
            return node; 
        }
 
        protected internal override Expression VisitDefault(DefaultExpression node) {
            Out(".Default(" + node.Type.ToString() + ")");
            return node;
        } 

        protected internal override Expression VisitLabel(LabelExpression node) { 
            Out(".Label", Flow.NewLine); 
            Indent();
            Visit(node.DefaultValue); 
            Dedent();
            NewLine();
            DumpLabel(node.Target);
            return node; 
        }
 
        protected internal override Expression VisitGoto(GotoExpression node) { 
            Out("." + node.Kind.ToString(), Flow.Space);
            Out(GetLabelTargetName(node.Target), Flow.Space); 
            Out("{", Flow.Space);
            Visit(node.Value);
            Out(Flow.Space, "}");
            return node; 
        }
 
        protected internal override Expression VisitLoop(LoopExpression node) { 
            Out(".Loop", Flow.Space);
            if (node.ContinueLabel != null) { 
                DumpLabel(node.ContinueLabel);
            }
            Out(" {", Flow.NewLine);
            Indent(); 
            Visit(node.Body);
            Dedent(); 
            Out(Flow.NewLine, "}"); 
            if (node.BreakLabel != null) {
                Out("", Flow.NewLine); 
                DumpLabel(node.BreakLabel);
            }
            return node;
        } 

        protected override SwitchCase VisitSwitchCase(SwitchCase node) { 
            foreach (var test in node.TestValues) { 
                Out(".Case (");
                Visit(test); 
                Out("):", Flow.NewLine);
            }
            Indent(); Indent();
            Visit(node.Body); 
            Dedent(); Dedent();
            NewLine(); 
            return node; 
        }
 
        protected internal override Expression VisitSwitch(SwitchExpression node) {
            Out(".Switch ");
            Out("(");
            Visit(node.SwitchValue); 
            Out(") {", Flow.NewLine);
            Visit(node.Cases, VisitSwitchCase); 
            if (node.DefaultBody != null) { 
                Out(".Default:", Flow.NewLine);
                Indent(); Indent(); 
                Visit(node.DefaultBody);
                Dedent(); Dedent();
                NewLine();
            } 
            Out("}");
            return node; 
        } 

        protected override CatchBlock VisitCatchBlock(CatchBlock node) { 
            Out(Flow.NewLine, "} .Catch (" + node.Test.ToString());
            if (node.Variable != null) {
                Out(Flow.Space, "");
                VisitParameter(node.Variable); 
            }
            if (node.Filter != null) { 
                Out(") .If (", Flow.Break); 
                Visit(node.Filter);
            } 
            Out(") {", Flow.NewLine);
            Indent();
            Visit(node.Body);
            Dedent(); 
            return node;
        } 
 
        protected internal override Expression VisitTry(TryExpression node) {
            Out(".Try {", Flow.NewLine); 
            Indent();
            Visit(node.Body);
            Dedent();
            Visit(node.Handlers, VisitCatchBlock); 
            if (node.Finally != null) {
                Out(Flow.NewLine, "} .Finally {", Flow.NewLine); 
                Indent(); 
                Visit(node.Finally);
                Dedent(); 
            } else if (node.Fault != null) {
                Out(Flow.NewLine, "} .Fault {", Flow.NewLine);
                Indent();
                Visit(node.Fault); 
                Dedent();
            } 
 
            Out(Flow.NewLine, "}");
            return node; 
        }

        protected internal override Expression VisitIndex(IndexExpression node) {
            if (node.Indexer != null) { 
                OutMember(node, node.Object, node.Indexer);
            } else { 
                ParenthesizedVisit(node, node.Object); 
            }
 
            VisitExpressions('[', node.Arguments);
            return node;
        }
 
        protected internal override Expression VisitExtension(Expression node) {
            Out(String.Format(CultureInfo.CurrentCulture, ".Extension<{0}>", node.GetType().ToString())); 
 
            if (node.CanReduce) {
                Out(Flow.Space, "{", Flow.NewLine); 
                Indent();
                Visit(node.Reduce());
                Dedent();
                Out(Flow.NewLine, "}"); 
            }
 
            return node; 
        }
 
        protected internal override Expression VisitDebugInfo(DebugInfoExpression node) {
            Out(String.Format(
                CultureInfo.CurrentCulture,
                ".DebugInfo({0}: {1}, {2} - {3}, {4})", 
                node.Document.FileName,
                node.StartLine, 
                node.StartColumn, 
                node.EndLine,
                node.EndColumn) 
            );
            return node;
        }
 

        private void DumpLabel(LabelTarget target) { 
            Out(String.Format(CultureInfo.CurrentCulture, ".LabelTarget {0}:", GetLabelTargetName(target))); 
        }
 
        private string GetLabelTargetName(LabelTarget target) {
            if (string.IsNullOrEmpty(target.Name)) {
                // Create the label target name as #Label1, #Label2, etc.
                return String.Format(CultureInfo.CurrentCulture, "#Label{0}", GetLabelTargetId(target)); 
            } else {
                return GetDisplayName(target.Name); 
            } 
        }
 
        private void WriteLambda(LambdaExpression lambda) {
            Out(
                String.Format(
                    CultureInfo.CurrentCulture, 
                    ".Lambda {0}<{1}>",
                    GetLambdaName(lambda), 
                    lambda.Type.ToString()) 
            );
 
            VisitDeclarations(lambda.Parameters);

            Out(Flow.Space, "{", Flow.NewLine);
            Indent(); 
            Visit(lambda.Body);
            Dedent(); 
            Out(Flow.NewLine, "}"); 
            Debug.Assert(_stack.Count == 0);
        } 

        private string GetLambdaName(LambdaExpression lambda) {
            if (String.IsNullOrEmpty(lambda.Name)) {
                return "#Lambda" + GetLambdaId(lambda); 
            }
            return GetDisplayName(lambda.Name); 
        } 

        ///  
        /// Return true if the input string contains any whitespace character.
        /// Otherwise false.
        /// 
        private static bool ContainsWhiteSpace(string name) { 
            foreach (char c in name) {
                if (Char.IsWhiteSpace(c)) { 
                    return true; 
                }
            } 
            return false;
        }

        private static string QuoteName(string name) { 
            return String.Format(CultureInfo.CurrentCulture, "'{0}'", name);
        } 
 
        private static string GetDisplayName(string name) {
            if (ContainsWhiteSpace(name)) { 
                // if name has whitespaces in it, quote it
                return QuoteName(name);
            } else {
                return name; 
            }
        } 
 
        #endregion
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK