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
5 years ago
|
#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
|