DebugViewWriter.cs :  » Script » IronRuby » Microsoft » Scripting » Ast » C# / CSharp Open Source

Home
C# / CSharp Open Source
1.2.6.4 mono .net core
2.2.6.4 mono core
3.Aspect Oriented Frameworks
4.Bloggers
5.Build Systems
6.Business Application
7.Charting Reporting Tools
8.Chat Servers
9.Code Coverage Tools
10.Content Management Systems CMS
11.CRM ERP
12.Database
13.Development
14.Email
15.Forum
16.Game
17.GIS
18.GUI
19.IDEs
20.Installers Generators
21.Inversion of Control Dependency Injection
22.Issue Tracking
23.Logging Tools
24.Message
25.Mobile
26.Network Clients
27.Network Servers
28.Office
29.PDF
30.Persistence Frameworks
31.Portals
32.Profilers
33.Project Management
34.RSS RDF
35.Rule Engines
36.Script
37.Search Engines
38.Sound Audio
39.Source Control
40.SQL Clients
41.Template Engines
42.Testing
43.UML
44.Web Frameworks
45.Web Service
46.Web Testing
47.Wiki Engines
48.Windows Presentation Foundation
49.Workflows
50.XML Parsers
C# / C Sharp
C# / C Sharp by API
C# / CSharp Tutorial
C# / CSharp Open Source » Script » IronRuby 
IronRuby » Microsoft » Scripting » Ast » 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;
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;

#if CLR2
namespace Microsoft.Scripting.Ast{
#else
namespace System.Linq.Expressions {
#endif
    [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<int> _stack = new Stack<int>();
        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<LambdaExpression> _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<LambdaExpression, int> _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<ParameterExpression, int> _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<LabelTarget, int> _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>(T e, ref Dictionary<T, int> ids) {
            if (ids == null) {
                ids = new Dictionary<T, int>();
                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);
        }

        /// <summary>
        /// Write out the given AST
        /// </summary>
        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<T>(char open, IList<T> expressions) where T : Expression {
            VisitExpressions<T>(open, ',', expressions);
        }

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

        private void VisitDeclarations(IList<ParameterExpression> expressions) {
            VisitExpressions('(', ',', expressions, variable =>
            {
                Out(variable.Type.ToString());
                if (variable.IsByRef) {
                    Out("&");
                }
                Out(" ");
                VisitParameter(variable);
            });
        }

        private void VisitExpressions<T>(char open, char separator, IList<T> expressions, Action<T> 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<T>(Expression<T> node) {
            Out(
                String.Format(CultureInfo.CurrentCulture,
                    "{0} {1}<{2}>",
                    ".Lambda",
                    GetLambdaName(node),
                    node.Type.ToString()
                )
            );

            if (_lambdas == null) {
                _lambdas = new Queue<LambdaExpression>();
            }

            // 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("<UnknownType>");
            }
            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 <type> 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);
        }

        /// <summary>
        /// Return true if the input string contains any whitespace character.
        /// Otherwise false.
        /// </summary>
        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
    }
}
www.java2v.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.