You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
960 lines
27 KiB
960 lines
27 KiB
#if NET20 || NET30 || !NET_4_6 |
|
|
|
// Copyright (c) Microsoft. All rights reserved. |
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information. |
|
|
|
using System.Collections.Generic; |
|
using System.Diagnostics; |
|
using System.Globalization; |
|
using System.Reflection; |
|
using System.Runtime.CompilerServices; |
|
using System.Text; |
|
using LinqInternal.Collections.ThreadSafe; |
|
using LinqInternal.Core; |
|
|
|
namespace System.Linq.Expressions.Reimplement |
|
{ |
|
internal sealed class ExpressionStringBuilder : ExpressionVisitor |
|
{ |
|
private readonly StringBuilder _out; |
|
|
|
// Associate every unique label or anonymous parameter in the tree with an integer. |
|
// The label is displayed as Label_#. |
|
private Dictionary<object, int> _ids; |
|
|
|
private ExpressionStringBuilder() |
|
{ |
|
_out = new StringBuilder(); |
|
} |
|
|
|
public override string ToString() |
|
{ |
|
return _out.ToString(); |
|
} |
|
|
|
private void AddLabel(LabelTarget label) |
|
{ |
|
if (_ids == null) |
|
{ |
|
_ids = new Dictionary<object, int> |
|
{ |
|
{ label, 0 } |
|
}; |
|
} |
|
else |
|
{ |
|
if (!_ids.ContainsKey(label)) |
|
{ |
|
_ids.Add(label, _ids.Count); |
|
} |
|
} |
|
} |
|
|
|
private int GetLabelId(LabelTarget label) |
|
{ |
|
if (_ids == null) |
|
{ |
|
_ids = new Dictionary<object, int>(); |
|
AddLabel(label); |
|
return 0; |
|
} |
|
else |
|
{ |
|
int id; |
|
if (!_ids.TryGetValue(label, out id)) |
|
{ |
|
//label is met the first time |
|
id = _ids.Count; |
|
AddLabel(label); |
|
} |
|
return id; |
|
} |
|
} |
|
|
|
private void AddParam(ParameterExpression p) |
|
{ |
|
if (_ids == null) |
|
{ |
|
_ids = new Dictionary<object, int> |
|
{ |
|
{ _ids, 0 } |
|
}; |
|
} |
|
else |
|
{ |
|
if (!_ids.ContainsKey(p)) |
|
{ |
|
_ids.Add(p, _ids.Count); |
|
} |
|
} |
|
} |
|
|
|
private int GetParamId(ParameterExpression p) |
|
{ |
|
if (_ids == null) |
|
{ |
|
_ids = new Dictionary<object, int>(); |
|
AddParam(p); |
|
return 0; |
|
} |
|
else |
|
{ |
|
int id; |
|
if (!_ids.TryGetValue(p, out id)) |
|
{ |
|
// p is met the first time |
|
id = _ids.Count; |
|
AddParam(p); |
|
} |
|
return id; |
|
} |
|
} |
|
|
|
#region The printing code |
|
|
|
private void Out(string s) |
|
{ |
|
_out.Append(s); |
|
} |
|
|
|
private void Out(char c) |
|
{ |
|
_out.Append(c); |
|
} |
|
|
|
#endregion The printing code |
|
|
|
#region Output an expresstion tree to a string |
|
|
|
internal static string ExpressionToString(Expression node) |
|
{ |
|
Debug.Assert(node != null); |
|
var esb = new ExpressionStringBuilder(); |
|
esb.Visit(node); |
|
return esb.ToString(); |
|
} |
|
|
|
internal static string CatchBlockToString(CatchBlock node) |
|
{ |
|
Debug.Assert(node != null); |
|
var esb = new ExpressionStringBuilder(); |
|
esb.VisitCatchBlock(node); |
|
return esb.ToString(); |
|
} |
|
|
|
internal static string SwitchCaseToString(SwitchCase node) |
|
{ |
|
Debug.Assert(node != null); |
|
var esb = new ExpressionStringBuilder(); |
|
esb.VisitSwitchCase(node); |
|
return esb.ToString(); |
|
} |
|
|
|
internal static string MemberBindingToString(MemberBinding node) |
|
{ |
|
Debug.Assert(node != null); |
|
var esb = new ExpressionStringBuilder(); |
|
esb.VisitMemberBinding(node); |
|
return esb.ToString(); |
|
} |
|
|
|
internal static string ElementInitBindingToString(ElementInit node) |
|
{ |
|
Debug.Assert(node != null); |
|
var esb = new ExpressionStringBuilder(); |
|
esb.VisitElementInit(node); |
|
return esb.ToString(); |
|
} |
|
|
|
private void VisitExpressions<T>(char open, IList<T> expressions, char close) where T : Expression |
|
{ |
|
VisitExpressions(open, expressions, close, ", "); |
|
} |
|
|
|
private void VisitExpressions<T>(char open, IList<T> expressions, char close, string seperator) where T : Expression |
|
{ |
|
Out(open); |
|
if (expressions != null) |
|
{ |
|
var isFirst = true; |
|
foreach (var e in expressions) |
|
{ |
|
if (isFirst) |
|
{ |
|
isFirst = false; |
|
} |
|
else |
|
{ |
|
Out(seperator); |
|
} |
|
Visit(e); |
|
} |
|
} |
|
Out(close); |
|
} |
|
|
|
protected internal override Expression VisitBinary(BinaryExpression node) |
|
{ |
|
if (node.NodeType == ExpressionType.ArrayIndex) |
|
{ |
|
Visit(node.Left); |
|
Out("["); |
|
Visit(node.Right); |
|
Out("]"); |
|
} |
|
else |
|
{ |
|
string op; |
|
switch (node.NodeType) |
|
{ |
|
// AndAlso and OrElse were unintentionally changed in |
|
// CLR 4. We changed them to "AndAlso" and "OrElse" to |
|
// be 3.5 compatible, but it turns out 3.5 shipped with |
|
// "&&" and "||". Oops. |
|
case ExpressionType.AndAlso: |
|
op = "AndAlso"; |
|
break; |
|
|
|
case ExpressionType.OrElse: |
|
op = "OrElse"; |
|
break; |
|
|
|
case ExpressionType.Assign: |
|
op = "="; |
|
break; |
|
|
|
case ExpressionType.Equal: |
|
op = "=="; |
|
break; |
|
|
|
case ExpressionType.NotEqual: |
|
op = "!="; |
|
break; |
|
|
|
case ExpressionType.GreaterThan: |
|
op = ">"; |
|
break; |
|
|
|
case ExpressionType.LessThan: |
|
op = "<"; |
|
break; |
|
|
|
case ExpressionType.GreaterThanOrEqual: |
|
op = ">="; |
|
break; |
|
|
|
case ExpressionType.LessThanOrEqual: |
|
op = "<="; |
|
break; |
|
|
|
case ExpressionType.Add: |
|
op = "+"; |
|
break; |
|
|
|
case ExpressionType.AddAssign: |
|
op = "+="; |
|
break; |
|
|
|
case ExpressionType.AddAssignChecked: |
|
op = "+="; |
|
break; |
|
|
|
case ExpressionType.AddChecked: |
|
op = "+"; |
|
break; |
|
|
|
case ExpressionType.Subtract: |
|
op = "-"; |
|
break; |
|
|
|
case ExpressionType.SubtractAssign: |
|
op = "-="; |
|
break; |
|
|
|
case ExpressionType.SubtractAssignChecked: |
|
op = "-="; |
|
break; |
|
|
|
case ExpressionType.SubtractChecked: |
|
op = "-"; |
|
break; |
|
|
|
case ExpressionType.Divide: |
|
op = "/"; |
|
break; |
|
|
|
case ExpressionType.DivideAssign: |
|
op = "/="; |
|
break; |
|
|
|
case ExpressionType.Modulo: |
|
op = "%"; |
|
break; |
|
|
|
case ExpressionType.ModuloAssign: |
|
op = "%="; |
|
break; |
|
|
|
case ExpressionType.Multiply: |
|
op = "*"; |
|
break; |
|
|
|
case ExpressionType.MultiplyAssign: |
|
op = "*="; |
|
break; |
|
|
|
case ExpressionType.MultiplyAssignChecked: |
|
op = "*="; |
|
break; |
|
|
|
case ExpressionType.MultiplyChecked: |
|
op = "*"; |
|
break; |
|
|
|
case ExpressionType.LeftShift: |
|
op = "<<"; |
|
break; |
|
|
|
case ExpressionType.LeftShiftAssign: |
|
op = "<<="; |
|
break; |
|
|
|
case ExpressionType.RightShift: |
|
op = ">>"; |
|
break; |
|
|
|
case ExpressionType.RightShiftAssign: |
|
op = ">>="; |
|
break; |
|
|
|
case ExpressionType.And: |
|
if (node.Type == typeof(bool) || node.Type == typeof(bool?)) |
|
{ |
|
op = "And"; |
|
} |
|
else |
|
{ |
|
op = "&"; |
|
} |
|
break; |
|
|
|
case ExpressionType.AndAssign: |
|
if (node.Type == typeof(bool) || node.Type == typeof(bool?)) |
|
{ |
|
op = "&&="; |
|
} |
|
else |
|
{ |
|
op = "&="; |
|
} |
|
break; |
|
|
|
case ExpressionType.Or: |
|
if (node.Type == typeof(bool) || node.Type == typeof(bool?)) |
|
{ |
|
op = "Or"; |
|
} |
|
else |
|
{ |
|
op = "|"; |
|
} |
|
break; |
|
|
|
case ExpressionType.OrAssign: |
|
if (node.Type == typeof(bool) || node.Type == typeof(bool?)) |
|
{ |
|
op = "||="; |
|
} |
|
else |
|
{ |
|
op = "|="; |
|
} |
|
break; |
|
|
|
case ExpressionType.ExclusiveOr: |
|
op = "^"; |
|
break; |
|
|
|
case ExpressionType.ExclusiveOrAssign: |
|
op = "^="; |
|
break; |
|
|
|
case ExpressionType.Power: |
|
op = "^"; |
|
break; |
|
|
|
case ExpressionType.PowerAssign: |
|
op = "**="; |
|
break; |
|
|
|
case ExpressionType.Coalesce: |
|
op = "??"; |
|
break; |
|
|
|
default: |
|
throw new InvalidOperationException(); |
|
} |
|
Out("("); |
|
Visit(node.Left); |
|
Out(' '); |
|
Out(op); |
|
Out(' '); |
|
Visit(node.Right); |
|
Out(")"); |
|
} |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitParameter(ParameterExpression node) |
|
{ |
|
if (node.IsByRef) |
|
{ |
|
Out("ref "); |
|
} |
|
var name = node.Name; |
|
if (string.IsNullOrEmpty(name)) |
|
{ |
|
Out("Param_" + GetParamId(node)); |
|
} |
|
else |
|
{ |
|
Out(name); |
|
} |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitLambda(LambdaExpression node) |
|
{ |
|
if (node.Parameters.Count == 1) |
|
{ |
|
// p => body |
|
Visit(node.Parameters[0]); |
|
} |
|
else |
|
{ |
|
// (p1, p2, ..., pn) => body |
|
VisitExpressions('(', node.Parameters, ')'); |
|
} |
|
Out(" => "); |
|
Visit(node.Body); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitListInit(ListInitExpression node) |
|
{ |
|
Visit(node.NewExpression); |
|
Out(" {"); |
|
var n = node.Initializers.Count; |
|
for (var i = 0; i < n; i++) |
|
{ |
|
if (i > 0) |
|
{ |
|
Out(", "); |
|
} |
|
Out(node.Initializers[i].ToString()); |
|
} |
|
Out("}"); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitConditional(ConditionalExpression node) |
|
{ |
|
Out("IIF("); |
|
Visit(node.Test); |
|
Out(", "); |
|
Visit(node.IfTrue); |
|
Out(", "); |
|
Visit(node.IfFalse); |
|
Out(")"); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitConstant(ConstantExpression node) |
|
{ |
|
if (node.Value != null) |
|
{ |
|
var sValue = node.Value.ToString(); |
|
if (node.Value is string) |
|
{ |
|
Out("\""); |
|
Out(sValue); |
|
Out("\""); |
|
} |
|
else if (sValue == node.Value.GetType().ToString()) |
|
{ |
|
Out("value("); |
|
Out(sValue); |
|
Out(")"); |
|
} |
|
else |
|
{ |
|
Out(sValue); |
|
} |
|
} |
|
else |
|
{ |
|
Out("null"); |
|
} |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitDebugInfo(DebugInfoExpression node) |
|
{ |
|
var s = string.Format( |
|
CultureInfo.CurrentCulture, |
|
"<DebugInfo({0}: {1}, {2}, {3}, {4})>", |
|
node.Document.FileName, |
|
node.StartLine, |
|
node.StartColumn, |
|
node.EndLine, |
|
node.EndColumn |
|
); |
|
Out(s); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) |
|
{ |
|
VisitExpressions('(', node.Variables, ')'); |
|
return node; |
|
} |
|
|
|
// Prints ".instanceField" or "declaringType.staticField" |
|
private void OutMember(Expression instance, MemberInfo member) |
|
{ |
|
if (instance != null) |
|
{ |
|
Visit(instance); |
|
Out("." + member.Name); |
|
} |
|
else |
|
{ |
|
// For static members, include the type name |
|
Out(member.DeclaringType.Name + "." + member.Name); |
|
} |
|
} |
|
|
|
protected internal override Expression VisitMember(MemberExpression node) |
|
{ |
|
OutMember(node.Expression, node.Member); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitMemberInit(MemberInitExpression node) |
|
{ |
|
if (node.NewExpression.Arguments.Count == 0 && |
|
node.NewExpression.Type.Name.Contains("<")) |
|
{ |
|
// anonymous type constructor |
|
Out("new"); |
|
} |
|
else |
|
{ |
|
Visit(node.NewExpression); |
|
} |
|
Out(" {"); |
|
var n = node.Bindings.Count; |
|
for (var i = 0; i < n; i++) |
|
{ |
|
var b = node.Bindings[i]; |
|
if (i > 0) |
|
{ |
|
Out(", "); |
|
} |
|
VisitMemberBinding(b); |
|
} |
|
Out("}"); |
|
return node; |
|
} |
|
|
|
protected override MemberAssignment VisitMemberAssignment(MemberAssignment node) |
|
{ |
|
Out(node.Member.Name); |
|
Out(" = "); |
|
Visit(node.Expression); |
|
return node; |
|
} |
|
|
|
protected override MemberListBinding VisitMemberListBinding(MemberListBinding node) |
|
{ |
|
Out(node.Member.Name); |
|
Out(" = {"); |
|
var n = node.Initializers.Count; |
|
for (var i = 0; i < n; i++) |
|
{ |
|
if (i > 0) |
|
{ |
|
Out(", "); |
|
} |
|
VisitElementInit(node.Initializers[i]); |
|
} |
|
Out("}"); |
|
return node; |
|
} |
|
|
|
protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding node) |
|
{ |
|
Out(node.Member.Name); |
|
Out(" = {"); |
|
var n = node.Bindings.Count; |
|
for (var i = 0; i < n; i++) |
|
{ |
|
if (i > 0) |
|
{ |
|
Out(", "); |
|
} |
|
VisitMemberBinding(node.Bindings[i]); |
|
} |
|
Out("}"); |
|
return node; |
|
} |
|
|
|
protected override ElementInit VisitElementInit(ElementInit node) |
|
{ |
|
Out(node.AddMethod.ToString()); |
|
const string sep = ", "; |
|
VisitExpressions('(', node.Arguments, ')', sep); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitInvocation(InvocationExpression node) |
|
{ |
|
Out("Invoke("); |
|
Visit(node.Expression); |
|
const string sep = ", "; |
|
var n = node.Arguments.Count; |
|
for (var i = 0; i < n; i++) |
|
{ |
|
Out(sep); |
|
Visit(node.Arguments[i]); |
|
} |
|
Out(")"); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitMethodCall(MethodCallExpression node) |
|
{ |
|
var start = 0; |
|
var ob = node.Object; |
|
|
|
if (node.Method.HasAttribute<ExtensionAttribute>()) |
|
{ |
|
start = 1; |
|
ob = node.Arguments[0]; |
|
} |
|
|
|
if (ob != null) |
|
{ |
|
Visit(ob); |
|
Out("."); |
|
} |
|
Out(node.Method.Name); |
|
Out("("); |
|
var n = node.Arguments.Count; |
|
for (var i = start; i < n; i++) |
|
{ |
|
if (i > start) |
|
{ |
|
Out(", "); |
|
} |
|
|
|
Visit(node.Arguments[i]); |
|
} |
|
Out(")"); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitNewArray(NewArrayExpression node) |
|
{ |
|
switch (node.NodeType) |
|
{ |
|
case ExpressionType.NewArrayBounds: |
|
// new MyType[](expr1, expr2) |
|
Out("new " + node.Type); |
|
VisitExpressions('(', node.Expressions, ')'); |
|
break; |
|
|
|
case ExpressionType.NewArrayInit: |
|
// new [] {expr1, expr2} |
|
Out("new [] "); |
|
VisitExpressions('{', node.Expressions, '}'); |
|
break; |
|
} |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitNew(NewExpression node) |
|
{ |
|
Out("new " + node.Type.Name); |
|
Out("("); |
|
var members = node.Members; |
|
for (var i = 0; i < node.Arguments.Count; i++) |
|
{ |
|
if (i > 0) |
|
{ |
|
Out(", "); |
|
} |
|
if (members != null) |
|
{ |
|
var name = members[i].Name; |
|
Out(name); |
|
Out(" = "); |
|
} |
|
Visit(node.Arguments[i]); |
|
} |
|
Out(")"); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitTypeBinary(TypeBinaryExpression node) |
|
{ |
|
Out("("); |
|
Visit(node.Expression); |
|
switch (node.NodeType) |
|
{ |
|
case ExpressionType.TypeIs: |
|
Out(" Is "); |
|
break; |
|
|
|
case ExpressionType.TypeEqual: |
|
Out(" TypeEqual "); |
|
break; |
|
} |
|
Out(node.TypeOperand.Name); |
|
Out(")"); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitUnary(UnaryExpression node) |
|
{ |
|
switch (node.NodeType) |
|
{ |
|
case ExpressionType.TypeAs: |
|
Out("("); |
|
break; |
|
|
|
case ExpressionType.Not: |
|
Out("Not("); |
|
break; |
|
|
|
case ExpressionType.Negate: |
|
case ExpressionType.NegateChecked: |
|
Out("-"); |
|
break; |
|
|
|
case ExpressionType.UnaryPlus: |
|
Out("+"); |
|
break; |
|
|
|
case ExpressionType.Quote: |
|
break; |
|
|
|
case ExpressionType.Throw: |
|
Out("throw("); |
|
break; |
|
|
|
case ExpressionType.Increment: |
|
Out("Increment("); |
|
break; |
|
|
|
case ExpressionType.Decrement: |
|
Out("Decrement("); |
|
break; |
|
|
|
case ExpressionType.PreIncrementAssign: |
|
Out("++"); |
|
break; |
|
|
|
case ExpressionType.PreDecrementAssign: |
|
Out("--"); |
|
break; |
|
|
|
case ExpressionType.OnesComplement: |
|
Out("~("); |
|
break; |
|
|
|
default: |
|
Out(node.NodeType.ToString()); |
|
Out("("); |
|
break; |
|
} |
|
|
|
Visit(node.Operand); |
|
|
|
switch (node.NodeType) |
|
{ |
|
case ExpressionType.Negate: |
|
case ExpressionType.NegateChecked: |
|
case ExpressionType.UnaryPlus: |
|
case ExpressionType.PreDecrementAssign: |
|
case ExpressionType.PreIncrementAssign: |
|
case ExpressionType.Quote: |
|
break; |
|
|
|
case ExpressionType.TypeAs: |
|
Out(" As "); |
|
Out(node.Type.Name); |
|
Out(")"); |
|
break; |
|
|
|
case ExpressionType.PostIncrementAssign: |
|
Out("++"); |
|
break; |
|
|
|
case ExpressionType.PostDecrementAssign: |
|
Out("--"); |
|
break; |
|
|
|
default: |
|
Out(")"); |
|
break; |
|
} |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitBlock(BlockExpression node) |
|
{ |
|
Out("{"); |
|
foreach (var v in node.Variables) |
|
{ |
|
Out("var "); |
|
Visit(v); |
|
Out(";"); |
|
} |
|
Out(" ... }"); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitDefault(DefaultExpression node) |
|
{ |
|
Out("default("); |
|
Out(node.Type.Name); |
|
Out(")"); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitLabel(LabelExpression node) |
|
{ |
|
Out("{ ... } "); |
|
DumpLabel(node.Target); |
|
Out(":"); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitGoto(GotoExpression node) |
|
{ |
|
Out(node.Kind.ToString().ToLower()); |
|
DumpLabel(node.Target); |
|
if (node.Value != null) |
|
{ |
|
Out(" ("); |
|
Visit(node.Value); |
|
Out(") "); |
|
} |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitLoop(LoopExpression node) |
|
{ |
|
Out("loop { ... }"); |
|
return node; |
|
} |
|
|
|
protected override SwitchCase VisitSwitchCase(SwitchCase node) |
|
{ |
|
Out("case "); |
|
VisitExpressions('(', node.TestValues, ')'); |
|
Out(": ..."); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitSwitch(SwitchExpression node) |
|
{ |
|
Out("switch "); |
|
Out("("); |
|
Visit(node.SwitchValue); |
|
Out(") { ... }"); |
|
return node; |
|
} |
|
|
|
protected override CatchBlock VisitCatchBlock(CatchBlock node) |
|
{ |
|
Out("catch (" + node.Test.Name); |
|
if (node.Variable != null) |
|
{ |
|
Out(node.Variable.Name ?? ""); |
|
} |
|
Out(") { ... }"); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitTry(TryExpression node) |
|
{ |
|
Out("try { ... }"); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitIndex(IndexExpression node) |
|
{ |
|
if (node.Object != null) |
|
{ |
|
Visit(node.Object); |
|
} |
|
else |
|
{ |
|
Debug.Assert(node.Indexer != null); |
|
Out(node.Indexer.DeclaringType.Name); |
|
} |
|
if (node.Indexer != null) |
|
{ |
|
Out("."); |
|
Out(node.Indexer.Name); |
|
} |
|
|
|
VisitExpressions('[', node.Arguments, ']'); |
|
return node; |
|
} |
|
|
|
protected internal override Expression VisitExtension(Expression node) |
|
{ |
|
// Prefer an overridden ToString, if available. |
|
var toString = node.GetType().GetMethod("ToString", ArrayReservoir<Type>.EmptyArray); |
|
if (toString.DeclaringType != typeof(Expression) && !toString.IsStatic) |
|
{ |
|
Out(node.ToString()); |
|
return node; |
|
} |
|
|
|
Out("["); |
|
// For 3.5 subclasses, print the NodeType. |
|
// For Extension nodes, print the class name. |
|
if (node.NodeType == ExpressionType.Extension) |
|
{ |
|
Out(node.GetType().FullName); |
|
} |
|
else |
|
{ |
|
Out(node.NodeType.ToString()); |
|
} |
|
Out("]"); |
|
return node; |
|
} |
|
|
|
private void DumpLabel(LabelTarget target) |
|
{ |
|
if (!string.IsNullOrEmpty(target.Name)) |
|
{ |
|
Out(target.Name); |
|
} |
|
else |
|
{ |
|
var labelId = GetLabelId(target); |
|
Out("UnamedLabel_" + labelId); |
|
} |
|
} |
|
|
|
#endregion Output an expresstion tree to a string |
|
} |
|
} |
|
|
|
#endif |