1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
6 * copy of the license can be found in the License.html file at the root of this distribution. If
7 * you cannot locate the Apache License, Version 2.0, please send an email to
8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
9 * by the terms of the Apache License, Version 2.0.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
17 using System.Collections.Generic;
18 using System.Collections.ObjectModel;
19 using System.Dynamic.Utils;
20 using System.Globalization;
22 using System.Reflection;
23 using System.Runtime.CompilerServices;
24 using System.Threading;
27 namespace Microsoft.Scripting.Ast {
28 using Microsoft.Scripting.Utils;
30 namespace System.Linq.Expressions {
33 /// The base type for all nodes in Expression Trees.
35 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
36 public abstract partial class Expression {
37 private delegate LambdaExpression LambdaFactory(Expression body, string name, bool tailCall, ReadOnlyCollection<ParameterExpression> parameters);
39 private static readonly CacheDict<Type, MethodInfo> _LambdaDelegateCache = new CacheDict<Type, MethodInfo>(40);
40 private static CacheDict<Type, LambdaFactory> _LambdaFactories;
42 // LINQ protected ctor from 3.5
44 // needs ConditionWeakTable in 4.0
46 // For 4.0, many frequently used Expression nodes have had their memory
47 // footprint reduced by removing the Type and NodeType fields. This has
48 // large performance benefits to all users of Expression Trees.
50 // To support the 3.5 protected constructor, we store the fields that
51 // used to be here in a ConditionalWeakTable.
53 private class ExtensionInfo {
54 public ExtensionInfo(ExpressionType nodeType, Type type) {
59 internal readonly ExpressionType NodeType;
60 internal readonly Type Type;
63 private static ConditionalWeakTable<Expression, ExtensionInfo> _legacyCtorSupportTable;
66 /// Constructs a new instance of <see cref="Expression"/>.
68 /// <param name="nodeType">The <see ctype="ExpressionType"/> of the <see cref="Expression"/>.</param>
69 /// <param name="type">The <see cref="Type"/> of the <see cref="Expression"/>.</param>
70 [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.")]
71 protected Expression(ExpressionType nodeType, Type type) {
72 // Can't enforce anything that V1 didn't
73 if (_legacyCtorSupportTable == null) {
74 Interlocked.CompareExchange(
75 ref _legacyCtorSupportTable,
76 new ConditionalWeakTable<Expression, ExtensionInfo>(),
81 _legacyCtorSupportTable.Add(this, new ExtensionInfo(nodeType, type));
85 /// Constructs a new instance of <see cref="Expression"/>.
87 protected Expression() {
91 /// The <see cref="ExpressionType"/> of the <see cref="Expression"/>.
93 public virtual ExpressionType NodeType {
95 ExtensionInfo extInfo;
96 if (_legacyCtorSupportTable != null && _legacyCtorSupportTable.TryGetValue(this, out extInfo)) {
97 return extInfo.NodeType;
100 // the extension expression failed to override NodeType
101 throw Error.ExtensionNodeMustOverrideProperty("Expression.NodeType");
107 /// The <see cref="Type"/> of the value represented by this <see cref="Expression"/>.
109 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]
110 public virtual Type Type {
112 ExtensionInfo extInfo;
113 if (_legacyCtorSupportTable != null && _legacyCtorSupportTable.TryGetValue(this, out extInfo)) {
117 // the extension expression failed to override Type
118 throw Error.ExtensionNodeMustOverrideProperty("Expression.Type");
123 /// Indicates that the node can be reduced to a simpler node. If this
124 /// returns true, Reduce() can be called to produce the reduced form.
126 public virtual bool CanReduce {
127 get { return false; }
131 /// Reduces this node to a simpler expression. If CanReduce returns
132 /// true, this should return a valid expression. This method is
133 /// allowed to return another node which itself must be reduced.
135 /// <returns>The reduced expression.</returns>
136 public virtual Expression Reduce() {
137 if (CanReduce) throw Error.ReducibleMustOverrideReduce();
142 /// Reduces the node and then calls the visitor delegate on the reduced expression.
143 /// Throws an exception if the node isn't reducible.
145 /// <param name="visitor">An instance of <see cref="Func{Expression, Expression}"/>.</param>
146 /// <returns>The expression being visited, or an expression which should replace it in the tree.</returns>
148 /// Override this method to provide logic to walk the node's children.
149 /// A typical implementation will call visitor.Visit on each of its
150 /// children, and if any of them change, should return a new copy of
151 /// itself with the modified children.
153 protected internal virtual Expression VisitChildren(ExpressionVisitor visitor) {
154 if (!CanReduce) throw Error.MustBeReducible();
155 return visitor.Visit(ReduceAndCheck());
159 /// Dispatches to the specific visit method for this node type. For
160 /// example, <see cref="MethodCallExpression" /> will call into
161 /// <see cref="ExpressionVisitor.VisitMethodCall" />.
163 /// <param name="visitor">The visitor to visit this node with.</param>
164 /// <returns>The result of visiting this node.</returns>
166 /// This default implementation for <see cref="ExpressionType.Extension" />
167 /// nodes will call <see cref="ExpressionVisitor.VisitExtension" />.
168 /// Override this method to call into a more specific method on a derived
169 /// visitor class of ExprressionVisitor. However, it should still
170 /// support unknown visitors by calling VisitExtension.
172 protected internal virtual Expression Accept(ExpressionVisitor visitor) {
173 return visitor.VisitExtension(this);
177 /// Reduces this node to a simpler expression. If CanReduce returns
178 /// true, this should return a valid expression. This method is
179 /// allowed to return another node which itself must be reduced.
181 /// <returns>The reduced expression.</returns>
183 /// Unlike Reduce, this method checks that the reduced node satisfies
184 /// certain invariants.
186 public Expression ReduceAndCheck() {
187 if (!CanReduce) throw Error.MustBeReducible();
189 var newNode = Reduce();
191 // 1. Reduction must return a new, non-null node
192 // 2. Reduction must return a new node whose result type can be assigned to the type of the original node
193 if (newNode == null || newNode == this) throw Error.MustReduceToDifferent();
194 if (!TypeUtils.AreReferenceAssignable(Type, newNode.Type)) throw Error.ReducedNotCompatible();
199 /// Reduces the expression to a known node type (i.e. not an Extension node)
200 /// or simply returns the expression if it is already a known type.
202 /// <returns>The reduced expression.</returns>
203 public Expression ReduceExtensions() {
205 while (node.NodeType == ExpressionType.Extension) {
206 node = node.ReduceAndCheck();
213 /// Creates a <see cref="String"/> representation of the Expression.
215 /// <returns>A <see cref="String"/> representation of the Expression.</returns>
216 public override string ToString() {
217 return ExpressionStringBuilder.ExpressionToString(this);
220 #if !FEATURE_CORE_DLR
222 /// Writes a <see cref="String"/> representation of the <see cref="Expression"/> to a <see cref="TextWriter"/>.
224 /// <param name="writer">A <see cref="TextWriter"/> that will be used to build the string representation.</param>
225 public void DumpExpression(TextWriter writer) {
226 DebugViewWriter.WriteTo(this, writer);
230 /// Creates a <see cref="String"/> representation of the Expression.
232 /// <returns>A <see cref="String"/> representation of the Expression.</returns>
233 public string DebugView {
235 private string DebugView {
238 using (System.IO.StringWriter writer = new System.IO.StringWriter(CultureInfo.CurrentCulture)) {
239 DebugViewWriter.WriteTo(this, writer);
240 return writer.ToString();
246 /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T.
248 /// This is called from various methods where we internally hold onto an IList of T
249 /// or a readonly collection of T. We check to see if we've already returned a
250 /// readonly collection of T and if so simply return the other one. Otherwise we do
251 /// a thread-safe replacement of the list w/ a readonly collection which wraps it.
253 /// Ultimately this saves us from having to allocate a ReadOnlyCollection for our
254 /// data types because the compiler is capable of going directly to the IList of T.
256 internal static ReadOnlyCollection<T> ReturnReadOnly<T>(ref IList<T> collection) {
257 IList<T> value = collection;
259 // if it's already read-only just return it.
260 ReadOnlyCollection<T> res = value as ReadOnlyCollection<T>;
265 // otherwise make sure only readonly collection every gets exposed
266 Interlocked.CompareExchange<IList<T>>(
273 return (ReadOnlyCollection<T>)collection;
277 /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T.
279 /// This is similar to the ReturnReadOnly of T. This version supports nodes which hold
280 /// onto multiple Expressions where one is typed to object. That object field holds either
281 /// an expression or a ReadOnlyCollection of Expressions. When it holds a ReadOnlyCollection
282 /// the IList which backs it is a ListArgumentProvider which uses the Expression which
283 /// implements IArgumentProvider to get 2nd and additional values. The ListArgumentProvider
284 /// continues to hold onto the 1st expression.
286 /// This enables users to get the ReadOnlyCollection w/o it consuming more memory than if
287 /// it was just an array. Meanwhile The DLR internally avoids accessing which would force
288 /// the readonly collection to be created resulting in a typical memory savings.
290 internal static ReadOnlyCollection<Expression> ReturnReadOnly(IArgumentProvider provider, ref object collection) {
291 Expression tObj = collection as Expression;
293 // otherwise make sure only one readonly collection ever gets exposed
294 Interlocked.CompareExchange(
296 new ReadOnlyCollection<Expression>(new ListArgumentProvider(provider, tObj)),
301 // and return what is not guaranteed to be a readonly collection
302 return (ReadOnlyCollection<Expression>)collection;
306 /// Helper which is used for specialized subtypes which use ReturnReadOnly(ref object, ...).
307 /// This is the reverse version of ReturnReadOnly which takes an IArgumentProvider.
309 /// This is used to return the 1st argument. The 1st argument is typed as object and either
310 /// contains a ReadOnlyCollection or the Expression. We check for the Expression and if it's
311 /// present we return that, otherwise we return the 1st element of the ReadOnlyCollection.
313 internal static T ReturnObject<T>(object collectionOrT) where T : class {
314 T t = collectionOrT as T;
319 return ((ReadOnlyCollection<T>)collectionOrT)[0];
322 private static void RequiresCanRead(Expression expression, string paramName) {
323 if (expression == null) {
324 throw new ArgumentNullException(paramName);
327 // validate that we can read the node
328 switch (expression.NodeType) {
329 case ExpressionType.Index:
330 IndexExpression index = (IndexExpression)expression;
331 if (index.Indexer != null && !index.Indexer.CanRead) {
332 throw new ArgumentException(Strings.ExpressionMustBeReadable, paramName);
335 case ExpressionType.MemberAccess:
336 MemberExpression member = (MemberExpression)expression;
337 MemberInfo memberInfo = member.Member;
338 if (memberInfo.MemberType == MemberTypes.Property) {
339 PropertyInfo prop = (PropertyInfo)memberInfo;
341 throw new ArgumentException(Strings.ExpressionMustBeReadable, paramName);
348 private static void RequiresCanRead(IEnumerable<Expression> items, string paramName) {
350 // this is called a lot, avoid allocating an enumerator if we can...
351 IList<Expression> listItems = items as IList<Expression>;
352 if (listItems != null) {
353 for (int i = 0; i < listItems.Count; i++) {
354 RequiresCanRead(listItems[i], paramName);
359 foreach (var i in items) {
360 RequiresCanRead(i, paramName);
364 private static void RequiresCanWrite(Expression expression, string paramName) {
365 if (expression == null) {
366 throw new ArgumentNullException(paramName);
369 bool canWrite = false;
370 switch (expression.NodeType) {
371 case ExpressionType.Index:
372 IndexExpression index = (IndexExpression)expression;
373 if (index.Indexer != null) {
374 canWrite = index.Indexer.CanWrite;
379 case ExpressionType.MemberAccess:
380 MemberExpression member = (MemberExpression)expression;
381 switch (member.Member.MemberType) {
382 case MemberTypes.Property:
383 PropertyInfo prop = (PropertyInfo)member.Member;
384 canWrite = prop.CanWrite;
386 case MemberTypes.Field:
387 FieldInfo field = (FieldInfo)member.Member;
388 canWrite = !(field.IsInitOnly || field.IsLiteral);
392 case ExpressionType.Parameter:
398 throw new ArgumentException(Strings.ExpressionMustBeWriteable, paramName);