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.
344 lines
12 KiB
344 lines
12 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.Collections.ObjectModel; |
|
using System.Dynamic.Utils; |
|
using System.Globalization; |
|
using System.Reflection; |
|
using System.Runtime.CompilerServices; |
|
using System.Threading; |
|
using LinqInternal.Core; |
|
|
|
namespace System.Linq.Expressions.Reimplement |
|
{ |
|
/// <summary> |
|
/// The base type for all nodes in Expression Trees. |
|
/// </summary> |
|
public abstract partial class Expression |
|
{ |
|
private static readonly CacheDict<Type, MethodInfo> _lambdaDelegateCache = new CacheDict<Type, MethodInfo>(40); |
|
private static volatile CacheDict<Type, Func<Expression, string, bool, ReadOnlyCollection<ParameterExpression>, LambdaExpression>> _lambdaFactories; |
|
|
|
// For 4.0, many frequently used Expression nodes have had their memory |
|
// footprint reduced by removing the Type and NodeType fields. This has |
|
// large performance benefits to all users of Expression Trees. |
|
// |
|
// To support the 3.5 protected constructor, we store the fields that |
|
// used to be here in a ConditionalWeakTable. |
|
|
|
private class ExtensionInfo |
|
{ |
|
public ExtensionInfo(ExpressionType nodeType, Type type) |
|
{ |
|
NodeType = nodeType; |
|
Type = type; |
|
} |
|
|
|
internal readonly ExpressionType NodeType; |
|
internal readonly Type Type; |
|
} |
|
|
|
private static ConditionalWeakTable<Expression, ExtensionInfo> _legacyCtorSupportTable; |
|
|
|
/// <summary> |
|
/// Constructs a new instance of <see cref="Expression"/>. |
|
/// </summary> |
|
/// <param name="nodeType">The <see ctype="ExpressionType"/> of the <see cref="Expression"/>.</param> |
|
/// <param name="type">The <see cref="Type"/> of the <see cref="Expression"/>.</param> |
|
[Obsolete("use a different constructor that does not take ExpressionType. Then override NodeType and Type properties to provide the values that would be specified to this constructor.")] |
|
protected Expression(ExpressionType nodeType, Type type) |
|
{ |
|
// Can't enforce anything that V1 didn't |
|
if (_legacyCtorSupportTable == null) |
|
{ |
|
Interlocked.CompareExchange( |
|
ref _legacyCtorSupportTable, |
|
new ConditionalWeakTable<Expression, ExtensionInfo>(), |
|
null |
|
); |
|
} |
|
|
|
_legacyCtorSupportTable.Add(this, new ExtensionInfo(nodeType, type)); |
|
} |
|
|
|
/// <summary> |
|
/// Constructs a new instance of <see cref="Expression"/>. |
|
/// </summary> |
|
protected Expression() |
|
{ |
|
} |
|
|
|
/// <summary> |
|
/// The <see cref="ExpressionType"/> of the <see cref="Expression"/>. |
|
/// </summary> |
|
public virtual ExpressionType NodeType |
|
{ |
|
get |
|
{ |
|
ExtensionInfo extInfo; |
|
if (_legacyCtorSupportTable != null && _legacyCtorSupportTable.TryGetValue(this, out extInfo)) |
|
{ |
|
return extInfo.NodeType; |
|
} |
|
|
|
// the extension expression failed to override NodeType |
|
throw Error.ExtensionNodeMustOverrideProperty("Expression.NodeType"); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// The <see cref="Type"/> of the value represented by this <see cref="Expression"/>. |
|
/// </summary> |
|
public virtual Type Type |
|
{ |
|
get |
|
{ |
|
ExtensionInfo extInfo; |
|
if (_legacyCtorSupportTable != null && _legacyCtorSupportTable.TryGetValue(this, out extInfo)) |
|
{ |
|
return extInfo.Type; |
|
} |
|
|
|
// the extension expression failed to override Type |
|
throw Error.ExtensionNodeMustOverrideProperty("Expression.Type"); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Indicates that the node can be reduced to a simpler node. If this |
|
/// returns true, Reduce() can be called to produce the reduced form. |
|
/// </summary> |
|
public virtual bool CanReduce |
|
{ |
|
get { return false; } |
|
} |
|
|
|
/// <summary> |
|
/// Reduces this node to a simpler expression. If CanReduce returns |
|
/// true, this should return a valid expression. This method is |
|
/// allowed to return another node which itself must be reduced. |
|
/// </summary> |
|
/// <returns>The reduced expression.</returns> |
|
public virtual Expression Reduce() |
|
{ |
|
if (CanReduce) |
|
{ |
|
throw Error.ReducibleMustOverrideReduce(); |
|
} |
|
|
|
return this; |
|
} |
|
|
|
/// <summary> |
|
/// Reduces the node and then calls the visitor delegate on the reduced expression. |
|
/// Throws an exception if the node isn't reducible. |
|
/// </summary> |
|
/// <param name="visitor">An instance of <see cref="Func{Expression, Expression}"/>.</param> |
|
/// <returns>The expression being visited, or an expression which should replace it in the tree.</returns> |
|
/// <remarks> |
|
/// Override this method to provide logic to walk the node's children. |
|
/// A typical implementation will call visitor.Visit on each of its |
|
/// children, and if any of them change, should return a new copy of |
|
/// itself with the modified children. |
|
/// </remarks> |
|
protected internal virtual Expression VisitChildren(ExpressionVisitor visitor) |
|
{ |
|
if (!CanReduce) |
|
{ |
|
throw Error.MustBeReducible(); |
|
} |
|
|
|
return visitor.Visit(ReduceAndCheck()); |
|
} |
|
|
|
/// <summary> |
|
/// Dispatches to the specific visit method for this node type. For |
|
/// example, <see cref="MethodCallExpression" /> will call into |
|
/// <see cref="ExpressionVisitor.VisitMethodCall" />. |
|
/// </summary> |
|
/// <param name="visitor">The visitor to visit this node with.</param> |
|
/// <returns>The result of visiting this node.</returns> |
|
/// <remarks> |
|
/// This default implementation for <see cref="ExpressionType.Extension" /> |
|
/// nodes will call <see cref="ExpressionVisitor.VisitExtension" />. |
|
/// Override this method to call into a more specific method on a derived |
|
/// visitor class of ExprressionVisitor. However, it should still |
|
/// support unknown visitors by calling VisitExtension. |
|
/// </remarks> |
|
protected internal virtual Expression Accept(ExpressionVisitor visitor) |
|
{ |
|
return visitor.VisitExtension(this); |
|
} |
|
|
|
/// <summary> |
|
/// Reduces this node to a simpler expression. If CanReduce returns |
|
/// true, this should return a valid expression. This method is |
|
/// allowed to return another node which itself must be reduced. |
|
/// </summary> |
|
/// <returns>The reduced expression.</returns> |
|
/// <remarks > |
|
/// Unlike Reduce, this method checks that the reduced node satisfies |
|
/// certain invariants. |
|
/// </remarks> |
|
public Expression ReduceAndCheck() |
|
{ |
|
if (!CanReduce) |
|
{ |
|
throw Error.MustBeReducible(); |
|
} |
|
|
|
var newNode = Reduce(); |
|
|
|
// 1. Reduction must return a new, non-null node |
|
// 2. Reduction must return a new node whose result type can be assigned to the type of the original node |
|
if (newNode == null || newNode == this) |
|
{ |
|
throw Error.MustReduceToDifferent(); |
|
} |
|
|
|
if (!TypeHelper.AreReferenceAssignable(Type, newNode.Type)) |
|
{ |
|
throw Error.ReducedNotCompatible(); |
|
} |
|
|
|
return newNode; |
|
} |
|
|
|
/// <summary> |
|
/// Reduces the expression to a known node type (i.e. not an Extension node) |
|
/// or simply returns the expression if it is already a known type. |
|
/// </summary> |
|
/// <returns>The reduced expression.</returns> |
|
public Expression ReduceExtensions() |
|
{ |
|
var node = this; |
|
while (node.NodeType == ExpressionType.Extension) |
|
{ |
|
node = node.ReduceAndCheck(); |
|
} |
|
return node; |
|
} |
|
|
|
/// <summary> |
|
/// Creates a <see cref="string"/> representation of the Expression. |
|
/// </summary> |
|
/// <returns>A <see cref="string"/> representation of the Expression.</returns> |
|
public override string ToString() |
|
{ |
|
return ExpressionStringBuilder.ExpressionToString(this); |
|
} |
|
|
|
/// <summary> |
|
/// Creates a <see cref="string"/> representation of the Expression. |
|
/// </summary> |
|
/// <returns>A <see cref="string"/> representation of the Expression.</returns> |
|
public string DebugView |
|
{ |
|
get |
|
{ |
|
using (var writer = new IO.StringWriter(CultureInfo.CurrentCulture)) |
|
{ |
|
DebugViewWriter.WriteTo(this, writer); |
|
return writer.ToString(); |
|
} |
|
} |
|
} |
|
|
|
public static ReadOnlyCollection<T> ReturnReadOnly<T>(ref IList<T> collection) |
|
{ |
|
return ExpressionUtils.ReturnReadOnly(ref collection); |
|
} |
|
|
|
internal static ReadOnlyCollection<Expression> ReturnReadOnly(IArgumentProvider provider, ref object collection) |
|
{ |
|
return ExpressionUtils.ReturnReadOnly(provider, ref collection); |
|
} |
|
|
|
public static T ReturnObject<T>(object collectionOrT) where T : class |
|
{ |
|
return ExpressionUtils.ReturnObject<T>(collectionOrT); |
|
} |
|
|
|
public static void RequiresCanRead(Expression expression, string paramName) |
|
{ |
|
ExpressionUtils.RequiresCanRead(expression, paramName); |
|
} |
|
|
|
public static void RequiresCanRead(IEnumerable<Expression> items, string paramName) |
|
{ |
|
if (items != null) |
|
{ |
|
// this is called a lot, avoid allocating an enumerator if we can... |
|
var listItems = items as IList<Expression>; |
|
if (listItems != null) |
|
{ |
|
foreach (var item in listItems) |
|
{ |
|
RequiresCanRead(item, paramName); |
|
} |
|
return; |
|
} |
|
|
|
foreach (var i in items) |
|
{ |
|
RequiresCanRead(i, paramName); |
|
} |
|
} |
|
} |
|
|
|
private static void RequiresCanWrite(Expression expression, string paramName) |
|
{ |
|
if (expression == null) |
|
{ |
|
throw new ArgumentNullException(paramName); |
|
} |
|
|
|
var canWrite = false; |
|
switch (expression.NodeType) |
|
{ |
|
case ExpressionType.Index: |
|
var index = (IndexExpression)expression; |
|
if (index.Indexer != null) |
|
{ |
|
canWrite = index.Indexer.CanWrite; |
|
} |
|
else |
|
{ |
|
canWrite = true; |
|
} |
|
break; |
|
|
|
case ExpressionType.MemberAccess: |
|
var member = (MemberExpression)expression; |
|
var prop = member.Member as PropertyInfo; |
|
if (prop != null) |
|
{ |
|
canWrite = prop.CanWrite; |
|
} |
|
else |
|
{ |
|
var field = member.Member as FieldInfo; |
|
if (field != null) |
|
{ |
|
canWrite = !(field.IsInitOnly || field.IsLiteral); |
|
} |
|
} |
|
break; |
|
|
|
case ExpressionType.Parameter: |
|
canWrite = true; |
|
break; |
|
} |
|
|
|
if (!canWrite) |
|
{ |
|
throw new ArgumentException(Strings.ExpressionMustBeWriteable, paramName); |
|
} |
|
} |
|
} |
|
} |
|
|
|
#endif |