MegaBites-AI/Windows-powershell
0372
1/* ****************************************************************************2 *3 * Copyright (c) Microsoft Corporation.4 *5 * This source code is subject to terms and conditions of the Microsoft Public License. A6 * copy of the license can be found in the License.html file at the root of this distribution. If7 * you cannot locate the Microsoft Public License, please send an email to8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound9 * by the terms of the Microsoft Public License.10 *11 * You must not remove this notice, or any other, from this software.12 *13 *14 * ***************************************************************************/15 16#if ENABLE_BINDER_DEBUG_LOGGING17 18using System.Linq.Expressions;19using System.Collections.Generic;20using System.IO;21using System.Runtime.CompilerServices;22using System.Reflection;23using System.Globalization;24using System.Diagnostics;25using System.Dynamic;26 27namespace System.Management.Automation.Language {28 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]29 internal sealed class DebugViewWriter : DynamicExpressionVisitor {30 [Flags]31 private enum Flow {32 None,33 Space,34 NewLine,35 36 Break = 0x8000 // newline if column > MaxColumn37 };38 39 private const int Tab = 4;40 private const int MaxColumn = 120;41 42 private TextWriter _out;43 private int _column;44 45 private Stack<int> _stack = new Stack<int>();46 private int _delta;47 private Flow _flow;48 49 // All the unique lambda expressions in the ET, will be used for displaying all50 // the lambda definitions.51 private Queue<LambdaExpression> _lambdas;52 53 // Associate every unique anonymous LambdaExpression in the tree with an integer.54 // The id is used to create a name for the anonymous lambda.55 //56 private Dictionary<LambdaExpression, int> _lambdaIds;57 58 // Associate every unique anonymous parameter or variable in the tree with an integer.59 // The id is used to create a name for the anonymous parameter or variable.60 //61 private Dictionary<ParameterExpression, int> _paramIds;62 63 // Associate every unique anonymous LabelTarget in the tree with an integer.64 // The id is used to create a name for the anonymous LabelTarget.65 //66 private Dictionary<LabelTarget, int> _labelIds;67 68 private DebugViewWriter(TextWriter file) {69 _out = file;70 }71 72 private int Base {73 get {74 return _stack.Count > 0 ? _stack.Peek() : 0;75 }76 }77 78 private int Delta {79 get { return _delta; }80 }81 82 private int Depth {83 get { return Base + Delta; }84 }85 86 private void Indent() {87 _delta += Tab;88 }89 90 private void Dedent() {91 _delta -= Tab;92 }93 94 private void NewLine() {95 _flow = Flow.NewLine;96 }97 98 private static int GetId<T>(T e, ref Dictionary<T, int> ids) {99 if (ids == null) {100 ids = new Dictionary<T, int>();101 ids.Add(e, 1);102 return 1;103 } else {104 int id;105 if (!ids.TryGetValue(e, out id)) {106 // e is met the first time107 id = ids.Count + 1;108 ids.Add(e, id);109 }110 111 return id;112 }113 }114 115 private int GetLambdaId(LambdaExpression le) {116 Debug.Assert(string.IsNullOrEmpty(le.Name));117 return GetId(le, ref _lambdaIds);118 }119 120 private int GetParamId(ParameterExpression p) {121 Debug.Assert(string.IsNullOrEmpty(p.Name));122 return GetId(p, ref _paramIds);123 }124 125 private int GetLabelTargetId(LabelTarget target) {126 Debug.Assert(string.IsNullOrEmpty(target.Name));127 return GetId(target, ref _labelIds);128 }129 130 /// <summary>131 /// Write out the given AST.132 /// </summary>133 internal static void WriteTo(Expression node, TextWriter writer) {134 Debug.Assert(node != null);135 Debug.Assert(writer != null);136 137 new DebugViewWriter(writer).WriteTo(node);138 }139 140 private void WriteTo(Expression node) {141 var lambda = node as LambdaExpression;142 if (lambda != null) {143 WriteLambda(lambda);144 } else {145 Visit(node);146 Debug.Assert(_stack.Count == 0);147 }148 149 //150 // Output all lambda expression definitions.151 // in the order of their appearances in the tree.152 //153 while (_lambdas != null && _lambdas.Count > 0) {154 WriteLine();155 WriteLine();156 WriteLambda(_lambdas.Dequeue());157 }158 }159 160#region The printing code161 162 private void Out(string s) {163 Out(Flow.None, s, Flow.None);164 }165 166 private void Out(Flow before, string s) {167 Out(before, s, Flow.None);168 }169 170 private void Out(string s, Flow after) {171 Out(Flow.None, s, after);172 }173 174 private void Out(Flow before, string s, Flow after) {175 switch (GetFlow(before)) {176 case Flow.None:177 break;178 case Flow.Space:179 Write(" ");180 break;181 case Flow.NewLine:182 WriteLine();183 Write(new string(' ', Depth));184 break;185 }186 187 Write(s);188 _flow = after;189 }190 191 private void WriteLine() {192 _out.WriteLine();193 _column = 0;194 }195 196 private void Write(string s) {197 _out.Write(s);198 _column += s.Length;199 }200 201 private Flow GetFlow(Flow flow) {202 Flow last;203 204 last = CheckBreak(_flow);205 flow = CheckBreak(flow);206 207 // Get the biggest flow that is requested None < Space < NewLine208 return (Flow)System.Math.Max((int)last, (int)flow);209 }210 211 private Flow CheckBreak(Flow flow) {212 if ((flow & Flow.Break) != 0) {213 if (_column > (MaxColumn + Depth)) {214 flow = Flow.NewLine;215 } else {216 flow &= ~Flow.Break;217 }218 }219 220 return flow;221 }222 223#endregion224 225#region The AST Output226 227 // More proper would be to make this a virtual method on Action228 private static string FormatBinder(CallSiteBinder binder) {229 ConvertBinder convert;230 GetMemberBinder getMember;231 SetMemberBinder setMember;232 DeleteMemberBinder deleteMember;233 GetIndexBinder getIndex;234 SetIndexBinder setIndex;235 DeleteIndexBinder deleteIndex;236 InvokeMemberBinder call;237 InvokeBinder invoke;238 CreateInstanceBinder create;239 UnaryOperationBinder unary;240 BinaryOperationBinder binary;241 242 if ((convert = binder as ConvertBinder) != null) {243 return "Convert " + convert.Type.ToString();244 } else if ((getMember = binder as GetMemberBinder) != null) {245 return "GetMember " + getMember.Name;246 } else if ((setMember = binder as SetMemberBinder) != null) {247 return "SetMember " + setMember.Name;248 } else if ((deleteMember = binder as DeleteMemberBinder) != null) {249 return "DeleteMember " + deleteMember.Name;250 } else if ((getIndex = binder as GetIndexBinder) != null) {251 return "GetIndex";252 } else if ((setIndex = binder as SetIndexBinder) != null) {253 return "SetIndex";254 } else if ((deleteIndex = binder as DeleteIndexBinder) != null) {255 return "DeleteIndex";256 } else if ((call = binder as InvokeMemberBinder) != null) {257 return "Call " + call.Name;258 } else if ((invoke = binder as InvokeBinder) != null) {259 return "Invoke";260 } else if ((create = binder as CreateInstanceBinder) != null) {261 return "Create";262 } else if ((unary = binder as UnaryOperationBinder) != null) {263 return "UnaryOperation " + unary.Operation;264 } else if ((binary = binder as BinaryOperationBinder) != null) {265 return "BinaryOperation " + binary.Operation;266 } else {267 return binder.ToString();268 }269 }270 271 private void VisitExpressions<T>(char open, IList<T> expressions) where T : Expression {272 VisitExpressions<T>(open, ',', expressions);273 }274 275 private void VisitExpressions<T>(char open, char separator, IList<T> expressions) where T : Expression {276 VisitExpressions(open, separator, expressions, e => Visit(e));277 }278 279 private void VisitDeclarations(IList<ParameterExpression> expressions) {280 VisitExpressions('(', ',', expressions, variable =>281 {282 Out(variable.Type.ToString());283 if (variable.IsByRef) {284 Out("&");285 }286 287 Out(" ");288 VisitParameter(variable);289 });290 }291 292 private void VisitExpressions<T>(char open, char separator, IList<T> expressions, Action<T> visit) {293 Out(open.ToString());294 295 if (expressions != null) {296 Indent();297 bool isFirst = true;298 foreach (T e in expressions) {299 if (isFirst) {300 if (open == '{' || expressions.Count > 1) {301 NewLine();302 }303 304 isFirst = false;305 } else {306 Out(separator.ToString(), Flow.NewLine);307 }308 309 visit(e);310 }311 312 Dedent();313 }314 315 char close;316 switch (open) {317 case '(': close = ')'; break;318 case '{': close = '}'; break;319 case '[': close = ']'; break;320 case '<': close = '>'; break;321 default:322 close = ' ';323 Diagnostics.Assert(false, "Unexpected open brace.");324 break;325 }326 327 if (open == '{') {328 NewLine();329 }330 331 Out(close.ToString(), Flow.Break);332 }333 334 protected override Expression VisitDynamic(DynamicExpression node) {335 Out(".Dynamic", Flow.Space);336 Out(FormatBinder(node.Binder));337 VisitExpressions('(', node.Arguments);338 return node;339 }340 341 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]342 protected override Expression VisitBinary(BinaryExpression node) {343 if (node.NodeType == ExpressionType.ArrayIndex) {344 ParenthesizedVisit(node, node.Left);345 Out("[");346 Visit(node.Right);347 Out("]");348 } else {349 bool parenthesizeLeft = NeedsParentheses(node, node.Left);350 bool parenthesizeRight = NeedsParentheses(node, node.Right);351 352 string op;353 bool isChecked = false;354 Flow beforeOp = Flow.Space;355 switch (node.NodeType) {356 case ExpressionType.Assign: op = "="; break;357 case ExpressionType.Equal: op = "=="; break;358 case ExpressionType.NotEqual: op = "!="; break;359 case ExpressionType.AndAlso: op = "&&"; beforeOp = Flow.Break | Flow.Space; break;360 case ExpressionType.OrElse: op = "||"; beforeOp = Flow.Break | Flow.Space; break;361 case ExpressionType.GreaterThan: op = ">"; break;362 case ExpressionType.LessThan: op = "<"; break;363 case ExpressionType.GreaterThanOrEqual: op = ">="; break;364 case ExpressionType.LessThanOrEqual: op = "<="; break;365 case ExpressionType.Add: op = "+"; break;366 case ExpressionType.AddAssign: op = "+="; break;367 case ExpressionType.AddAssignChecked: op = "+="; isChecked = true; break;368 case ExpressionType.AddChecked: op = "+"; isChecked = true; break;369 case ExpressionType.Subtract: op = "-"; break;370 case ExpressionType.SubtractAssign: op = "-="; break;371 case ExpressionType.SubtractAssignChecked: op = "-="; isChecked = true; break;372 case ExpressionType.SubtractChecked: op = "-"; isChecked = true; break;373 case ExpressionType.Divide: op = "/"; break;374 case ExpressionType.DivideAssign: op = "/="; break;375 case ExpressionType.Modulo: op = "%"; break;376 case ExpressionType.ModuloAssign: op = "%="; break;377 case ExpressionType.Multiply: op = "*"; break;378 case ExpressionType.MultiplyAssign: op = "*="; break;379 case ExpressionType.MultiplyAssignChecked: op = "*="; isChecked = true; break;380 case ExpressionType.MultiplyChecked: op = "*"; isChecked = true; break;381 case ExpressionType.LeftShift: op = "<<"; break;382 case ExpressionType.LeftShiftAssign: op = "<<="; break;383 case ExpressionType.RightShift: op = ">>"; break;384 case ExpressionType.RightShiftAssign: op = ">>="; break;385 case ExpressionType.And: op = "&"; break;386 case ExpressionType.AndAssign: op = "&="; break;387 case ExpressionType.Or: op = "|"; break;388 case ExpressionType.OrAssign: op = "|="; break;389 case ExpressionType.ExclusiveOr: op = "^"; break;390 case ExpressionType.ExclusiveOrAssign: op = "^="; break;391 case ExpressionType.Power: op = "**"; break;392 case ExpressionType.PowerAssign: op = "**="; break;393 case ExpressionType.Coalesce: op = "??"; break;394 395 default:396 throw new InvalidOperationException();397 }398 399 if (parenthesizeLeft) {400 Out("(", Flow.None);401 }402 403 Visit(node.Left);404 if (parenthesizeLeft) {405 Out(Flow.None, ")", Flow.Break);406 }407 408 // prepend # to the operator to represent checked op409 if (isChecked) {410 op = string.Format(411 CultureInfo.CurrentCulture,412 "#{0}",413 op414 );415 }416 417 Out(beforeOp, op, Flow.Space | Flow.Break);418 419 if (parenthesizeRight) {420 Out("(", Flow.None);421 }422 423 Visit(node.Right);424 if (parenthesizeRight) {425 Out(Flow.None, ")", Flow.Break);426 }427 }428 429 return node;430 }431 432 protected override Expression VisitParameter(ParameterExpression node) {433 // Have '$' for the DebugView of ParameterExpressions434 Out("$");435 if (string.IsNullOrEmpty(node.Name)) {436 // If no name if provided, generate a name as $var1, $var2.437 // No guarantee for not having name conflicts with user provided variable names.438 //439 int id = GetParamId(node);440 Out("var" + id);441 } else {442 Out(GetDisplayName(node.Name));443 }444 445 return node;446 }447 448 protected override Expression VisitLambda<T>(Expression<T> node) {449 Out(450 string.Format(CultureInfo.CurrentCulture,451 "{0} {1}<{2}>",452 ".Lambda",453 GetLambdaName(node),454 node.Type.ToString()455 )456 );457 458 if (_lambdas == null) {459 _lambdas = new Queue<LambdaExpression>();460 }461 462 // N^2 performance, for keeping the order of the lambdas.463 if (!_lambdas.Contains(node)) {464 _lambdas.Enqueue(node);465 }466 467 return node;468 }469 470 private static bool IsSimpleExpression(Expression node) {471 var binary = node as BinaryExpression;472 if (binary != null) {473 return binary.Left is not BinaryExpression && binary.Right is not BinaryExpression;474 }475 476 return false;477 }478 479 protected override Expression VisitConditional(ConditionalExpression node) {480 if (IsSimpleExpression(node.Test)) {481 Out(".If (");482 Visit(node.Test);483 Out(") {", Flow.NewLine);484 } else {485 Out(".If (", Flow.NewLine);486 Indent();487 Visit(node.Test);488 Dedent();489 Out(Flow.NewLine, ") {", Flow.NewLine);490 }491 492 Indent();493 Visit(node.IfTrue);494 Dedent();495 Out(Flow.NewLine, "} .Else {", Flow.NewLine);496 Indent();497 Visit(node.IfFalse);498 Dedent();499 Out(Flow.NewLine, "}");500 return node;501 }502 503 protected override Expression VisitConstant(ConstantExpression node) {504 object value = node.Value;505 506 if (value == null) {507 Out("null");508 } else if ((value is string) && node.Type == typeof(string)) {509 Out(string.Format(510 CultureInfo.CurrentCulture,511 "\"{0}\"",512 value));513 } else if ((value is char) && node.Type == typeof(char)) {514 Out(string.Format(515 CultureInfo.CurrentCulture,516 "'{0}'",517 value));518 } else if ((value is int) && node.Type == typeof(int)519 || (value is bool) && node.Type == typeof(bool)) {520 Out(value.ToString());521 } else {522 string suffix = GetConstantValueSuffix(node.Type);523 if (suffix != null) {524 Out(value.ToString());525 Out(suffix);526 } else {527 Out(string.Format(528 CultureInfo.CurrentCulture,529 ".Constant<{0}>({1})",530 node.Type.ToString(),531 value));532 }533 }534 535 return node;536 }537 538 private static string GetConstantValueSuffix(Type type) {539 if (type == typeof(UInt32)) {540 return "U";541 }542 543 if (type == typeof(Int64)) {544 return "L";545 }546 547 if (type == typeof(UInt64)) {548 return "UL";549 }550 551 if (type == typeof(double)) {552 return "D";553 }554 555 if (type == typeof(Single)) {556 return "F";557 }558 559 if (type == typeof(decimal)) {560 return "M";561 }562 563 return null;564 }565 566 protected override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) {567 Out(".RuntimeVariables");568 VisitExpressions('(', node.Variables);569 return node;570 }571 572 // Prints ".instanceField" or "declaringType.staticField"573 private void OutMember(Expression node, Expression instance, MemberInfo member) {574 if (instance != null) {575 ParenthesizedVisit(node, instance);576 Out("." + member.Name);577 } else {578 // For static members, include the type name579 Out(member.DeclaringType.ToString() + "." + member.Name);580 }581 }582 583 protected override Expression VisitMember(MemberExpression node) {584 OutMember(node, node.Expression, node.Member);585 return node;586 }587 588 protected override Expression VisitInvocation(InvocationExpression node) {589 Out(".Invoke ");590 ParenthesizedVisit(node, node.Expression);591 VisitExpressions('(', node.Arguments);592 return node;593 }594 595 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]596 private static bool NeedsParentheses(Expression parent, Expression child) {597 Debug.Assert(parent != null);598 if (child == null) {599 return false;600 }601 602 // Some nodes always have parentheses because of how they are603 // displayed, for example: ".Unbox(obj.Foo)"604 switch (parent.NodeType) {605 case ExpressionType.Increment:606 case ExpressionType.Decrement:607 case ExpressionType.IsTrue:608 case ExpressionType.IsFalse:609 case ExpressionType.Unbox:610 return true;611 }612 613 int childOpPrec = GetOperatorPrecedence(child);614 int parentOpPrec = GetOperatorPrecedence(parent);615 616 if (childOpPrec == parentOpPrec) {617 // When parent op and child op has the same precedence,618 // we want to be a little conservative to have more clarity.619 // Parentheses are not needed if620 // 1) Both ops are &&, ||, &, |, or ^, all of them are the only621 // op that has the precedence.622 // 2) Parent op is + or *, e.g. x + (y - z) can be simplified to623 // x + y - z.624 // 3) Parent op is -, / or %, and the child is the left operand.625 // In this case, if left and right operand are the same, we don't626 // remove parenthesis, e.g. (x + y) - (x + y)627 //628 switch (parent.NodeType) {629 case ExpressionType.AndAlso:630 case ExpressionType.OrElse:631 case ExpressionType.And:632 case ExpressionType.Or:633 case ExpressionType.ExclusiveOr:634 // Since these ops are the only ones on their precedence,635 // the child op must be the same.636 Debug.Assert(child.NodeType == parent.NodeType);637 // We remove the parenthesis, e.g. x && y && z638 return false;639 case ExpressionType.Add:640 case ExpressionType.AddChecked:641 case ExpressionType.Multiply:642 case ExpressionType.MultiplyChecked:643 return false;644 case ExpressionType.Subtract:645 case ExpressionType.SubtractChecked:646 case ExpressionType.Divide:647 case ExpressionType.Modulo:648 BinaryExpression binary = parent as BinaryExpression;649 Debug.Assert(binary != null);650 // Need to have parenthesis for the right operand.651 return child == binary.Right;652 }653 654 return true;655 }656 657 // Special case: negate of a constant needs parentheses, to658 // disambiguate it from a negative constant.659 if (child != null && child.NodeType == ExpressionType.Constant &&660 (parent.NodeType == ExpressionType.Negate || parent.NodeType == ExpressionType.NegateChecked)) {661 return true;662 }663 664 // If the parent op has higher precedence, need parentheses for the child.665 return childOpPrec < parentOpPrec;666 }667 668 // the greater the higher669 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]670 private static int GetOperatorPrecedence(Expression node) {671 672 // Roughly matches C# operator precedence, with some additional673 // operators. Also things which are not binary/unary expressions,674 // such as conditional and type testing, don't use this mechanism.675 switch (node.NodeType) {676 // Assignment677 case ExpressionType.Assign:678 case ExpressionType.ExclusiveOrAssign:679 case ExpressionType.AddAssign:680 case ExpressionType.AddAssignChecked:681 case ExpressionType.SubtractAssign:682 case ExpressionType.SubtractAssignChecked:683 case ExpressionType.DivideAssign:684 case ExpressionType.ModuloAssign:685 case ExpressionType.MultiplyAssign:686 case ExpressionType.MultiplyAssignChecked:687 case ExpressionType.LeftShiftAssign:688 case ExpressionType.RightShiftAssign:689 case ExpressionType.AndAssign:690 case ExpressionType.OrAssign:691 case ExpressionType.PowerAssign:692 case ExpressionType.Coalesce:693 return 1;694 695 // Conditional (?:) would go here696 697 // Conditional OR698 case ExpressionType.OrElse:699 return 2;700 701 // Conditional AND702 case ExpressionType.AndAlso:703 return 3;704 705 // Logical OR706 case ExpressionType.Or:707 return 4;708 709 // Logical XOR710 case ExpressionType.ExclusiveOr:711 return 5;712 713 // Logical AND714 case ExpressionType.And:715 return 6;716 717 // Equality718 case ExpressionType.Equal:719 case ExpressionType.NotEqual:720 return 7;721 722 // Relational, type testing723 case ExpressionType.GreaterThan:724 case ExpressionType.LessThan:725 case ExpressionType.GreaterThanOrEqual:726 case ExpressionType.LessThanOrEqual:727 case ExpressionType.TypeAs:728 case ExpressionType.TypeIs:729 case ExpressionType.TypeEqual:730 return 8;731 732 // Shift733 case ExpressionType.LeftShift:734 case ExpressionType.RightShift:735 return 9;736 737 // Additive738 case ExpressionType.Add:739 case ExpressionType.AddChecked:740 case ExpressionType.Subtract:741 case ExpressionType.SubtractChecked:742 return 10;743 744 // Multiplicative745 case ExpressionType.Divide:746 case ExpressionType.Modulo:747 case ExpressionType.Multiply:748 case ExpressionType.MultiplyChecked:749 return 11;750 751 // Unary752 case ExpressionType.Negate:753 case ExpressionType.NegateChecked:754 case ExpressionType.UnaryPlus:755 case ExpressionType.Not:756 case ExpressionType.Convert:757 case ExpressionType.ConvertChecked:758 case ExpressionType.PreIncrementAssign:759 case ExpressionType.PreDecrementAssign:760 case ExpressionType.OnesComplement:761 case ExpressionType.Increment:762 case ExpressionType.Decrement:763 case ExpressionType.IsTrue:764 case ExpressionType.IsFalse:765 case ExpressionType.Unbox:766 case ExpressionType.Throw:767 return 12;768 769 // Power, which is not in C#770 // But VB/Python/Ruby put it here, above unary.771 case ExpressionType.Power:772 return 13;773 774 // Primary, which includes all other node types:775 // member access, calls, indexing, new.776 case ExpressionType.PostIncrementAssign:777 case ExpressionType.PostDecrementAssign:778 default:779 return 14;780 781 // These aren't expressions, so never need parentheses:782 // constants, variables783 case ExpressionType.Constant:784 case ExpressionType.Parameter:785 return 15;786 }787 }788 789 private void ParenthesizedVisit(Expression parent, Expression nodeToVisit) {790 if (NeedsParentheses(parent, nodeToVisit)) {791 Out("(");792 Visit(nodeToVisit);793 Out(")");794 } else {795 Visit(nodeToVisit);796 }797 }798 799 protected override Expression VisitMethodCall(MethodCallExpression node) {800 Out(".Call ");801 if (node.Object != null) {802 ParenthesizedVisit(node, node.Object);803 } else if (node.Method.DeclaringType != null) {804 Out(node.Method.DeclaringType.ToString());805 } else {806 Out("<UnknownType>");807 }808 809 Out(".");810 Out(node.Method.Name);811 VisitExpressions('(', node.Arguments);812 return node;813 }814 815 protected override Expression VisitNewArray(NewArrayExpression node) {816 if (node.NodeType == ExpressionType.NewArrayBounds) {817 // .NewArray MyType[expr1, expr2]818 Out(".NewArray " + node.Type.GetElementType().ToString());819 VisitExpressions('[', node.Expressions);820 } else {821 // .NewArray MyType {expr1, expr2}822 823 Out(".NewArray " + node.Type.ToString(), Flow.Space);824 VisitExpressions('{', node.Expressions);825 }826 827 return node;828 }829 830 protected override Expression VisitNew(NewExpression node) {831 Out(".New " + node.Type.ToString());832 VisitExpressions('(', node.Arguments);833 return node;834 }835 836 protected override ElementInit VisitElementInit(ElementInit node) {837 if (node.Arguments.Count == 1) {838 Visit(node.Arguments[0]);839 } else {840 VisitExpressions('{', node.Arguments);841 }842 843 return node;844 }845 846 protected override Expression VisitListInit(ListInitExpression node) {847 Visit(node.NewExpression);848 VisitExpressions('{', ',', node.Initializers, e => VisitElementInit(e));849 return node;850 }851 852 protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment) {853 Out(assignment.Member.Name);854 Out(Flow.Space, "=", Flow.Space);855 Visit(assignment.Expression);856 return assignment;857 }858 859 protected override MemberListBinding VisitMemberListBinding(MemberListBinding binding) {860 Out(binding.Member.Name);861 Out(Flow.Space, "=", Flow.Space);862 VisitExpressions('{', ',', binding.Initializers, e => VisitElementInit(e));863 return binding;864 }865 866 protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding) {867 Out(binding.Member.Name);868 Out(Flow.Space, "=", Flow.Space);869 VisitExpressions('{', ',', binding.Bindings, e => VisitMemberBinding(e));870 return binding;871 }872 873 protected override Expression VisitMemberInit(MemberInitExpression node) {874 Visit(node.NewExpression);875 VisitExpressions('{', ',', node.Bindings, e => VisitMemberBinding(e));876 return node;877 }878 879 protected override Expression VisitTypeBinary(TypeBinaryExpression node) {880 ParenthesizedVisit(node, node.Expression);881 switch (node.NodeType) {882 case ExpressionType.TypeIs:883 Out(Flow.Space, ".Is", Flow.Space);884 break;885 case ExpressionType.TypeEqual:886 Out(Flow.Space, ".TypeEqual", Flow.Space);887 break;888 }889 890 Out(node.TypeOperand.ToString());891 return node;892 }893 894 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]895 protected override Expression VisitUnary(UnaryExpression node) {896 bool parenthesize = NeedsParentheses(node, node.Operand);897 898 switch (node.NodeType) {899 case ExpressionType.Convert:900 Out("(" + node.Type.ToString() + ")");901 break;902 case ExpressionType.ConvertChecked:903 Out("#(" + node.Type.ToString() + ")");904 break;905 case ExpressionType.TypeAs:906 break;907 case ExpressionType.Not:908 Out(node.Type == typeof(bool) ? "!" : "~");909 break;910 case ExpressionType.OnesComplement:911 Out("~");912 break;913 case ExpressionType.Negate:914 Out("-");915 break;916 case ExpressionType.NegateChecked:917 Out("#-");918 break;919 case ExpressionType.UnaryPlus:920 Out("+");921 break;922 case ExpressionType.ArrayLength:923 break;924 case ExpressionType.Quote:925 Out("'");926 break;927 case ExpressionType.Throw:928 if (node.Operand == null) {929 Out(".Rethrow");930 } else {931 Out(".Throw", Flow.Space);932 }933 934 break;935 case ExpressionType.IsFalse:936 Out(".IsFalse");937 break;938 case ExpressionType.IsTrue:939 Out(".IsTrue");940 break;941 case ExpressionType.Decrement:942 Out(".Decrement");943 break;944 case ExpressionType.Increment:945 Out(".Increment");946 break;947 case ExpressionType.PreDecrementAssign:948 Out("--");949 break;950 case ExpressionType.PreIncrementAssign:951 Out("++");952 break;953 case ExpressionType.Unbox:954 Out(".Unbox");955 break;956 }957 958 ParenthesizedVisit(node, node.Operand);959 960 switch (node.NodeType) {961 case ExpressionType.TypeAs:962 Out(Flow.Space, ".As", Flow.Space | Flow.Break);963 Out(node.Type.ToString());964 break;965 966 case ExpressionType.ArrayLength:967 Out(".Length");968 break;969 970 case ExpressionType.PostDecrementAssign:971 Out("--");972 break;973 974 case ExpressionType.PostIncrementAssign:975 Out("++");976 break;977 }978 979 return node;980 }981 982 protected override Expression VisitBlock(BlockExpression node) {983 Out(".Block");984 985 // Display <type> if the type of the BlockExpression is different from the986 // last expression's type in the block.987 if (node.Type != node.Expressions[node.Expressions.Count - 1].Type) {988 Out(string.Create(CultureInfo.CurrentCulture, $"<{node.Type}>"));989 }990 991 VisitDeclarations(node.Variables);992 Out(" ");993 // Use ; to separate expressions in the block994 VisitExpressions('{', ';', node.Expressions);995 996 return node;997 }998 999 protected override Expression VisitDefault(DefaultExpression node) {1000 Out(".Default(" + node.Type.ToString() + ")");1001 return node;1002 }1003 1004 protected override Expression VisitLabel(LabelExpression node) {1005 Out(".Label", Flow.NewLine);1006 Indent();1007 Visit(node.DefaultValue);1008 Dedent();1009 NewLine();1010 DumpLabel(node.Target);1011 return node;1012 }1013 1014 protected override Expression VisitGoto(GotoExpression node) {1015 Out("." + node.Kind.ToString(), Flow.Space);1016 Out(GetLabelTargetName(node.Target), Flow.Space);1017 Out("{", Flow.Space);1018 Visit(node.Value);1019 Out(Flow.Space, "}");1020 return node;1021 }1022 1023 protected override Expression VisitLoop(LoopExpression node) {1024 Out(".Loop", Flow.Space);1025 if (node.ContinueLabel != null) {1026 DumpLabel(node.ContinueLabel);1027 }1028 1029 Out(" {", Flow.NewLine);1030 Indent();1031 Visit(node.Body);1032 Dedent();1033 Out(Flow.NewLine, "}");1034 if (node.BreakLabel != null) {1035 Out(string.Empty, Flow.NewLine);1036 DumpLabel(node.BreakLabel);1037 }1038 1039 return node;1040 }1041 1042 protected override SwitchCase VisitSwitchCase(SwitchCase node) {1043 foreach (var test in node.TestValues) {1044 Out(".Case (");1045 Visit(test);1046 Out("):", Flow.NewLine);1047 }1048 1049 Indent(); Indent();1050 Visit(node.Body);1051 Dedent(); Dedent();1052 NewLine();1053 return node;1054 }1055 1056 protected override Expression VisitSwitch(SwitchExpression node) {1057 Out(".Switch ");1058 Out("(");1059 Visit(node.SwitchValue);1060 Out(") {", Flow.NewLine);1061 Visit(node.Cases, VisitSwitchCase);1062 if (node.DefaultBody != null) {1063 Out(".Default:", Flow.NewLine);1064 Indent(); Indent();1065 Visit(node.DefaultBody);1066 Dedent(); Dedent();1067 NewLine();1068 }1069 1070 Out("}");1071 return node;1072 }1073 1074 protected override CatchBlock VisitCatchBlock(CatchBlock node) {1075 Out(Flow.NewLine, "} .Catch (" + node.Test.ToString());1076 if (node.Variable != null) {1077 Out(Flow.Space, string.Empty);1078 VisitParameter(node.Variable);1079 }1080 1081 if (node.Filter != null) {1082 Out(") .If (", Flow.Break);1083 Visit(node.Filter);1084 }1085 1086 Out(") {", Flow.NewLine);1087 Indent();1088 Visit(node.Body);1089 Dedent();1090 return node;1091 }1092 1093 protected override Expression VisitTry(TryExpression node) {1094 Out(".Try {", Flow.NewLine);1095 Indent();1096 Visit(node.Body);1097 Dedent();1098 Visit(node.Handlers, VisitCatchBlock);1099 if (node.Finally != null) {1100 Out(Flow.NewLine, "} .Finally {", Flow.NewLine);1101 Indent();1102 Visit(node.Finally);1103 Dedent();1104 } else if (node.Fault != null) {1105 Out(Flow.NewLine, "} .Fault {", Flow.NewLine);1106 Indent();1107 Visit(node.Fault);1108 Dedent();1109 }1110 1111 Out(Flow.NewLine, "}");1112 return node;1113 }1114 1115 protected override Expression VisitIndex(IndexExpression node) {1116 if (node.Indexer != null) {1117 OutMember(node, node.Object, node.Indexer);1118 } else {1119 ParenthesizedVisit(node, node.Object);1120 }1121 1122 VisitExpressions('[', node.Arguments);1123 return node;1124 }1125 1126 protected override Expression VisitExtension(Expression node) {1127 Out(string.Create(CultureInfo.CurrentCulture, $".Extension<{node.GetType()}>"));1128 1129 if (node.CanReduce) {1130 Out(Flow.Space, "{", Flow.NewLine);1131 Indent();1132 Visit(node.Reduce());1133 Dedent();1134 Out(Flow.NewLine, "}");1135 }1136 1137 return node;1138 }1139 1140 protected override Expression VisitDebugInfo(DebugInfoExpression node) {1141 Out(string.Format(1142 CultureInfo.CurrentCulture,1143 ".DebugInfo({0}: {1}, {2} - {3}, {4})",1144 node.Document.FileName,1145 node.StartLine,1146 node.StartColumn,1147 node.EndLine,1148 node.EndColumn)1149 );1150 return node;1151 }1152 1153 private void DumpLabel(LabelTarget target) {1154 Out(string.Create(CultureInfo.CurrentCulture, $".LabelTarget {GetLabelTargetName(target)}:"));1155 }1156 1157 private string GetLabelTargetName(LabelTarget target) {1158 if (string.IsNullOrEmpty(target.Name)) {1159 // Create the label target name as #Label1, #Label2, etc.1160 return string.Create(CultureInfo.CurrentCulture, $"#Label{GetLabelTargetId(target)}");1161 } else {1162 return GetDisplayName(target.Name);1163 }1164 }1165 1166 private void WriteLambda(LambdaExpression lambda) {1167 Out(1168 string.Create(CultureInfo.CurrentCulture, $".Lambda {GetLambdaName(lambda)}<{lambda.Type}>")1169 );1170 1171 VisitDeclarations(lambda.Parameters);1172 1173 Out(Flow.Space, "{", Flow.NewLine);1174 Indent();1175 Visit(lambda.Body);1176 Dedent();1177 Out(Flow.NewLine, "}");1178 Debug.Assert(_stack.Count == 0);1179 }1180 1181 private string GetLambdaName(LambdaExpression lambda) {1182 if (string.IsNullOrEmpty(lambda.Name)) {1183 return "#Lambda" + GetLambdaId(lambda);1184 }1185 1186 return GetDisplayName(lambda.Name);1187 }1188 1189 /// <summary>1190 /// Return true if the input string contains any whitespace character.1191 /// Otherwise false.1192 /// </summary>1193 private static bool ContainsWhiteSpace(string name) {1194 foreach (char c in name) {1195 if (char.IsWhiteSpace(c)) {1196 return true;1197 }1198 }1199 1200 return false;