Merge pull request #347 from JamesB7/master
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Ast / ExpressionStringBuilder.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
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.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 using System;
17 using System.Collections.Generic;
18 using System.Diagnostics;
19 using System.Dynamic;
20 using System.Dynamic.Utils;
21 using System.Globalization;
22 using System.Reflection;
23 using System.Runtime.CompilerServices;
24 using System.Text;
25 using Microsoft.Scripting.Utils;
26
27 #if !FEATURE_CORE_DLR
28 namespace Microsoft.Scripting.Ast {
29 #else
30 namespace System.Linq.Expressions {
31 #endif
32     [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
33     internal sealed class ExpressionStringBuilder : ExpressionVisitor {
34         private StringBuilder _out;
35
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;
39
40         private ExpressionStringBuilder() {
41             _out = new StringBuilder();
42         }
43
44         public override string ToString() {
45             return _out.ToString();
46         }
47
48         private void AddLabel(LabelTarget label) {
49             if (_ids == null) {
50                 _ids = new Dictionary<object, int>();
51                 _ids.Add(label, 0);
52             } else {
53                 if (!_ids.ContainsKey(label)) {
54                     _ids.Add(label, _ids.Count);
55                 }
56             }
57         }
58
59         private int GetLabelId(LabelTarget label) {
60             if (_ids == null) {
61                 _ids = new Dictionary<object, int>();
62                 AddLabel(label);
63                 return 0;
64             } else {
65                 int id;
66                 if (!_ids.TryGetValue(label, out id)) {
67                     //label is met the first time
68                     id = _ids.Count;
69                     AddLabel(label);
70                 }
71                 return id;
72             }
73         }
74
75         private void AddParam(ParameterExpression p) {
76             if (_ids == null) {
77                 _ids = new Dictionary<object, int>();
78                 _ids.Add(_ids, 0);
79             } else {
80                 if (!_ids.ContainsKey(p)) {
81                     _ids.Add(p, _ids.Count);
82                 }
83             }
84         }
85
86         private int GetParamId(ParameterExpression p) {
87             if (_ids == null) {
88                 _ids = new Dictionary<object, int>();
89                 AddParam(p);
90                 return 0;
91             } else {
92                 int id;
93                 if (!_ids.TryGetValue(p, out id)) {
94                     // p is met the first time
95                     id = _ids.Count;
96                     AddParam(p);
97                 }
98                 return id;
99             }
100         }
101
102         #region The printing code
103
104         private void Out(string s) {
105             _out.Append(s);
106         }
107
108         private void Out(char c) {
109             _out.Append(c);
110         }
111
112         #endregion
113
114         #region Output an expresstion tree to a string
115
116         /// <summary>
117         /// Output a given expression tree to a string.
118         /// </summary>
119         internal static string ExpressionToString(Expression node) {
120             Debug.Assert(node != null);
121             ExpressionStringBuilder esb = new ExpressionStringBuilder();
122             esb.Visit(node);
123             return esb.ToString();
124         }
125
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();
131         }
132
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();
138         }
139
140         /// <summary>
141         /// Output a given member binding to a string.
142         /// </summary>
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();
148         }
149
150         /// <summary>
151         /// Output a given ElementInit to a string.
152         /// </summary>
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();
158         }
159
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;
169
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) {
179                 return "GetIndex";
180             } else if (binder is SetIndexBinder) {
181                 return "SetIndex";
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) {
187                 return "Invoke";
188             } else if (binder is CreateInstanceBinder) {
189                 return "Create";
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();
194             } else {
195                 return "CallSiteBinder";
196             }
197         }
198
199         private void VisitExpressions<T>(char open, IList<T> expressions, char close) where T : Expression {
200             VisitExpressions(open, expressions, close, ", ");
201         }
202
203         private void VisitExpressions<T>(char open, IList<T> expressions, char close, string seperator) where T : Expression {
204             Out(open);
205             if (expressions != null) {
206                 bool isFirst = true;
207                 foreach (T e in expressions) {
208                     if (isFirst) {
209                         isFirst = false;
210                     } else {
211                         Out(seperator);
212                     }
213                     Visit(e);
214                 }
215             }
216             Out(close);
217         }
218
219         protected internal override Expression VisitDynamic(DynamicExpression node) {
220             Out(FormatBinder(node.Binder));
221             VisitExpressions('(', node.Arguments, ')');
222             return node;
223         }
224
225         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
226         protected internal override Expression VisitBinary(BinaryExpression node) {
227             if (node.NodeType == ExpressionType.ArrayIndex) {
228                 Visit(node.Left);
229                 Out("[");
230                 Visit(node.Right);
231                 Out("]");
232             } else {
233                 string op;
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:
240                         op = "AndAlso";
241                         break;
242                     case ExpressionType.OrElse:
243                         op = "OrElse";
244                         break;
245                     case ExpressionType.Assign: op = "="; break;
246                     case ExpressionType.Equal:
247                                                 op = "==";
248                                                 break;
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?)) {
276                             op = "And";
277                         } else {
278                             op = "&";
279                         }
280                         break;
281                     case ExpressionType.AndAssign:
282                         if (node.Type == typeof(bool) || node.Type == typeof(bool?)) {
283                             op = "&&=";
284                         } else {
285                             op = "&=";
286                         }
287                         break;
288                     case ExpressionType.Or:
289                         if (node.Type == typeof(bool) || node.Type == typeof(bool?)) {
290                             op = "Or";
291                         } else {
292                             op = "|";
293                         }
294                         break;
295                     case ExpressionType.OrAssign:
296                         if (node.Type == typeof(bool) || node.Type == typeof(bool?)) {
297                             op = "||=";
298                         } else { op = "|="; }
299                         break;
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;
305
306                     default:
307                         throw new InvalidOperationException();
308                 }
309                 Out("(");
310                 Visit(node.Left);
311                 Out(' ');
312                 Out(op);
313                 Out(' ');
314                 Visit(node.Right);
315                 Out(")");
316             }
317             return node;
318         }
319
320         protected internal override Expression VisitParameter(ParameterExpression node) {
321             if (node.IsByRef) {
322                 Out("ref ");
323             }
324             string name = node.Name;
325             if (String.IsNullOrEmpty(name)) {
326                 Out("Param_" + GetParamId(node));
327             } else {
328                 Out(name);
329             }
330             return node;
331         }
332
333         protected internal override Expression VisitLambda<T>(Expression<T> node) {
334             if (node.Parameters.Count == 1) {
335                 // p => body
336                 Visit(node.Parameters[0]);
337             } else {
338                 // (p1, p2, ..., pn) => body
339                 VisitExpressions('(', node.Parameters, ')');
340             }
341             Out(" => ");
342             Visit(node.Body);
343             return node;
344         }
345
346         protected internal override Expression VisitListInit(ListInitExpression node) {
347             Visit(node.NewExpression);
348             Out(" {");
349             for (int i = 0, n = node.Initializers.Count; i < n; i++) {
350                 if (i > 0) {
351                     Out(", ");
352                 }
353                 Out(node.Initializers[i].ToString());
354             }
355             Out("}");
356             return node;
357         }
358
359         protected internal override Expression VisitConditional(ConditionalExpression node) {
360             Out("IIF(");
361             Visit(node.Test);
362             Out(", ");
363             Visit(node.IfTrue);
364             Out(", ");
365             Visit(node.IfFalse);
366             Out(")");
367             return node;
368         }
369
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) {
374                     Out("\"");
375                     Out(sValue);
376                     Out("\"");
377                 } else if (sValue == node.Value.GetType().ToString()) {
378                     Out("value(");
379                     Out(sValue);
380                     Out(")");
381                 } else {
382                     Out(sValue);
383                 }
384             } else {
385                 Out("null");
386             }
387             return node;
388         }
389
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,
395                 node.StartLine,
396                 node.StartColumn,
397                 node.EndLine,
398                 node.EndColumn
399             );
400             Out(s);
401             return node;
402         }
403
404         protected internal override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) {
405             VisitExpressions('(', node.Variables, ')');
406             return node;
407         }
408
409         // Prints ".instanceField" or "declaringType.staticField"
410         private void OutMember(Expression instance, MemberInfo member) {
411             if (instance != null) {
412                 Visit(instance);
413                 Out("." + member.Name);
414             } else {
415                 // For static members, include the type name
416                 Out(member.DeclaringType.Name + "." + member.Name);
417             }
418         }
419
420         protected internal override Expression VisitMember(MemberExpression node) {
421             OutMember(node.Expression, node.Member);
422             return node;
423         }
424
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
429                 Out("new");
430             } else {
431                 Visit(node.NewExpression);
432             }
433             Out(" {");
434             for (int i = 0, n = node.Bindings.Count; i < n; i++) {
435                 MemberBinding b = node.Bindings[i];
436                 if (i > 0) {
437                     Out(", ");
438                 }
439                 VisitMemberBinding(b);
440             }
441             Out("}");
442             return node;
443         }
444
445         protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment) {
446             Out(assignment.Member.Name);
447             Out(" = ");
448             Visit(assignment.Expression);
449             return assignment;
450         }
451
452         protected override MemberListBinding VisitMemberListBinding(MemberListBinding binding) {
453             Out(binding.Member.Name);
454             Out(" = {");
455             for (int i = 0, n = binding.Initializers.Count; i < n; i++) {
456                 if (i > 0) {
457                     Out(", ");
458                 }
459                 VisitElementInit(binding.Initializers[i]);
460             }
461             Out("}");
462             return binding;
463         }
464
465         protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding) {
466             Out(binding.Member.Name);
467             Out(" = {");
468             for (int i = 0, n = binding.Bindings.Count; i < n; i++) {
469                 if (i > 0) {
470                     Out(", ");
471                 }
472                 VisitMemberBinding(binding.Bindings[i]);
473             }
474             Out("}");
475             return binding;
476         }
477
478         protected override ElementInit VisitElementInit(ElementInit initializer) {
479             Out(initializer.AddMethod.ToString());
480             string sep = ", ";
481             VisitExpressions('(', initializer.Arguments, ')', sep);
482             return initializer;
483         }
484
485         protected internal override Expression VisitInvocation(InvocationExpression node) {
486             Out("Invoke(");
487             Visit(node.Expression);
488             string sep = ", ";
489             for (int i = 0, n = node.Arguments.Count; i < n; i++) {
490                 Out(sep);
491                 Visit(node.Arguments[i]);
492             }
493             Out(")");
494             return node;
495         }
496
497         protected internal override Expression VisitMethodCall(MethodCallExpression node) {
498             int start = 0;
499             Expression ob = node.Object;
500
501             if (Attribute.GetCustomAttribute(node.Method, typeof(ExtensionAttribute)) != null) {
502                 start = 1;
503                 ob = node.Arguments[0];
504             }
505
506             if (ob != null) {
507                 Visit(ob);
508                 Out(".");
509             }
510             Out(node.Method.Name);
511             Out("(");
512             for (int i = start, n = node.Arguments.Count; i < n; i++) {
513                 if (i > start)
514                     Out(", ");
515                 Visit(node.Arguments[i]);
516             }
517             Out(")");
518             return node;
519         }
520
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, ')');
527                     break;
528                 case ExpressionType.NewArrayInit:
529                     // new [] {expr1, expr2}
530                     Out("new [] ");
531                     VisitExpressions('{', node.Expressions, '}');
532                     break;
533             }
534             return node;
535         }
536
537         protected internal override Expression VisitNew(NewExpression node) {
538             Out("new " + node.Type.Name);
539             Out("(");
540             var members = node.Members;
541             for (int i = 0; i < node.Arguments.Count; i++) {
542                 if (i > 0) {
543                     Out(", ");
544                 }
545                 if (members != null) {
546                     string name = members[i].Name;                    
547                     Out(name);
548                     Out(" = ");
549                 }
550                 Visit(node.Arguments[i]);
551             }
552             Out(")");
553             return node;
554         }
555
556         protected internal override Expression VisitTypeBinary(TypeBinaryExpression node) {
557             Out("(");
558             Visit(node.Expression);
559             switch (node.NodeType) {
560                 case ExpressionType.TypeIs:
561                     Out(" Is ");
562                     break;
563                 case ExpressionType.TypeEqual:
564                     Out(" TypeEqual ");
565                     break;
566             }
567             Out(node.TypeOperand.Name);
568             Out(")");
569             return node;
570         }
571
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:
576                     Out("(");
577                     break;
578                 case ExpressionType.Not:
579                     Out("Not(");
580                     break;
581                 case ExpressionType.Negate:
582                 case ExpressionType.NegateChecked:
583                     Out("-");
584                     break;
585                 case ExpressionType.UnaryPlus:
586                     Out("+");
587                     break;
588                 case ExpressionType.Quote:
589                     break;
590                 case ExpressionType.Throw:
591                     Out("throw(");
592                     break;
593                 case ExpressionType.Increment:
594                     Out("Increment(");
595                     break;
596                 case ExpressionType.Decrement:
597                     Out("Decrement(");
598                     break;
599                 case ExpressionType.PreIncrementAssign:
600                     Out("++");
601                     break;
602                 case ExpressionType.PreDecrementAssign:
603                     Out("--");
604                     break;
605                 case ExpressionType.OnesComplement:
606                     Out("~(");
607                     break;
608                 default:
609                     Out(node.NodeType.ToString());
610                     Out("(");
611                     break;
612             }
613
614             Visit(node.Operand);
615
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:
623                     break;
624                 case ExpressionType.TypeAs:
625                     Out(" As ");
626                     Out(node.Type.Name);
627                     Out(")");
628                     break;
629                 case ExpressionType.PostIncrementAssign:
630                     Out("++");
631                     break;
632                 case ExpressionType.PostDecrementAssign:
633                     Out("--");
634                     break;
635                 default:
636                     Out(")");
637                     break;
638             }
639             return node;
640         }
641
642         protected internal override Expression VisitBlock(BlockExpression node) {
643             Out("{");
644             foreach (var v in node.Variables) {
645                 Out("var ");
646                 Visit(v);
647                 Out(";");
648             }
649             Out(" ... }");
650             return node;
651         }
652
653         protected internal override Expression VisitDefault(DefaultExpression node) {
654             Out("default(");
655             Out(node.Type.Name);
656             Out(")");
657             return node;
658         }
659
660         protected internal override Expression VisitLabel(LabelExpression node) {
661             Out("{ ... } ");
662             DumpLabel(node.Target);
663             Out(":");
664             return node;
665         }
666
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) {
671                 Out(" (");
672                 Visit(node.Value);
673                 Out(") ");
674             }
675             return node;
676         }
677
678         protected internal override Expression VisitLoop(LoopExpression node) {
679             Out("loop { ... }");
680             return node;
681         }
682
683         protected override SwitchCase VisitSwitchCase(SwitchCase node) {
684             Out("case ");
685             VisitExpressions('(', node.TestValues, ')');
686             Out(": ...");
687             return node;
688         }
689
690         protected internal override Expression VisitSwitch(SwitchExpression node) {
691             Out("switch ");
692             Out("(");
693             Visit(node.SwitchValue);
694             Out(") { ... }");
695             return node;
696         }
697
698         protected override CatchBlock VisitCatchBlock(CatchBlock node) {
699             Out("catch (" + node.Test.Name);
700             if (node.Variable != null) {
701                 Out(node.Variable.Name ?? "");
702             }
703             Out(") { ... }");
704             return node;
705         }
706
707         protected internal override Expression VisitTry(TryExpression node) {
708             Out("try { ... }");
709             return node;
710         }
711
712         protected internal override Expression VisitIndex(IndexExpression node) {
713             if (node.Object != null) {
714                 Visit(node.Object);
715             } else {
716                 Debug.Assert(node.Indexer != null);
717                 Out(node.Indexer.DeclaringType.Name);
718             }
719             if (node.Indexer != null) {
720                 Out(".");
721                 Out(node.Indexer.Name);
722             }
723
724             VisitExpressions('[', node.Arguments, ']');
725             return node;
726         }
727
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());
734                 return node;
735             }
736
737             Out("[");
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);
742             } else {
743                 Out(node.NodeType.ToString());
744             }
745             Out("]");
746             return node;
747         }
748
749         private void DumpLabel(LabelTarget target) {
750             if (!String.IsNullOrEmpty(target.Name)) {
751                 Out(target.Name);
752             } else {
753                 int labelId = GetLabelId(target);
754                 Out("UnamedLabel_" + labelId);
755             }
756         }
757         #endregion
758     }
759 }