#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 { /// /// The base type for all nodes in Expression Trees. /// public abstract partial class Expression { private static readonly CacheDict _lambdaDelegateCache = new CacheDict(40); private static volatile CacheDict, 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 _legacyCtorSupportTable; /// /// Constructs a new instance of . /// /// The of the . /// The of the . [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(), null ); } _legacyCtorSupportTable.Add(this, new ExtensionInfo(nodeType, type)); } /// /// Constructs a new instance of . /// protected Expression() { } /// /// The of the . /// 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"); } } /// /// The of the value represented by this . /// 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"); } } /// /// Indicates that the node can be reduced to a simpler node. If this /// returns true, Reduce() can be called to produce the reduced form. /// public virtual bool CanReduce { get { return false; } } /// /// 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. /// /// The reduced expression. public virtual Expression Reduce() { if (CanReduce) { throw Error.ReducibleMustOverrideReduce(); } return this; } /// /// Reduces the node and then calls the visitor delegate on the reduced expression. /// Throws an exception if the node isn't reducible. /// /// An instance of . /// The expression being visited, or an expression which should replace it in the tree. /// /// 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. /// protected internal virtual Expression VisitChildren(ExpressionVisitor visitor) { if (!CanReduce) { throw Error.MustBeReducible(); } return visitor.Visit(ReduceAndCheck()); } /// /// Dispatches to the specific visit method for this node type. For /// example, will call into /// . /// /// The visitor to visit this node with. /// The result of visiting this node. /// /// This default implementation for /// nodes will call . /// 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. /// protected internal virtual Expression Accept(ExpressionVisitor visitor) { return visitor.VisitExtension(this); } /// /// 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. /// /// The reduced expression. /// /// Unlike Reduce, this method checks that the reduced node satisfies /// certain invariants. /// 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; } /// /// 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. /// /// The reduced expression. public Expression ReduceExtensions() { var node = this; while (node.NodeType == ExpressionType.Extension) { node = node.ReduceAndCheck(); } return node; } /// /// Creates a representation of the Expression. /// /// A representation of the Expression. public override string ToString() { return ExpressionStringBuilder.ExpressionToString(this); } /// /// Creates a representation of the Expression. /// /// A representation of the Expression. public string DebugView { get { using (var writer = new IO.StringWriter(CultureInfo.CurrentCulture)) { DebugViewWriter.WriteTo(this, writer); return writer.ToString(); } } } public static ReadOnlyCollection ReturnReadOnly(ref IList collection) { return ExpressionUtils.ReturnReadOnly(ref collection); } internal static ReadOnlyCollection ReturnReadOnly(IArgumentProvider provider, ref object collection) { return ExpressionUtils.ReturnReadOnly(provider, ref collection); } public static T ReturnObject(object collectionOrT) where T : class { return ExpressionUtils.ReturnObject(collectionOrT); } public static void RequiresCanRead(Expression expression, string paramName) { ExpressionUtils.RequiresCanRead(expression, paramName); } public static void RequiresCanRead(IEnumerable items, string paramName) { if (items != null) { // this is called a lot, avoid allocating an enumerator if we can... var listItems = items as IList; 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