1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
6 * copy of the license can be found in the License.html file at the root of this distribution. If
7 * you cannot locate the Apache License, Version 2.0, please send an email to
8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
9 * by the terms of the Apache License, Version 2.0.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
17 using System.Collections.Generic;
18 using System.Diagnostics;
20 using System.Dynamic.Utils;
21 using System.Globalization;
23 using System.Reflection;
24 using System.Runtime.CompilerServices;
25 using System.Collections.ObjectModel;
28 namespace Microsoft.Scripting.Ast {
30 namespace System.Linq.Expressions {
32 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
33 internal sealed class DebugViewWriter : ExpressionVisitor {
40 Break = 0x8000 // newline if column > MaxColumn
43 private const int Tab = 4;
44 private const int MaxColumn = 120;
46 private TextWriter _out;
49 private Stack<int> _stack = new Stack<int>();
53 // All the unique lambda expressions in the ET, will be used for displaying all
54 // the lambda definitions.
55 private Queue<LambdaExpression> _lambdas;
57 // Associate every unique anonymous LambdaExpression in the tree with an integer.
58 // The id is used to create a name for the anonymous lambda.
60 private Dictionary<LambdaExpression, int> _lambdaIds;
62 // Associate every unique anonymous parameter or variable in the tree with an integer.
63 // The id is used to create a name for the anonymous parameter or variable.
65 private Dictionary<ParameterExpression, int> _paramIds;
67 // Associate every unique anonymous LabelTarget in the tree with an integer.
68 // The id is used to create a name for the anonymous LabelTarget.
70 private Dictionary<LabelTarget, int> _labelIds;
72 private DebugViewWriter(TextWriter file) {
78 return _stack.Count > 0 ? _stack.Peek() : 0;
83 get { return _delta; }
87 get { return Base + Delta; }
90 private void Indent() {
93 private void Dedent() {
97 private void NewLine() {
101 private static int GetId<T>(T e, ref Dictionary<T, int> ids) {
103 ids = new Dictionary<T, int>();
108 if (!ids.TryGetValue(e, out id)) {
109 // e is met the first time
117 private int GetLambdaId(LambdaExpression le) {
118 Debug.Assert(String.IsNullOrEmpty(le.Name));
119 return GetId(le, ref _lambdaIds);
122 private int GetParamId(ParameterExpression p) {
123 Debug.Assert(String.IsNullOrEmpty(p.Name));
124 return GetId(p, ref _paramIds);
127 private int GetLabelTargetId(LabelTarget target) {
128 Debug.Assert(String.IsNullOrEmpty(target.Name));
129 return GetId(target, ref _labelIds);
133 /// Write out the given AST
135 internal static void WriteTo(Expression node, TextWriter writer) {
136 Debug.Assert(node != null);
137 Debug.Assert(writer != null);
139 new DebugViewWriter(writer).WriteTo(node);
142 private void WriteTo(Expression node) {
143 var lambda = node as LambdaExpression;
144 if (lambda != null) {
148 Debug.Assert(_stack.Count == 0);
152 // Output all lambda expression definitions.
153 // in the order of their appearances in the tree.
155 while (_lambdas != null && _lambdas.Count > 0) {
158 WriteLambda(_lambdas.Dequeue());
162 #region The printing code
164 private void Out(string s) {
165 Out(Flow.None, s, Flow.None);
168 private void Out(Flow before, string s) {
169 Out(before, s, Flow.None);
172 private void Out(string s, Flow after) {
173 Out(Flow.None, s, after);
176 private void Out(Flow before, string s, Flow after) {
177 switch (GetFlow(before)) {
185 Write(new String(' ', Depth));
192 private void WriteLine() {
196 private void Write(string s) {
201 private Flow GetFlow(Flow flow) {
204 last = CheckBreak(_flow);
205 flow = CheckBreak(flow);
207 // Get the biggest flow that is requested None < Space < NewLine
208 return (Flow)System.Math.Max((int)last, (int)flow);
211 private Flow CheckBreak(Flow flow) {
212 if ((flow & Flow.Break) != 0) {
213 if (_column > (MaxColumn + Depth)) {
224 #region The AST Output
226 // More proper would be to make this a virtual method on Action
227 private static string FormatBinder(CallSiteBinder binder) {
228 ConvertBinder convert;
229 GetMemberBinder getMember;
230 SetMemberBinder setMember;
231 DeleteMemberBinder deleteMember;
232 InvokeMemberBinder call;
233 UnaryOperationBinder unary;
234 BinaryOperationBinder binary;
236 if ((convert = binder as ConvertBinder) != null) {
237 return "Convert " + convert.Type.ToString();
238 } else if ((getMember = binder as GetMemberBinder) != null) {
239 return "GetMember " + getMember.Name;
240 } else if ((setMember = binder as SetMemberBinder) != null) {
241 return "SetMember " + setMember.Name;
242 } else if ((deleteMember = binder as DeleteMemberBinder) != null) {
243 return "DeleteMember " + deleteMember.Name;
244 } else if (binder is GetIndexBinder) {
246 } else if (binder is SetIndexBinder) {
248 } else if (binder is DeleteIndexBinder) {
249 return "DeleteIndex";
250 } else if ((call = binder as InvokeMemberBinder) != null) {
251 return "Call " + call.Name;
252 } else if (binder is InvokeBinder) {
254 } else if (binder is CreateInstanceBinder) {
256 } else if ((unary = binder as UnaryOperationBinder) != null) {
257 return "UnaryOperation " + unary.Operation;
258 } else if ((binary = binder as BinaryOperationBinder) != null) {
259 return "BinaryOperation " + binary.Operation;
261 return binder.ToString();
265 private void VisitExpressions<T>(char open, IList<T> expressions) where T : Expression {
266 VisitExpressions<T>(open, ',', expressions);
269 private void VisitExpressions<T>(char open, char separator, IList<T> expressions) where T : Expression {
270 VisitExpressions(open, separator, expressions, e => Visit(e));
273 private void VisitDeclarations(IList<ParameterExpression> expressions) {
274 VisitExpressions('(', ',', expressions, variable =>
276 Out(variable.Type.ToString());
277 if (variable.IsByRef) {
281 VisitParameter(variable);
285 private void VisitExpressions<T>(char open, char separator, IList<T> expressions, Action<T> visit) {
286 Out(open.ToString());
288 if (expressions != null) {
291 foreach (T e in expressions) {
293 if (open == '{' || expressions.Count > 1) {
298 Out(separator.ToString(), Flow.NewLine);
307 case '(': close = ')'; break;
308 case '{': close = '}'; break;
309 case '[': close = ']'; break;
310 case '<': close = '>'; break;
311 default: throw ContractUtils.Unreachable;
317 Out(close.ToString(), Flow.Break);
320 protected internal override Expression VisitDynamic(DynamicExpression node) {
321 Out(".Dynamic", Flow.Space);
322 Out(FormatBinder(node.Binder));
323 VisitExpressions('(', node.Arguments);
327 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
328 protected internal override Expression VisitBinary(BinaryExpression node) {
329 if (node.NodeType == ExpressionType.ArrayIndex) {
330 ParenthesizedVisit(node, node.Left);
335 bool parenthesizeLeft = NeedsParentheses(node, node.Left);
336 bool parenthesizeRight = NeedsParentheses(node, node.Right);
339 bool isChecked = false;
340 Flow beforeOp = Flow.Space;
341 switch (node.NodeType) {
342 case ExpressionType.Assign: op = "="; break;
343 case ExpressionType.Equal: op = "=="; break;
344 case ExpressionType.NotEqual: op = "!="; break;
345 case ExpressionType.AndAlso: op = "&&"; beforeOp = Flow.Break | Flow.Space; break;
346 case ExpressionType.OrElse: op = "||"; beforeOp = Flow.Break | Flow.Space; break;
347 case ExpressionType.GreaterThan: op = ">"; break;
348 case ExpressionType.LessThan: op = "<"; break;
349 case ExpressionType.GreaterThanOrEqual: op = ">="; break;
350 case ExpressionType.LessThanOrEqual: op = "<="; break;
351 case ExpressionType.Add: op = "+"; break;
352 case ExpressionType.AddAssign: op = "+="; break;
353 case ExpressionType.AddAssignChecked: op = "+="; isChecked = true; break;
354 case ExpressionType.AddChecked: op = "+"; isChecked = true; break;
355 case ExpressionType.Subtract: op = "-"; break;
356 case ExpressionType.SubtractAssign: op = "-="; break;
357 case ExpressionType.SubtractAssignChecked: op = "-="; isChecked = true; break;
358 case ExpressionType.SubtractChecked: op = "-"; isChecked = true; break;
359 case ExpressionType.Divide: op = "/"; break;
360 case ExpressionType.DivideAssign: op = "/="; break;
361 case ExpressionType.Modulo: op = "%"; break;
362 case ExpressionType.ModuloAssign: op = "%="; break;
363 case ExpressionType.Multiply: op = "*"; break;
364 case ExpressionType.MultiplyAssign: op = "*="; break;
365 case ExpressionType.MultiplyAssignChecked: op = "*="; isChecked = true; break;
366 case ExpressionType.MultiplyChecked: op = "*"; isChecked = true; break;
367 case ExpressionType.LeftShift: op = "<<"; break;
368 case ExpressionType.LeftShiftAssign: op = "<<="; break;
369 case ExpressionType.RightShift: op = ">>"; break;
370 case ExpressionType.RightShiftAssign: op = ">>="; break;
371 case ExpressionType.And: op = "&"; break;
372 case ExpressionType.AndAssign: op = "&="; break;
373 case ExpressionType.Or: op = "|"; break;
374 case ExpressionType.OrAssign: op = "|="; break;
375 case ExpressionType.ExclusiveOr: op = "^"; break;
376 case ExpressionType.ExclusiveOrAssign: op = "^="; break;
377 case ExpressionType.Power: op = "**"; break;
378 case ExpressionType.PowerAssign: op = "**="; break;
379 case ExpressionType.Coalesce: op = "??"; break;
382 throw new InvalidOperationException();
385 if (parenthesizeLeft) {
390 if (parenthesizeLeft) {
391 Out(Flow.None, ")", Flow.Break);
394 // prepend # to the operator to represent checked op
397 CultureInfo.CurrentCulture,
402 Out(beforeOp, op, Flow.Space | Flow.Break);
404 if (parenthesizeRight) {
408 if (parenthesizeRight) {
409 Out(Flow.None, ")", Flow.Break);
415 protected internal override Expression VisitParameter(ParameterExpression node) {
416 // Have '$' for the DebugView of ParameterExpressions
418 if (String.IsNullOrEmpty(node.Name)) {
419 // If no name if provided, generate a name as $var1, $var2.
420 // No guarantee for not having name conflicts with user provided variable names.
422 int id = GetParamId(node);
425 Out(GetDisplayName(node.Name));
430 protected internal override Expression VisitLambda<T>(Expression<T> node) {
432 String.Format(CultureInfo.CurrentCulture,
440 if (_lambdas == null) {
441 _lambdas = new Queue<LambdaExpression>();
444 // N^2 performance, for keeping the order of the lambdas.
445 if (!_lambdas.Contains(node)) {
446 _lambdas.Enqueue(node);
452 private static bool IsSimpleExpression(Expression node) {
453 var binary = node as BinaryExpression;
454 if (binary != null) {
455 return !(binary.Left is BinaryExpression || binary.Right is BinaryExpression);
461 protected internal override Expression VisitConditional(ConditionalExpression node) {
462 if (IsSimpleExpression(node.Test)) {
465 Out(") {", Flow.NewLine);
467 Out(".If (", Flow.NewLine);
471 Out(Flow.NewLine, ") {", Flow.NewLine);
476 Out(Flow.NewLine, "} .Else {", Flow.NewLine);
480 Out(Flow.NewLine, "}");
484 protected internal override Expression VisitConstant(ConstantExpression node) {
485 object value = node.Value;
489 } else if ((value is string) && node.Type == typeof(string)) {
491 CultureInfo.CurrentCulture,
494 } else if ((value is char) && node.Type == typeof(char)) {
496 CultureInfo.CurrentCulture,
499 } else if ((value is int) && node.Type == typeof(int)
500 || (value is bool) && node.Type == typeof(bool)) {
501 Out(value.ToString());
503 string suffix = GetConstantValueSuffix(node.Type);
504 if (suffix != null) {
505 Out(value.ToString());
509 CultureInfo.CurrentCulture,
510 ".Constant<{0}>({1})",
511 node.Type.ToString(),
518 private static string GetConstantValueSuffix(Type type) {
519 if (type == typeof(UInt32)) {
522 if (type == typeof(Int64)) {
525 if (type == typeof(UInt64)) {
528 if (type == typeof(Double)) {
531 if (type == typeof(Single)) {
534 if (type == typeof(Decimal)) {
540 protected internal override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) {
541 Out(".RuntimeVariables");
542 VisitExpressions('(', node.Variables);
546 // Prints ".instanceField" or "declaringType.staticField"
547 private void OutMember(Expression node, Expression instance, MemberInfo member) {
548 if (instance != null) {
549 ParenthesizedVisit(node, instance);
550 Out("." + member.Name);
552 // For static members, include the type name
553 Out(member.DeclaringType.ToString() + "." + member.Name);
557 protected internal override Expression VisitMember(MemberExpression node) {
558 OutMember(node, node.Expression, node.Member);
562 protected internal override Expression VisitInvocation(InvocationExpression node) {
564 ParenthesizedVisit(node, node.Expression);
565 VisitExpressions('(', node.Arguments);
569 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
570 private static bool NeedsParentheses(Expression parent, Expression child) {
571 Debug.Assert(parent != null);
576 // Some nodes always have parentheses because of how they are
577 // displayed, for example: ".Unbox(obj.Foo)"
578 switch (parent.NodeType) {
579 case ExpressionType.Increment:
580 case ExpressionType.Decrement:
581 case ExpressionType.IsTrue:
582 case ExpressionType.IsFalse:
583 case ExpressionType.Unbox:
587 int childOpPrec = GetOperatorPrecedence(child);
588 int parentOpPrec = GetOperatorPrecedence(parent);
590 if (childOpPrec == parentOpPrec) {
591 // When parent op and child op has the same precedence,
592 // we want to be a little conservative to have more clarity.
593 // Parentheses are not needed if
594 // 1) Both ops are &&, ||, &, |, or ^, all of them are the only
595 // op that has the precedence.
596 // 2) Parent op is + or *, e.g. x + (y - z) can be simplified to
598 // 3) Parent op is -, / or %, and the child is the left operand.
599 // In this case, if left and right operand are the same, we don't
600 // remove parenthesis, e.g. (x + y) - (x + y)
602 switch (parent.NodeType) {
603 case ExpressionType.AndAlso:
604 case ExpressionType.OrElse:
605 case ExpressionType.And:
606 case ExpressionType.Or:
607 case ExpressionType.ExclusiveOr:
608 // Since these ops are the only ones on their precedence,
609 // the child op must be the same.
610 Debug.Assert(child.NodeType == parent.NodeType);
611 // We remove the parenthesis, e.g. x && y && z
613 case ExpressionType.Add:
614 case ExpressionType.AddChecked:
615 case ExpressionType.Multiply:
616 case ExpressionType.MultiplyChecked:
618 case ExpressionType.Subtract:
619 case ExpressionType.SubtractChecked:
620 case ExpressionType.Divide:
621 case ExpressionType.Modulo:
622 BinaryExpression binary = parent as BinaryExpression;
623 Debug.Assert(binary != null);
624 // Need to have parenthesis for the right operand.
625 return child == binary.Right;
630 // Special case: negate of a constant needs parentheses, to
631 // disambiguate it from a negative constant.
632 if (child != null && child.NodeType == ExpressionType.Constant &&
633 (parent.NodeType == ExpressionType.Negate || parent.NodeType == ExpressionType.NegateChecked)) {
637 // If the parent op has higher precedence, need parentheses for the child.
638 return childOpPrec < parentOpPrec;
641 // the greater the higher
642 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
643 private static int GetOperatorPrecedence(Expression node) {
645 // Roughly matches C# operator precedence, with some additional
646 // operators. Also things which are not binary/unary expressions,
647 // such as conditional and type testing, don't use this mechanism.
648 switch (node.NodeType) {
650 case ExpressionType.Assign:
651 case ExpressionType.ExclusiveOrAssign:
652 case ExpressionType.AddAssign:
653 case ExpressionType.AddAssignChecked:
654 case ExpressionType.SubtractAssign:
655 case ExpressionType.SubtractAssignChecked:
656 case ExpressionType.DivideAssign:
657 case ExpressionType.ModuloAssign:
658 case ExpressionType.MultiplyAssign:
659 case ExpressionType.MultiplyAssignChecked:
660 case ExpressionType.LeftShiftAssign:
661 case ExpressionType.RightShiftAssign:
662 case ExpressionType.AndAssign:
663 case ExpressionType.OrAssign:
664 case ExpressionType.PowerAssign:
665 case ExpressionType.Coalesce:
668 // Conditional (?:) would go here
671 case ExpressionType.OrElse:
675 case ExpressionType.AndAlso:
679 case ExpressionType.Or:
683 case ExpressionType.ExclusiveOr:
687 case ExpressionType.And:
691 case ExpressionType.Equal:
692 case ExpressionType.NotEqual:
695 // Relational, type testing
696 case ExpressionType.GreaterThan:
697 case ExpressionType.LessThan:
698 case ExpressionType.GreaterThanOrEqual:
699 case ExpressionType.LessThanOrEqual:
700 case ExpressionType.TypeAs:
701 case ExpressionType.TypeIs:
702 case ExpressionType.TypeEqual:
706 case ExpressionType.LeftShift:
707 case ExpressionType.RightShift:
711 case ExpressionType.Add:
712 case ExpressionType.AddChecked:
713 case ExpressionType.Subtract:
714 case ExpressionType.SubtractChecked:
718 case ExpressionType.Divide:
719 case ExpressionType.Modulo:
720 case ExpressionType.Multiply:
721 case ExpressionType.MultiplyChecked:
725 case ExpressionType.Negate:
726 case ExpressionType.NegateChecked:
727 case ExpressionType.UnaryPlus:
728 case ExpressionType.Not:
729 case ExpressionType.Convert:
730 case ExpressionType.ConvertChecked:
731 case ExpressionType.PreIncrementAssign:
732 case ExpressionType.PreDecrementAssign:
733 case ExpressionType.OnesComplement:
734 case ExpressionType.Increment:
735 case ExpressionType.Decrement:
736 case ExpressionType.IsTrue:
737 case ExpressionType.IsFalse:
738 case ExpressionType.Unbox:
739 case ExpressionType.Throw:
742 // Power, which is not in C#
743 // But VB/Python/Ruby put it here, above unary.
744 case ExpressionType.Power:
747 // Primary, which includes all other node types:
748 // member access, calls, indexing, new.
749 case ExpressionType.PostIncrementAssign:
750 case ExpressionType.PostDecrementAssign:
754 // These aren't expressions, so never need parentheses:
755 // constants, variables
756 case ExpressionType.Constant:
757 case ExpressionType.Parameter:
762 private void ParenthesizedVisit(Expression parent, Expression nodeToVisit) {
763 if (NeedsParentheses(parent, nodeToVisit)) {
772 protected internal override Expression VisitMethodCall(MethodCallExpression node) {
774 if (node.Object != null) {
775 ParenthesizedVisit(node, node.Object);
776 } else if (node.Method.DeclaringType != null) {
777 Out(node.Method.DeclaringType.ToString());
779 Out("<UnknownType>");
782 Out(node.Method.Name);
783 VisitExpressions('(', node.Arguments);
787 protected internal override Expression VisitNewArray(NewArrayExpression node) {
788 if (node.NodeType == ExpressionType.NewArrayBounds) {
789 // .NewArray MyType[expr1, expr2]
790 Out(".NewArray " + node.Type.GetElementType().ToString());
791 VisitExpressions('[', node.Expressions);
793 // .NewArray MyType {expr1, expr2}
794 Out(".NewArray " + node.Type.ToString(), Flow.Space);
795 VisitExpressions('{', node.Expressions);
800 protected internal override Expression VisitNew(NewExpression node) {
801 Out(".New " + node.Type.ToString());
802 VisitExpressions('(', node.Arguments);
806 protected override ElementInit VisitElementInit(ElementInit node) {
807 if (node.Arguments.Count == 1) {
808 Visit(node.Arguments[0]);
810 VisitExpressions('{', node.Arguments);
815 protected internal override Expression VisitListInit(ListInitExpression node) {
816 Visit(node.NewExpression);
817 VisitExpressions('{', ',', node.Initializers, e => VisitElementInit(e));
821 protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment) {
822 Out(assignment.Member.Name);
823 Out(Flow.Space, "=", Flow.Space);
824 Visit(assignment.Expression);
828 protected override MemberListBinding VisitMemberListBinding(MemberListBinding binding) {
829 Out(binding.Member.Name);
830 Out(Flow.Space, "=", Flow.Space);
831 VisitExpressions('{', ',', binding.Initializers, e => VisitElementInit(e));
835 protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding) {
836 Out(binding.Member.Name);
837 Out(Flow.Space, "=", Flow.Space);
838 VisitExpressions('{', ',', binding.Bindings, e => VisitMemberBinding(e));
842 protected internal override Expression VisitMemberInit(MemberInitExpression node) {
843 Visit(node.NewExpression);
844 VisitExpressions('{', ',', node.Bindings, e => VisitMemberBinding(e));
848 protected internal override Expression VisitTypeBinary(TypeBinaryExpression node) {
849 ParenthesizedVisit(node, node.Expression);
850 switch (node.NodeType) {
851 case ExpressionType.TypeIs:
852 Out(Flow.Space, ".Is", Flow.Space);
854 case ExpressionType.TypeEqual:
855 Out(Flow.Space, ".TypeEqual", Flow.Space);
858 Out(node.TypeOperand.ToString());
862 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
863 protected internal override Expression VisitUnary(UnaryExpression node) {
864 switch (node.NodeType) {
865 case ExpressionType.Convert:
866 Out("(" + node.Type.ToString() + ")");
868 case ExpressionType.ConvertChecked:
869 Out("#(" + node.Type.ToString() + ")");
871 case ExpressionType.TypeAs:
873 case ExpressionType.Not:
874 Out(node.Type == typeof(bool) ? "!" : "~");
876 case ExpressionType.OnesComplement:
879 case ExpressionType.Negate:
882 case ExpressionType.NegateChecked:
885 case ExpressionType.UnaryPlus:
888 case ExpressionType.ArrayLength:
890 case ExpressionType.Quote:
893 case ExpressionType.Throw:
894 if (node.Operand == null) {
897 Out(".Throw", Flow.Space);
900 case ExpressionType.IsFalse:
903 case ExpressionType.IsTrue:
906 case ExpressionType.Decrement:
909 case ExpressionType.Increment:
912 case ExpressionType.PreDecrementAssign:
915 case ExpressionType.PreIncrementAssign:
918 case ExpressionType.Unbox:
923 ParenthesizedVisit(node, node.Operand);
925 switch (node.NodeType) {
926 case ExpressionType.TypeAs:
927 Out(Flow.Space, ".As", Flow.Space | Flow.Break);
928 Out(node.Type.ToString());
931 case ExpressionType.ArrayLength:
935 case ExpressionType.PostDecrementAssign:
939 case ExpressionType.PostIncrementAssign:
946 protected internal override Expression VisitBlock(BlockExpression node) {
949 // Display <type> if the type of the BlockExpression is different from the
950 // last expression's type in the block.
951 if (node.Type != node.GetExpression(node.ExpressionCount - 1).Type) {
952 Out(String.Format(CultureInfo.CurrentCulture, "<{0}>", node.Type.ToString()));
955 VisitDeclarations(node.Variables);
957 // Use ; to separate expressions in the block
958 VisitExpressions('{', ';', node.Expressions);
963 protected internal override Expression VisitDefault(DefaultExpression node) {
964 Out(".Default(" + node.Type.ToString() + ")");
968 protected internal override Expression VisitLabel(LabelExpression node) {
969 Out(".Label", Flow.NewLine);
971 Visit(node.DefaultValue);
974 DumpLabel(node.Target);
978 protected internal override Expression VisitGoto(GotoExpression node) {
979 Out("." + node.Kind.ToString(), Flow.Space);
980 Out(GetLabelTargetName(node.Target), Flow.Space);
981 Out("{", Flow.Space);
983 Out(Flow.Space, "}");
987 protected internal override Expression VisitLoop(LoopExpression node) {
988 Out(".Loop", Flow.Space);
989 if (node.ContinueLabel != null) {
990 DumpLabel(node.ContinueLabel);
992 Out(" {", Flow.NewLine);
996 Out(Flow.NewLine, "}");
997 if (node.BreakLabel != null) {
998 Out("", Flow.NewLine);
999 DumpLabel(node.BreakLabel);
1004 protected override SwitchCase VisitSwitchCase(SwitchCase node) {
1005 foreach (var test in node.TestValues) {
1008 Out("):", Flow.NewLine);
1017 protected internal override Expression VisitSwitch(SwitchExpression node) {
1020 Visit(node.SwitchValue);
1021 Out(") {", Flow.NewLine);
1022 Visit(node.Cases, VisitSwitchCase);
1023 if (node.DefaultBody != null) {
1024 Out(".Default:", Flow.NewLine);
1026 Visit(node.DefaultBody);
1034 protected override CatchBlock VisitCatchBlock(CatchBlock node) {
1035 Out(Flow.NewLine, "} .Catch (" + node.Test.ToString());
1036 if (node.Variable != null) {
1037 Out(Flow.Space, "");
1038 VisitParameter(node.Variable);
1040 if (node.Filter != null) {
1041 Out(") .If (", Flow.Break);
1044 Out(") {", Flow.NewLine);
1051 protected internal override Expression VisitTry(TryExpression node) {
1052 Out(".Try {", Flow.NewLine);
1056 Visit(node.Handlers, VisitCatchBlock);
1057 if (node.Finally != null) {
1058 Out(Flow.NewLine, "} .Finally {", Flow.NewLine);
1060 Visit(node.Finally);
1062 } else if (node.Fault != null) {
1063 Out(Flow.NewLine, "} .Fault {", Flow.NewLine);
1069 Out(Flow.NewLine, "}");
1073 protected internal override Expression VisitIndex(IndexExpression node) {
1074 if (node.Indexer != null) {
1075 OutMember(node, node.Object, node.Indexer);
1077 ParenthesizedVisit(node, node.Object);
1080 VisitExpressions('[', node.Arguments);
1084 protected internal override Expression VisitExtension(Expression node) {
1085 Out(String.Format(CultureInfo.CurrentCulture, ".Extension<{0}>", node.GetType().ToString()));
1087 if (node.CanReduce) {
1088 Out(Flow.Space, "{", Flow.NewLine);
1090 Visit(node.Reduce());
1092 Out(Flow.NewLine, "}");
1098 protected internal override Expression VisitDebugInfo(DebugInfoExpression node) {
1100 CultureInfo.CurrentCulture,
1101 ".DebugInfo({0}: {1}, {2} - {3}, {4})",
1102 node.Document.FileName,
1112 private void DumpLabel(LabelTarget target) {
1113 Out(String.Format(CultureInfo.CurrentCulture, ".LabelTarget {0}:", GetLabelTargetName(target)));
1116 private string GetLabelTargetName(LabelTarget target) {
1117 if (string.IsNullOrEmpty(target.Name)) {
1118 // Create the label target name as #Label1, #Label2, etc.
1119 return String.Format(CultureInfo.CurrentCulture, "#Label{0}", GetLabelTargetId(target));
1121 return GetDisplayName(target.Name);
1125 private void WriteLambda(LambdaExpression lambda) {
1128 CultureInfo.CurrentCulture,
1130 GetLambdaName(lambda),
1131 lambda.Type.ToString())
1134 VisitDeclarations(lambda.Parameters);
1136 Out(Flow.Space, "{", Flow.NewLine);
1140 Out(Flow.NewLine, "}");
1141 Debug.Assert(_stack.Count == 0);
1144 private string GetLambdaName(LambdaExpression lambda) {
1145 if (String.IsNullOrEmpty(lambda.Name)) {
1146 return "#Lambda" + GetLambdaId(lambda);
1148 return GetDisplayName(lambda.Name);
1152 /// Return true if the input string contains any whitespace character.
1153 /// Otherwise false.
1155 private static bool ContainsWhiteSpace(string name) {
1156 foreach (char c in name) {
1157 if (Char.IsWhiteSpace(c)) {
1164 private static string QuoteName(string name) {
1165 return String.Format(CultureInfo.CurrentCulture, "'{0}'", name);
1168 private static string GetDisplayName(string name) {
1169 if (ContainsWhiteSpace(name)) {
1170 // if name has whitespaces in it, quote it
1171 return QuoteName(name);