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;
22 using System.Reflection;
23 using System.Runtime.CompilerServices;
25 using Microsoft.Scripting.Utils;
28 namespace Microsoft.Scripting.Ast {
30 namespace System.Linq.Expressions {
32 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
33 internal sealed class ExpressionStringBuilder : ExpressionVisitor {
34 private StringBuilder _out;
36 // Associate every unique label or anonymous parameter in the tree with an integer.
37 // The label is displayed as Label_#.
38 private Dictionary<object, int> _ids;
40 private ExpressionStringBuilder() {
41 _out = new StringBuilder();
44 public override string ToString() {
45 return _out.ToString();
48 private void AddLabel(LabelTarget label) {
50 _ids = new Dictionary<object, int>();
53 if (!_ids.ContainsKey(label)) {
54 _ids.Add(label, _ids.Count);
59 private int GetLabelId(LabelTarget label) {
61 _ids = new Dictionary<object, int>();
66 if (!_ids.TryGetValue(label, out id)) {
67 //label is met the first time
75 private void AddParam(ParameterExpression p) {
77 _ids = new Dictionary<object, int>();
80 if (!_ids.ContainsKey(p)) {
81 _ids.Add(p, _ids.Count);
86 private int GetParamId(ParameterExpression p) {
88 _ids = new Dictionary<object, int>();
93 if (!_ids.TryGetValue(p, out id)) {
94 // p is met the first time
102 #region The printing code
104 private void Out(string s) {
108 private void Out(char c) {
114 #region Output an expresstion tree to a string
117 /// Output a given expression tree to a string.
119 internal static string ExpressionToString(Expression node) {
120 Debug.Assert(node != null);
121 ExpressionStringBuilder esb = new ExpressionStringBuilder();
123 return esb.ToString();
126 internal static string CatchBlockToString(CatchBlock node) {
127 Debug.Assert(node != null);
128 ExpressionStringBuilder esb = new ExpressionStringBuilder();
129 esb.VisitCatchBlock(node);
130 return esb.ToString();
133 internal static string SwitchCaseToString(SwitchCase node) {
134 Debug.Assert(node != null);
135 ExpressionStringBuilder esb = new ExpressionStringBuilder();
136 esb.VisitSwitchCase(node);
137 return esb.ToString();
141 /// Output a given member binding to a string.
143 internal static string MemberBindingToString(MemberBinding node) {
144 Debug.Assert(node != null);
145 ExpressionStringBuilder esb = new ExpressionStringBuilder();
146 esb.VisitMemberBinding(node);
147 return esb.ToString();
151 /// Output a given ElementInit to a string.
153 internal static string ElementInitBindingToString(ElementInit node) {
154 Debug.Assert(node != null);
155 ExpressionStringBuilder esb = new ExpressionStringBuilder();
156 esb.VisitElementInit(node);
157 return esb.ToString();
160 // More proper would be to make this a virtual method on Action
161 private static string FormatBinder(CallSiteBinder binder) {
162 ConvertBinder convert;
163 GetMemberBinder getMember;
164 SetMemberBinder setMember;
165 DeleteMemberBinder deleteMember;
166 InvokeMemberBinder call;
167 UnaryOperationBinder unary;
168 BinaryOperationBinder binary;
170 if ((convert = binder as ConvertBinder) != null) {
171 return "Convert " + convert.Type;
172 } else if ((getMember = binder as GetMemberBinder) != null) {
173 return "GetMember " + getMember.Name;
174 } else if ((setMember = binder as SetMemberBinder) != null) {
175 return "SetMember " + setMember.Name;
176 } else if ((deleteMember = binder as DeleteMemberBinder) != null) {
177 return "DeleteMember " + deleteMember.Name;
178 } else if (binder is GetIndexBinder) {
180 } else if (binder is SetIndexBinder) {
182 } else if (binder is DeleteIndexBinder) {
183 return "DeleteIndex";
184 } else if ((call = binder as InvokeMemberBinder) != null) {
185 return "Call " + call.Name;
186 } else if (binder is InvokeBinder) {
188 } else if (binder is CreateInstanceBinder) {
190 } else if ((unary = binder as UnaryOperationBinder) != null) {
191 return unary.Operation.ToString();
192 } else if ((binary = binder as BinaryOperationBinder) != null) {
193 return binary.Operation.ToString();
195 return "CallSiteBinder";
199 private void VisitExpressions<T>(char open, IList<T> expressions, char close) where T : Expression {
200 VisitExpressions(open, expressions, close, ", ");
203 private void VisitExpressions<T>(char open, IList<T> expressions, char close, string seperator) where T : Expression {
205 if (expressions != null) {
207 foreach (T e in expressions) {
219 protected internal override Expression VisitDynamic(DynamicExpression node) {
220 Out(FormatBinder(node.Binder));
221 VisitExpressions('(', node.Arguments, ')');
225 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
226 protected internal override Expression VisitBinary(BinaryExpression node) {
227 if (node.NodeType == ExpressionType.ArrayIndex) {
234 switch (node.NodeType) {
235 // AndAlso and OrElse were unintentionally changed in
236 // CLR 4. We changed them to "AndAlso" and "OrElse" to
237 // be 3.5 compatible, but it turns out 3.5 shipped with
238 // "&&" and "||". Oops.
239 case ExpressionType.AndAlso:
242 case ExpressionType.OrElse:
245 case ExpressionType.Assign: op = "="; break;
246 case ExpressionType.Equal:
249 case ExpressionType.NotEqual: op = "!="; break;
250 case ExpressionType.GreaterThan: op = ">"; break;
251 case ExpressionType.LessThan: op = "<"; break;
252 case ExpressionType.GreaterThanOrEqual: op = ">="; break;
253 case ExpressionType.LessThanOrEqual: op = "<="; break;
254 case ExpressionType.Add: op = "+"; break;
255 case ExpressionType.AddAssign: op = "+="; break;
256 case ExpressionType.AddAssignChecked: op = "+="; break;
257 case ExpressionType.AddChecked: op = "+"; break;
258 case ExpressionType.Subtract: op = "-"; break;
259 case ExpressionType.SubtractAssign: op = "-="; break;
260 case ExpressionType.SubtractAssignChecked: op = "-="; break;
261 case ExpressionType.SubtractChecked: op = "-"; break;
262 case ExpressionType.Divide: op = "/"; break;
263 case ExpressionType.DivideAssign: op = "/="; break;
264 case ExpressionType.Modulo: op = "%"; break;
265 case ExpressionType.ModuloAssign: op = "%="; break;
266 case ExpressionType.Multiply: op = "*"; break;
267 case ExpressionType.MultiplyAssign: op = "*="; break;
268 case ExpressionType.MultiplyAssignChecked: op = "*="; break;
269 case ExpressionType.MultiplyChecked: op = "*"; break;
270 case ExpressionType.LeftShift: op = "<<"; break;
271 case ExpressionType.LeftShiftAssign: op = "<<="; break;
272 case ExpressionType.RightShift: op = ">>"; break;
273 case ExpressionType.RightShiftAssign: op = ">>="; break;
274 case ExpressionType.And:
275 if (node.Type == typeof(bool) || node.Type == typeof(bool?)) {
281 case ExpressionType.AndAssign:
282 if (node.Type == typeof(bool) || node.Type == typeof(bool?)) {
288 case ExpressionType.Or:
289 if (node.Type == typeof(bool) || node.Type == typeof(bool?)) {
295 case ExpressionType.OrAssign:
296 if (node.Type == typeof(bool) || node.Type == typeof(bool?)) {
298 } else { op = "|="; }
300 case ExpressionType.ExclusiveOr: op = "^"; break;
301 case ExpressionType.ExclusiveOrAssign: op = "^="; break;
302 case ExpressionType.Power: op = "^"; break;
303 case ExpressionType.PowerAssign: op = "**="; break;
304 case ExpressionType.Coalesce: op = "??"; break;
307 throw new InvalidOperationException();
320 protected internal override Expression VisitParameter(ParameterExpression node) {
324 string name = node.Name;
325 if (String.IsNullOrEmpty(name)) {
326 Out("Param_" + GetParamId(node));
333 protected internal override Expression VisitLambda<T>(Expression<T> node) {
334 if (node.Parameters.Count == 1) {
336 Visit(node.Parameters[0]);
338 // (p1, p2, ..., pn) => body
339 VisitExpressions('(', node.Parameters, ')');
346 protected internal override Expression VisitListInit(ListInitExpression node) {
347 Visit(node.NewExpression);
349 for (int i = 0, n = node.Initializers.Count; i < n; i++) {
353 Out(node.Initializers[i].ToString());
359 protected internal override Expression VisitConditional(ConditionalExpression node) {
370 protected internal override Expression VisitConstant(ConstantExpression node) {
371 if (node.Value != null) {
372 string sValue = node.Value.ToString();
373 if (node.Value is string) {
377 } else if (sValue == node.Value.GetType().ToString()) {
390 protected internal override Expression VisitDebugInfo(DebugInfoExpression node) {
391 string s = String.Format(
392 CultureInfo.CurrentCulture,
393 "<DebugInfo({0}: {1}, {2}, {3}, {4})>",
394 node.Document.FileName,
404 protected internal override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) {
405 VisitExpressions('(', node.Variables, ')');
409 // Prints ".instanceField" or "declaringType.staticField"
410 private void OutMember(Expression instance, MemberInfo member) {
411 if (instance != null) {
413 Out("." + member.Name);
415 // For static members, include the type name
416 Out(member.DeclaringType.Name + "." + member.Name);
420 protected internal override Expression VisitMember(MemberExpression node) {
421 OutMember(node.Expression, node.Member);
425 protected internal override Expression VisitMemberInit(MemberInitExpression node) {
426 if (node.NewExpression.Arguments.Count == 0 &&
427 node.NewExpression.Type.Name.Contains("<")) {
428 // anonymous type constructor
431 Visit(node.NewExpression);
434 for (int i = 0, n = node.Bindings.Count; i < n; i++) {
435 MemberBinding b = node.Bindings[i];
439 VisitMemberBinding(b);
445 protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment) {
446 Out(assignment.Member.Name);
448 Visit(assignment.Expression);
452 protected override MemberListBinding VisitMemberListBinding(MemberListBinding binding) {
453 Out(binding.Member.Name);
455 for (int i = 0, n = binding.Initializers.Count; i < n; i++) {
459 VisitElementInit(binding.Initializers[i]);
465 protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding) {
466 Out(binding.Member.Name);
468 for (int i = 0, n = binding.Bindings.Count; i < n; i++) {
472 VisitMemberBinding(binding.Bindings[i]);
478 protected override ElementInit VisitElementInit(ElementInit initializer) {
479 Out(initializer.AddMethod.ToString());
481 VisitExpressions('(', initializer.Arguments, ')', sep);
485 protected internal override Expression VisitInvocation(InvocationExpression node) {
487 Visit(node.Expression);
489 for (int i = 0, n = node.Arguments.Count; i < n; i++) {
491 Visit(node.Arguments[i]);
497 protected internal override Expression VisitMethodCall(MethodCallExpression node) {
499 Expression ob = node.Object;
501 if (Attribute.GetCustomAttribute(node.Method, typeof(ExtensionAttribute)) != null) {
503 ob = node.Arguments[0];
510 Out(node.Method.Name);
512 for (int i = start, n = node.Arguments.Count; i < n; i++) {
515 Visit(node.Arguments[i]);
521 protected internal override Expression VisitNewArray(NewArrayExpression node) {
522 switch (node.NodeType) {
523 case ExpressionType.NewArrayBounds:
524 // new MyType[](expr1, expr2)
525 Out("new " + node.Type.ToString());
526 VisitExpressions('(', node.Expressions, ')');
528 case ExpressionType.NewArrayInit:
529 // new [] {expr1, expr2}
531 VisitExpressions('{', node.Expressions, '}');
537 protected internal override Expression VisitNew(NewExpression node) {
538 Out("new " + node.Type.Name);
540 var members = node.Members;
541 for (int i = 0; i < node.Arguments.Count; i++) {
545 if (members != null) {
546 string name = members[i].Name;
550 Visit(node.Arguments[i]);
556 protected internal override Expression VisitTypeBinary(TypeBinaryExpression node) {
558 Visit(node.Expression);
559 switch (node.NodeType) {
560 case ExpressionType.TypeIs:
563 case ExpressionType.TypeEqual:
567 Out(node.TypeOperand.Name);
572 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
573 protected internal override Expression VisitUnary(UnaryExpression node) {
574 switch (node.NodeType) {
575 case ExpressionType.TypeAs:
578 case ExpressionType.Not:
581 case ExpressionType.Negate:
582 case ExpressionType.NegateChecked:
585 case ExpressionType.UnaryPlus:
588 case ExpressionType.Quote:
590 case ExpressionType.Throw:
593 case ExpressionType.Increment:
596 case ExpressionType.Decrement:
599 case ExpressionType.PreIncrementAssign:
602 case ExpressionType.PreDecrementAssign:
605 case ExpressionType.OnesComplement:
609 Out(node.NodeType.ToString());
616 switch (node.NodeType) {
617 case ExpressionType.Negate:
618 case ExpressionType.NegateChecked:
619 case ExpressionType.UnaryPlus:
620 case ExpressionType.PreDecrementAssign:
621 case ExpressionType.PreIncrementAssign:
622 case ExpressionType.Quote:
624 case ExpressionType.TypeAs:
629 case ExpressionType.PostIncrementAssign:
632 case ExpressionType.PostDecrementAssign:
642 protected internal override Expression VisitBlock(BlockExpression node) {
644 foreach (var v in node.Variables) {
653 protected internal override Expression VisitDefault(DefaultExpression node) {
660 protected internal override Expression VisitLabel(LabelExpression node) {
662 DumpLabel(node.Target);
667 protected internal override Expression VisitGoto(GotoExpression node) {
668 Out(node.Kind.ToString().ToLower(CultureInfo.CurrentCulture));
669 DumpLabel(node.Target);
670 if (node.Value != null) {
678 protected internal override Expression VisitLoop(LoopExpression node) {
683 protected override SwitchCase VisitSwitchCase(SwitchCase node) {
685 VisitExpressions('(', node.TestValues, ')');
690 protected internal override Expression VisitSwitch(SwitchExpression node) {
693 Visit(node.SwitchValue);
698 protected override CatchBlock VisitCatchBlock(CatchBlock node) {
699 Out("catch (" + node.Test.Name);
700 if (node.Variable != null) {
701 Out(node.Variable.Name ?? "");
707 protected internal override Expression VisitTry(TryExpression node) {
712 protected internal override Expression VisitIndex(IndexExpression node) {
713 if (node.Object != null) {
716 Debug.Assert(node.Indexer != null);
717 Out(node.Indexer.DeclaringType.Name);
719 if (node.Indexer != null) {
721 Out(node.Indexer.Name);
724 VisitExpressions('[', node.Arguments, ']');
728 protected internal override Expression VisitExtension(Expression node) {
729 // Prefer an overriden ToString, if available.
730 var flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.ExactBinding;
731 var toString = node.GetType().GetMethod("ToString", flags, null, ReflectionUtils.EmptyTypes, null);
732 if (toString.DeclaringType != typeof(Expression)) {
733 Out(node.ToString());
738 // For 3.5 subclasses, print the NodeType.
739 // For Extension nodes, print the class name.
740 if (node.NodeType == ExpressionType.Extension) {
741 Out(node.GetType().FullName);
743 Out(node.NodeType.ToString());
749 private void DumpLabel(LabelTarget target) {
750 if (!String.IsNullOrEmpty(target.Name)) {
753 int labelId = GetLabelId(target);
754 Out("UnamedLabel_" + labelId);