|
/* ****************************************************************************
*
* Copyright (c) Microsoft Corporation.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0. A
* copy of the license can be found in the License.html file at the root of this distribution. If
* you cannot locate the Apache License, Version 2.0, please send an email to
* dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
* by the terms of the Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*
*
* ***************************************************************************/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.Dynamic.Utils;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
#if CLR2
namespace Microsoft.Scripting.Ast {
#else
namespace System.Linq.Expressions {
#endif
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
internal sealed class ExpressionStringBuilder : ExpressionVisitor {
private 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>();
_ids.Add(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.Add(_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
#region Output an expresstion tree to a string
/// <summary>
/// Output a given expression tree to a string.
/// </summary>
internal static string ExpressionToString(Expression node) {
Debug.Assert(node != null);
ExpressionStringBuilder esb = new ExpressionStringBuilder();
esb.Visit(node);
return esb.ToString();
}
internal static string CatchBlockToString(CatchBlock node) {
Debug.Assert(node != null);
ExpressionStringBuilder esb = new ExpressionStringBuilder();
esb.VisitCatchBlock(node);
return esb.ToString();
}
internal static string SwitchCaseToString(SwitchCase node) {
Debug.Assert(node != null);
ExpressionStringBuilder esb = new ExpressionStringBuilder();
esb.VisitSwitchCase(node);
return esb.ToString();
}
/// <summary>
/// Output a given member binding to a string.
/// </summary>
internal static string MemberBindingToString(MemberBinding node) {
Debug.Assert(node != null);
ExpressionStringBuilder esb = new ExpressionStringBuilder();
esb.VisitMemberBinding(node);
return esb.ToString();
}
/// <summary>
/// Output a given ElementInit to a string.
/// </summary>
internal static string ElementInitBindingToString(ElementInit node) {
Debug.Assert(node != null);
ExpressionStringBuilder esb = new ExpressionStringBuilder();
esb.VisitElementInit(node);
return esb.ToString();
}
// More proper would be to make this a virtual method on Action
private static string FormatBinder(CallSiteBinder binder) {
ConvertBinder convert;
GetMemberBinder getMember;
SetMemberBinder setMember;
DeleteMemberBinder deleteMember;
InvokeMemberBinder call;
UnaryOperationBinder unary;
BinaryOperationBinder binary;
if ((convert = binder as ConvertBinder) != null) {
return "Convert " + convert.Type;
} else if ((getMember = binder as GetMemberBinder) != null) {
return "GetMember " + getMember.Name;
} else if ((setMember = binder as SetMemberBinder) != null) {
return "SetMember " + setMember.Name;
} else if ((deleteMember = binder as DeleteMemberBinder) != null) {
return "DeleteMember " + deleteMember.Name;
} else if (binder is GetIndexBinder) {
return "GetIndex";
} else if (binder is SetIndexBinder) {
return "SetIndex";
} else if (binder is DeleteIndexBinder) {
return "DeleteIndex";
} else if ((call = binder as InvokeMemberBinder) != null) {
return "Call " + call.Name;
} else if (binder is InvokeBinder) {
return "Invoke";
} else if (binder is CreateInstanceBinder) {
return "Create";
} else if ((unary = binder as UnaryOperationBinder) != null) {
return unary.Operation.ToString();
} else if ((binary = binder as BinaryOperationBinder) != null) {
return binary.Operation.ToString();
} else {
return "CallSiteBinder";
}
}
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) {
bool isFirst = true;
foreach (T e in expressions) {
if (isFirst) {
isFirst = false;
} else {
Out(seperator);
}
Visit(e);
}
}
Out(close);
}
protected internal override Expression VisitDynamic(DynamicExpression node) {
Out(FormatBinder(node.Binder));
VisitExpressions('(', node.Arguments, ')');
return node;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
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";
#if SILVERLIGHT
if (Expression.SilverlightQuirks) op = "&&";
#endif
break;
case ExpressionType.OrElse:
op = "OrElse";
#if SILVERLIGHT
if (Expression.SilverlightQuirks) op = "||";
#endif
break;
case ExpressionType.Assign: op = "="; break;
case ExpressionType.Equal:
op = "==";
#if SILVERLIGHT
if (Expression.SilverlightQuirks) op = "=";
#endif
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 ");
}
string name = node.Name;
if (String.IsNullOrEmpty(name)) {
#if SILVERLIGHT
if (Expression.SilverlightQuirks) {
Out(name ?? "<param>");
return node;
}
#endif
Out("Param_" + GetParamId(node));
} else {
Out(name);
}
return node;
}
protected internal override Expression VisitLambda<T>(Expression<T> 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(" {");
for (int i = 0, n = node.Initializers.Count; 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) {
string 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) {
string 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(" {");
for (int i = 0, n = node.Bindings.Count; i < n; i++) {
MemberBinding b = node.Bindings[i];
if (i > 0) {
Out(", ");
}
VisitMemberBinding(b);
}
Out("}");
return node;
}
protected override MemberAssignment VisitMemberAssignment(MemberAssignment assignment) {
Out(assignment.Member.Name);
Out(" = ");
Visit(assignment.Expression);
return assignment;
}
protected override MemberListBinding VisitMemberListBinding(MemberListBinding binding) {
Out(binding.Member.Name);
Out(" = {");
for (int i = 0, n = binding.Initializers.Count; i < n; i++) {
if (i > 0) {
Out(", ");
}
VisitElementInit(binding.Initializers[i]);
}
Out("}");
return binding;
}
protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding binding) {
Out(binding.Member.Name);
Out(" = {");
for (int i = 0, n = binding.Bindings.Count; i < n; i++) {
if (i > 0) {
Out(", ");
}
VisitMemberBinding(binding.Bindings[i]);
}
Out("}");
return binding;
}
protected override ElementInit VisitElementInit(ElementInit initializer) {
Out(initializer.AddMethod.ToString());
string sep = ", ";
#if SILVERLIGHT
if (Expression.SilverlightQuirks) sep = ",";
#endif
VisitExpressions('(', initializer.Arguments, ')', sep);
return initializer;
}
protected internal override Expression VisitInvocation(InvocationExpression node) {
Out("Invoke(");
Visit(node.Expression);
string sep = ", ";
#if SILVERLIGHT
if (Expression.SilverlightQuirks) sep = ",";
#endif
for (int i = 0, n = node.Arguments.Count; i < n; i++) {
Out(sep);
Visit(node.Arguments[i]);
}
Out(")");
return node;
}
protected internal override Expression VisitMethodCall(MethodCallExpression node) {
int start = 0;
Expression ob = node.Object;
if (Attribute.GetCustomAttribute(node.Method, typeof(ExtensionAttribute)) != null) {
start = 1;
ob = node.Arguments[0];
}
if (ob != null) {
Visit(ob);
Out(".");
}
Out(node.Method.Name);
Out("(");
for (int i = start, n = node.Arguments.Count; 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.ToString());
VisitExpressions('(', node.Expressions, ')');
break;
case ExpressionType.NewArrayInit:
// new [] {expr1, expr2}
Out("new [] ");
VisitExpressions('{', node.Expressions, '}');
break;
}
return node;
}
#if SILVERLIGHT
private static PropertyInfo GetPropertyNoThrow(MethodInfo method) {
if (method == null)
return null;
Type type = method.DeclaringType;
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic;
flags |= (method.IsStatic) ? BindingFlags.Static : BindingFlags.Instance;
PropertyInfo[] props = type.GetProperties(flags);
foreach (PropertyInfo pi in props) {
if (pi.CanRead && method == pi.GetGetMethod(true)) {
return pi;
}
if (pi.CanWrite && method == pi.GetSetMethod(true)) {
return pi;
}
}
return null;
}
#endif
protected internal override Expression VisitNew(NewExpression node) {
Out("new " + node.Type.Name);
Out("(");
var members = node.Members;
for (int i = 0; i < node.Arguments.Count; i++) {
if (i > 0) {
Out(", ");
}
if (members != null) {
string name = members[i].Name;
#if SILVERLIGHT
// Members can be the get/set methods rather than the fields/properties
PropertyInfo pi = null;
if (Expression.SilverlightQuirks &&
members[i].MemberType == MemberTypes.Method &&
(pi = GetPropertyNoThrow((MethodInfo)members[i])) != null) {
name = pi.Name;
}
#endif
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;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")]
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(CultureInfo.CurrentCulture));
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 overriden ToString, if available.
var flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.ExactBinding;
var toString = node.GetType().GetMethod("ToString", flags, null, Type.EmptyTypes, null);
if (toString.DeclaringType != typeof(Expression)) {
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 {
int labelId = GetLabelId(target);
Out("UnamedLabel_" + labelId);
}
}
#endregion
}
}
|