1 /* ****************************************************************************
\r
3 * Copyright (c) Microsoft Corporation.
\r
5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
\r
6 * copy of the license can be found in the License.html file at the root of this distribution. If
\r
7 * you cannot locate the Apache License, Version 2.0, please send an email to
\r
8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
\r
9 * by the terms of the Apache License, Version 2.0.
\r
11 * You must not remove this notice, or any other, from this software.
\r
14 * ***************************************************************************/
\r
17 using System.Collections.Generic;
\r
18 using System.Collections.ObjectModel;
\r
19 using System.Dynamic.Utils;
\r
20 using System.Globalization;
\r
22 using System.Reflection;
\r
23 using System.Runtime.CompilerServices;
\r
24 using System.Threading;
\r
31 namespace Microsoft.Scripting.Ast {
\r
32 using Microsoft.Scripting.Utils;
\r
34 namespace System.Linq.Expressions {
\r
37 /// The base type for all nodes in Expression Trees.
\r
39 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
\r
40 public abstract partial class Expression {
\r
41 private delegate LambdaExpression LambdaFactory(Expression body, string name, bool tailCall, ReadOnlyCollection<ParameterExpression> parameters);
\r
43 private static readonly CacheDict<Type, MethodInfo> _LambdaDelegateCache = new CacheDict<Type, MethodInfo>(40);
\r
44 private static CacheDict<Type, LambdaFactory> _LambdaFactories;
\r
46 // LINQ protected ctor from 3.5
\r
48 #if !CLR2 // needs ConditionWeakTable in 4.0
\r
50 // For 4.0, many frequently used Expression nodes have had their memory
\r
51 // footprint reduced by removing the Type and NodeType fields. This has
\r
52 // large performance benefits to all users of Expression Trees.
\r
54 // To support the 3.5 protected constructor, we store the fields that
\r
55 // used to be here in a ConditionalWeakTable.
\r
57 private class ExtensionInfo {
\r
58 public ExtensionInfo(ExpressionType nodeType, Type type) {
\r
59 NodeType = nodeType;
\r
63 internal readonly ExpressionType NodeType;
\r
64 internal readonly Type Type;
\r
67 private static ConditionalWeakTable<Expression, ExtensionInfo> _legacyCtorSupportTable;
\r
70 /// Constructs a new instance of <see cref="Expression"/>.
\r
72 /// <param name="nodeType">The <see ctype="ExpressionType"/> of the <see cref="Expression"/>.</param>
\r
73 /// <param name="type">The <see cref="Type"/> of the <see cref="Expression"/>.</param>
\r
74 [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.")]
\r
75 protected Expression(ExpressionType nodeType, Type type) {
\r
76 // Can't enforce anything that V1 didn't
\r
77 if (_legacyCtorSupportTable == null) {
\r
78 Interlocked.CompareExchange(
\r
79 ref _legacyCtorSupportTable,
\r
80 new ConditionalWeakTable<Expression, ExtensionInfo>(),
\r
85 _legacyCtorSupportTable.Add(this, new ExtensionInfo(nodeType, type));
\r
90 /// Constructs a new instance of <see cref="Expression"/>.
\r
92 protected Expression() {
\r
96 /// The <see cref="ExpressionType"/> of the <see cref="Expression"/>.
\r
98 public virtual ExpressionType NodeType {
\r
101 ExtensionInfo extInfo;
\r
102 if (_legacyCtorSupportTable != null && _legacyCtorSupportTable.TryGetValue(this, out extInfo)) {
\r
103 return extInfo.NodeType;
\r
106 // the extension expression failed to override NodeType
\r
107 throw Error.ExtensionNodeMustOverrideProperty("Expression.NodeType");
\r
113 /// The <see cref="Type"/> of the value represented by this <see cref="Expression"/>.
\r
115 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]
\r
116 public virtual Type Type {
\r
119 ExtensionInfo extInfo;
\r
120 if (_legacyCtorSupportTable != null && _legacyCtorSupportTable.TryGetValue(this, out extInfo)) {
\r
121 return extInfo.Type;
\r
124 // the extension expression failed to override Type
\r
125 throw Error.ExtensionNodeMustOverrideProperty("Expression.Type");
\r
130 /// Indicates that the node can be reduced to a simpler node. If this
\r
131 /// returns true, Reduce() can be called to produce the reduced form.
\r
133 public virtual bool CanReduce {
\r
134 get { return false; }
\r
138 /// Reduces this node to a simpler expression. If CanReduce returns
\r
139 /// true, this should return a valid expression. This method is
\r
140 /// allowed to return another node which itself must be reduced.
\r
142 /// <returns>The reduced expression.</returns>
\r
143 public virtual Expression Reduce() {
\r
144 if (CanReduce) throw Error.ReducibleMustOverrideReduce();
\r
149 /// Reduces the node and then calls the visitor delegate on the reduced expression.
\r
150 /// Throws an exception if the node isn't reducible.
\r
152 /// <param name="visitor">An instance of <see cref="Func{Expression, Expression}"/>.</param>
\r
153 /// <returns>The expression being visited, or an expression which should replace it in the tree.</returns>
\r
155 /// Override this method to provide logic to walk the node's children.
\r
156 /// A typical implementation will call visitor.Visit on each of its
\r
157 /// children, and if any of them change, should return a new copy of
\r
158 /// itself with the modified children.
\r
160 protected internal virtual Expression VisitChildren(ExpressionVisitor visitor) {
\r
161 if (!CanReduce) throw Error.MustBeReducible();
\r
162 return visitor.Visit(ReduceAndCheck());
\r
166 /// Dispatches to the specific visit method for this node type. For
\r
167 /// example, <see cref="MethodCallExpression" /> will call into
\r
168 /// <see cref="ExpressionVisitor.VisitMethodCall" />.
\r
170 /// <param name="visitor">The visitor to visit this node with.</param>
\r
171 /// <returns>The result of visiting this node.</returns>
\r
173 /// This default implementation for <see cref="ExpressionType.Extension" />
\r
174 /// nodes will call <see cref="ExpressionVisitor.VisitExtension" />.
\r
175 /// Override this method to call into a more specific method on a derived
\r
176 /// visitor class of ExprressionVisitor. However, it should still
\r
177 /// support unknown visitors by calling VisitExtension.
\r
179 protected internal virtual Expression Accept(ExpressionVisitor visitor) {
\r
180 return visitor.VisitExtension(this);
\r
184 /// Reduces this node to a simpler expression. If CanReduce returns
\r
185 /// true, this should return a valid expression. This method is
\r
186 /// allowed to return another node which itself must be reduced.
\r
188 /// <returns>The reduced expression.</returns>
\r
190 /// Unlike Reduce, this method checks that the reduced node satisfies
\r
191 /// certain invariants.
\r
193 public Expression ReduceAndCheck() {
\r
194 if (!CanReduce) throw Error.MustBeReducible();
\r
196 var newNode = Reduce();
\r
198 // 1. Reduction must return a new, non-null node
\r
199 // 2. Reduction must return a new node whose result type can be assigned to the type of the original node
\r
200 if (newNode == null || newNode == this) throw Error.MustReduceToDifferent();
\r
201 if (!TypeUtils.AreReferenceAssignable(Type, newNode.Type)) throw Error.ReducedNotCompatible();
\r
206 /// Reduces the expression to a known node type (i.e. not an Extension node)
\r
207 /// or simply returns the expression if it is already a known type.
\r
209 /// <returns>The reduced expression.</returns>
\r
210 public Expression ReduceExtensions() {
\r
212 while (node.NodeType == ExpressionType.Extension) {
\r
213 node = node.ReduceAndCheck();
\r
220 /// Creates a <see cref="String"/> representation of the Expression.
\r
222 /// <returns>A <see cref="String"/> representation of the Expression.</returns>
\r
223 public override string ToString() {
\r
224 return ExpressionStringBuilder.ExpressionToString(this);
\r
229 /// Writes a <see cref="String"/> representation of the <see cref="Expression"/> to a <see cref="TextWriter"/>.
\r
231 /// <param name="writer">A <see cref="TextWriter"/> that will be used to build the string representation.</param>
\r
232 public void DumpExpression(TextWriter writer) {
\r
233 DebugViewWriter.WriteTo(this, writer);
\r
237 /// Creates a <see cref="String"/> representation of the Expression.
\r
239 /// <returns>A <see cref="String"/> representation of the Expression.</returns>
\r
240 public string DebugView {
\r
242 private string DebugView {
\r
245 using (System.IO.StringWriter writer = new System.IO.StringWriter(CultureInfo.CurrentCulture)) {
\r
246 DebugViewWriter.WriteTo(this, writer);
\r
247 return writer.ToString();
\r
253 /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T.
\r
255 /// This is called from various methods where we internally hold onto an IList of T
\r
256 /// or a readonly collection of T. We check to see if we've already returned a
\r
257 /// readonly collection of T and if so simply return the other one. Otherwise we do
\r
258 /// a thread-safe replacement of the list w/ a readonly collection which wraps it.
\r
260 /// Ultimately this saves us from having to allocate a ReadOnlyCollection for our
\r
261 /// data types because the compiler is capable of going directly to the IList of T.
\r
263 internal static ReadOnlyCollection<T> ReturnReadOnly<T>(ref IList<T> collection) {
\r
264 IList<T> value = collection;
\r
266 // if it's already read-only just return it.
\r
267 ReadOnlyCollection<T> res = value as ReadOnlyCollection<T>;
\r
272 // otherwise make sure only readonly collection every gets exposed
\r
273 Interlocked.CompareExchange<IList<T>>(
\r
275 value.ToReadOnly(),
\r
280 return (ReadOnlyCollection<T>)collection;
\r
284 /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T.
\r
286 /// This is similar to the ReturnReadOnly of T. This version supports nodes which hold
\r
287 /// onto multiple Expressions where one is typed to object. That object field holds either
\r
288 /// an expression or a ReadOnlyCollection of Expressions. When it holds a ReadOnlyCollection
\r
289 /// the IList which backs it is a ListArgumentProvider which uses the Expression which
\r
290 /// implements IArgumentProvider to get 2nd and additional values. The ListArgumentProvider
\r
291 /// continues to hold onto the 1st expression.
\r
293 /// This enables users to get the ReadOnlyCollection w/o it consuming more memory than if
\r
294 /// it was just an array. Meanwhile The DLR internally avoids accessing which would force
\r
295 /// the readonly collection to be created resulting in a typical memory savings.
\r
297 internal static ReadOnlyCollection<Expression> ReturnReadOnly(IArgumentProvider provider, ref object collection) {
\r
298 Expression tObj = collection as Expression;
\r
299 if (tObj != null) {
\r
300 // otherwise make sure only one readonly collection ever gets exposed
\r
301 Interlocked.CompareExchange(
\r
303 new ReadOnlyCollection<Expression>(new ListArgumentProvider(provider, tObj)),
\r
308 // and return what is not guaranteed to be a readonly collection
\r
309 return (ReadOnlyCollection<Expression>)collection;
\r
313 /// Helper which is used for specialized subtypes which use ReturnReadOnly(ref object, ...).
\r
314 /// This is the reverse version of ReturnReadOnly which takes an IArgumentProvider.
\r
316 /// This is used to return the 1st argument. The 1st argument is typed as object and either
\r
317 /// contains a ReadOnlyCollection or the Expression. We check for the Expression and if it's
\r
318 /// present we return that, otherwise we return the 1st element of the ReadOnlyCollection.
\r
320 internal static T ReturnObject<T>(object collectionOrT) where T : class {
\r
321 T t = collectionOrT as T;
\r
326 return ((ReadOnlyCollection<T>)collectionOrT)[0];
\r
331 // Quirks mode for Expression Trees as they existed in Silverlight 2 and 3
\r
332 internal readonly static bool SilverlightQuirks =
\r
333 AppDomain.CurrentDomain.IsCompatibilitySwitchSet("APP_EARLIER_THAN_SL4.0").GetValueOrDefault();
\r
335 internal readonly static bool SilverlightQuirks = true;
\r
339 private static void RequiresCanRead(Expression expression, string paramName) {
\r
340 if (expression == null) {
\r
341 throw new ArgumentNullException(paramName);
\r
344 // validate that we can read the node
\r
345 switch (expression.NodeType) {
\r
346 case ExpressionType.Index:
\r
347 IndexExpression index = (IndexExpression)expression;
\r
348 if (index.Indexer != null && !index.Indexer.CanRead) {
\r
349 throw new ArgumentException(Strings.ExpressionMustBeReadable, paramName);
\r
352 case ExpressionType.MemberAccess:
\r
353 MemberExpression member = (MemberExpression)expression;
\r
354 MemberInfo memberInfo = member.Member;
\r
355 if (memberInfo.MemberType == MemberTypes.Property) {
\r
356 PropertyInfo prop = (PropertyInfo)memberInfo;
\r
357 if (!prop.CanRead) {
\r
358 throw new ArgumentException(Strings.ExpressionMustBeReadable, paramName);
\r
365 private static void RequiresCanRead(IEnumerable<Expression> items, string paramName) {
\r
366 if (items != null) {
\r
367 // this is called a lot, avoid allocating an enumerator if we can...
\r
368 IList<Expression> listItems = items as IList<Expression>;
\r
369 if (listItems != null) {
\r
370 for (int i = 0; i < listItems.Count; i++) {
\r
371 RequiresCanRead(listItems[i], paramName);
\r
376 foreach (var i in items) {
\r
377 RequiresCanRead(i, paramName);
\r
381 private static void RequiresCanWrite(Expression expression, string paramName) {
\r
382 if (expression == null) {
\r
383 throw new ArgumentNullException(paramName);
\r
386 bool canWrite = false;
\r
387 switch (expression.NodeType) {
\r
388 case ExpressionType.Index:
\r
389 IndexExpression index = (IndexExpression)expression;
\r
390 if (index.Indexer != null) {
\r
391 canWrite = index.Indexer.CanWrite;
\r
396 case ExpressionType.MemberAccess:
\r
397 MemberExpression member = (MemberExpression)expression;
\r
398 switch (member.Member.MemberType) {
\r
399 case MemberTypes.Property:
\r
400 PropertyInfo prop = (PropertyInfo)member.Member;
\r
401 canWrite = prop.CanWrite;
\r
403 case MemberTypes.Field:
\r
404 FieldInfo field = (FieldInfo)member.Member;
\r
405 canWrite = !(field.IsInitOnly || field.IsLiteral);
\r
409 case ExpressionType.Parameter:
\r
415 throw new ArgumentException(Strings.ExpressionMustBeWriteable, paramName);
\r