PageRenderTime 117ms CodeModel.GetById 81ms app.highlight 15ms RepoModel.GetById 15ms app.codeStats 0ms

/Microsoft.Scripting.Core/Ast/Expression.cs

https://bitbucket.org/stefanrusek/xronos
C# | 413 lines | 228 code | 44 blank | 141 comment | 38 complexity | 3f7f0417e6a4bac110969cfd1c881c55 MD5 | raw file
  1/* ****************************************************************************
  2 *
  3 * Copyright (c) Microsoft Corporation. 
  4 *
  5 * This source code is subject to terms and conditions of the Microsoft Public License. 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  Microsoft Public License, 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 Microsoft Public License.
 10 *
 11 * You must not remove this notice, or any other, from this software.
 12 *
 13 *
 14 * ***************************************************************************/
 15using System; using Microsoft;
 16
 17
 18using System.Collections.Generic;
 19using System.Collections.ObjectModel;
 20#if CODEPLEX_40
 21using System.Dynamic.Utils;
 22#else
 23using Microsoft.Scripting.Utils;
 24#endif
 25using System.Globalization;
 26using System.IO;
 27using System.Reflection;
 28using System.Runtime.CompilerServices;
 29#if !CODEPLEX_40
 30using Microsoft.Runtime.CompilerServices;
 31#endif
 32
 33using System.Threading;
 34
 35#if CODEPLEX_40
 36namespace System.Linq.Expressions {
 37#else
 38namespace Microsoft.Linq.Expressions {
 39#endif
 40    /// <summary>
 41    /// The base type for all nodes in Expression Trees.
 42    /// </summary>
 43    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
 44    public abstract partial class Expression {
 45        private delegate LambdaExpression LambdaFactory(Expression body, string name, bool tailCall, ReadOnlyCollection<ParameterExpression> parameters);
 46
 47        private static readonly CacheDict<Type, MethodInfo> _LambdaDelegateCache = new CacheDict<Type, MethodInfo>(40);
 48        private static CacheDict<Type, LambdaFactory> _LambdaFactories;
 49
 50        // protected ctors are part of API surface area
 51
 52#if !MICROSOFT_SCRIPTING_CORE
 53        private class ExtensionInfo {
 54            public ExtensionInfo(ExpressionType nodeType, Type type) {
 55                NodeType = nodeType;
 56                Type = type;
 57            }
 58
 59            internal readonly ExpressionType NodeType;
 60            internal readonly Type Type;
 61        }
 62
 63        private static ConditionalWeakTable<Expression, ExtensionInfo> _legacyCtorSupportTable;
 64
 65        // LinqV1 ctor
 66        /// <summary>
 67        /// Constructs a new instance of <see cref="Expression"/>.
 68        /// </summary>
 69        /// <param name="nodeType">The <see ctype="ExpressionType"/> of the <see cref="Expression"/>.</param>
 70        /// <param name="type">The <see cref="Type"/> of the <see cref="Expression"/>.</param>
 71        [Obsolete("use a different constructor that does not take ExpressionType.  Then override GetExpressionType and GetNodeKind to provide the values that would be specified to this constructor.")]
 72        protected Expression(ExpressionType nodeType, Type type) {
 73            // Can't enforce anything that V1 didn't
 74            if (_legacyCtorSupportTable == null) {
 75                Interlocked.CompareExchange(
 76                    ref _legacyCtorSupportTable,
 77                    new ConditionalWeakTable<Expression, ExtensionInfo>(),
 78                    null
 79                );
 80            }
 81
 82            _legacyCtorSupportTable.Add(this, new ExtensionInfo(nodeType, type));
 83        }
 84#endif
 85        /// <summary>
 86        /// Constructs a new instance of <see cref="Expression"/>.
 87        /// </summary>
 88        protected Expression() {
 89        }
 90
 91        /// <summary>
 92        /// The <see cref="ExpressionType"/> of the <see cref="Expression"/>.
 93        /// </summary>
 94        public ExpressionType NodeType {
 95            get { return NodeTypeImpl(); }
 96        }
 97
 98
 99        /// <summary>
100        /// The <see cref="Type"/> of the value represented by this <see cref="Expression"/>.
101        /// </summary>
102        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]
103        public Type Type {
104            get { return TypeImpl(); }
105        }
106
107        /// <summary>
108        /// Indicates that the node can be reduced to a simpler node. If this 
109        /// returns true, Reduce() can be called to produce the reduced form.
110        /// </summary>
111        public virtual bool CanReduce {
112            get { return false; }
113        }
114
115        /// <summary>
116        /// Returns the node type of this Expression. Extension nodes should return
117        /// ExpressionType.Extension when overriding this method.
118        /// </summary>
119        /// <returns>The <see cref="ExpressionType"/> of the expression.</returns>
120        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
121        protected virtual ExpressionType NodeTypeImpl() {
122#if !MICROSOFT_SCRIPTING_CORE
123            ExtensionInfo extInfo;
124            if (_legacyCtorSupportTable.TryGetValue(this, out extInfo)) {
125                return extInfo.NodeType;
126            }
127#endif
128
129            // the extension expression failed to override NodeTypeImpl
130            throw Error.ExtensionNodeMustOverrideMethod("Expression.NodeTypeImpl()");
131        }
132
133        /// <summary>
134        /// Gets the static type of the expression that this <see cref="Expression" /> represents.
135        /// </summary>
136        /// <returns>The <see cref="Type"/> that represents the static type of the expression.</returns>
137        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
138        protected virtual Type TypeImpl() {
139#if !MICROSOFT_SCRIPTING_CORE
140            ExtensionInfo extInfo;
141            if (_legacyCtorSupportTable.TryGetValue(this, out extInfo)) {
142                return extInfo.Type;
143            }
144#endif
145
146            // the extension expression failed to override TypeImpl
147            throw Error.ExtensionNodeMustOverrideMethod("Expression.TypeImpl()");
148        }
149
150        /// <summary>
151        /// Reduces this node to a simpler expression. If CanReduce returns
152        /// true, this should return a valid expression. This method is
153        /// allowed to return another node which itself must be reduced.
154        /// </summary>
155        /// <returns>The reduced expression.</returns>
156        public virtual Expression Reduce() {
157            ContractUtils.Requires(!CanReduce, "this", Strings.ReducibleMustOverrideReduce);
158            return this;
159        }
160
161        /// <summary>
162        /// Reduces the node and then calls the visitor delegate on the reduced expression.
163        /// Throws an exception if the node isn't reducible.
164        /// </summary>
165        /// <param name="visitor">An instance of <see cref="Func{Expression, Expression}"/>.</param>
166        /// <returns>The expression being visited, or an expression which should replace it in the tree.</returns>
167        /// <remarks>
168        /// Override this method to provide logic to walk the node's children. 
169        /// A typical implementation will call visitor.Visit on each of its
170        /// children, and if any of them change, should return a new copy of
171        /// itself with the modified children.
172        /// </remarks>
173        protected internal virtual Expression VisitChildren(Func<Expression, Expression> visitor) {
174            ContractUtils.Requires(CanReduce, "this", Strings.MustBeReducible);
175            return visitor(ReduceExtensions());
176        }
177
178        // Visitor pattern: this is the method that dispatches back to the visitor
179        // NOTE: this is unlike the Visit method, which provides a hook for
180        // derived classes to extend the visitor framework to be able to walk
181        // themselves
182        internal virtual Expression Accept(ExpressionVisitor visitor) {
183            return visitor.VisitExtension(this);
184        }
185
186        /// <summary>
187        /// Reduces this node to a simpler expression. If CanReduce returns
188        /// true, this should return a valid expression. This method is
189        /// allowed to return another node which itself must be reduced.
190        /// </summary>
191        /// <returns>The reduced expression.</returns>
192        /// <remarks >
193        /// Unlike Reduce, this method checks that the reduced node satisfies
194        /// certain invariants.
195        /// </remarks>
196        public Expression ReduceAndCheck() {
197            ContractUtils.Requires(CanReduce, "this", Strings.MustBeReducible);
198
199            var newNode = Reduce();
200
201            // 1. Reduction must return a new, non-null node
202            // 2. Reduction must return a new node whose result type can be assigned to the type of the original node
203            ContractUtils.Requires(newNode != null && newNode != this, "this", Strings.MustReduceToDifferent);
204            ContractUtils.Requires(TypeUtils.AreReferenceAssignable(Type, newNode.Type), "this", Strings.ReducedNotCompatible);
205            return newNode;
206        }
207
208        /// <summary>
209        /// Reduces the expression to a known node type (i.e. not an Extension node)
210        /// or simply returns the expression if it is already a known type.
211        /// </summary>
212        /// <returns>The reduced expression.</returns>
213        public Expression ReduceExtensions() {
214            var node = this;
215            while (node.NodeType == ExpressionType.Extension) {
216                node = node.ReduceAndCheck();
217            }
218            return node;
219        }
220
221
222        /// <summary>
223        /// Creates a <see cref="String"/> representation of the Expression.
224        /// </summary>
225        /// <returns>A <see cref="String"/> representation of the Expression.</returns>
226        public override string ToString() {
227            return ExpressionStringBuilder.ExpressionToString(this);
228        }
229
230#if MICROSOFT_SCRIPTING_CORE
231        /// <summary>
232        /// Writes a <see cref="String"/> representation of the <see cref="Expression"/> to a <see cref="TextWriter"/>.
233        /// </summary>
234        /// <param name="descr">A description for the root Expression.</param>
235        /// <param name="writer">A <see cref="TextWriter"/> that will be used to build the string representation.</param>
236        public void DumpExpression(string descr, TextWriter writer) {
237            ExpressionWriter.Dump(this, descr, writer);
238        }
239
240        /// <summary>
241        /// Creates a <see cref="String"/> representation of the Expression.
242        /// </summary>
243        /// <returns>A <see cref="String"/> representation of the Expression.</returns>
244        public string DebugView {
245#else
246        private string DebugView {
247#endif
248            get {
249                using (System.IO.StringWriter writer = new System.IO.StringWriter(CultureInfo.CurrentCulture)) {
250                    ExpressionWriter.Dump(this, GetType().Name, writer);
251                    return writer.ToString();
252                }
253            }
254        }
255
256        /// <summary>
257        /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T.
258        /// 
259        /// This is called from various methods where we internally hold onto an IList of T
260        /// or a readonly collection of T.  We check to see if we've already returned a 
261        /// readonly collection of T and if so simply return the other one.  Otherwise we do 
262        /// a thread-safe replacement of the list w/ a readonly collection which wraps it.
263        /// 
264        /// Ultimately this saves us from having to allocate a ReadOnlyCollection for our
265        /// data types because the compiler is capable of going directly to the IList of T.
266        /// </summary>
267        internal static ReadOnlyCollection<T> ReturnReadOnly<T>(ref IList<T> collection) {
268            IList<T> value = collection;
269
270            // if it's already read-only just return it.
271            ReadOnlyCollection<T> res = value as ReadOnlyCollection<T>;
272            if (res != null) {
273                return res;
274            }
275
276            // otherwise make sure only readonly collection every gets exposed
277            Interlocked.CompareExchange<IList<T>>(
278                ref collection,
279                value.ToReadOnly(),
280                value
281            );
282
283            // and return it
284            return (ReadOnlyCollection<T>)collection;
285        }
286
287        /// <summary>
288        /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T.
289        /// 
290        /// This is similar to the ReturnReadOnly of T. This version supports nodes which hold 
291        /// onto multiple Expressions where one is typed to object.  That object field holds either
292        /// an expression or a ReadOnlyCollection of Expressions.  When it holds a ReadOnlyCollection
293        /// the IList which backs it is a ListArgumentProvider which uses the Expression which
294        /// implements IArgumentProvider to get 2nd and additional values.  The ListArgumentProvider 
295        /// continues to hold onto the 1st expression.  
296        /// 
297        /// This enables users to get the ReadOnlyCollection w/o it consuming more memory than if 
298        /// it was just an array.  Meanwhile The DLR internally avoids accessing  which would force 
299        /// the readonly collection to be created resulting in a typical memory savings.
300        /// </summary>
301        internal static ReadOnlyCollection<Expression> ReturnReadOnly(IArgumentProvider provider, ref object collection) {
302            Expression tObj = collection as Expression;
303            if (tObj != null) {
304                // otherwise make sure only one readonly collection ever gets exposed
305                Interlocked.CompareExchange(
306                    ref collection,
307                    new ReadOnlyCollection<Expression>(new ListArgumentProvider(provider, tObj)),
308                    tObj
309                );
310            }
311
312            // and return what is not guaranteed to be a readonly collection
313            return (ReadOnlyCollection<Expression>)collection;
314        }
315
316        /// <summary>
317        /// Helper which is used for specialized subtypes which use ReturnReadOnly(ref object, ...). 
318        /// This is the reverse version of ReturnReadOnly which takes an IArgumentProvider.
319        /// 
320        /// This is used to return the 1st argument.  The 1st argument is typed as object and either
321        /// contains a ReadOnlyCollection or the Expression.  We check for the Expression and if it's
322        /// present we return that, otherwise we return the 1st element of the ReadOnlyCollection.
323        /// </summary>
324        internal static T ReturnObject<T>(object collectionOrT) where T : class {
325            T t = collectionOrT as T;
326            if (t != null) {
327                return t;
328            }
329
330            return ((ReadOnlyCollection<T>)collectionOrT)[0];
331        }
332
333        private static void RequiresCanRead(Expression expression, string paramName) {
334            if (expression == null) {
335                throw new ArgumentNullException(paramName);
336            }
337
338            // validate that we can read the node
339            switch (expression.NodeType) {
340                case ExpressionType.Index:
341                    IndexExpression index = (IndexExpression)expression;
342                    if (index.Indexer != null && !index.Indexer.CanRead) {
343                        throw new ArgumentException(Strings.ExpressionMustBeReadable, paramName);
344                    }
345                    break;
346                case ExpressionType.MemberAccess:
347                    MemberExpression member = (MemberExpression)expression;
348                    MemberInfo memberInfo = member.Member;
349                    if (memberInfo.MemberType == MemberTypes.Property) {
350                        PropertyInfo prop = (PropertyInfo)memberInfo;
351                        if (!prop.CanRead) {
352                            throw new ArgumentException(Strings.ExpressionMustBeReadable, paramName);
353                        }
354                    }
355                    break;
356            }
357        }
358
359        private static void RequiresCanRead(IEnumerable<Expression> items, string paramName) {
360            if (items != null) {
361                // this is called a lot, avoid allocating an enumerator if we can...
362                IList<Expression> listItems = items as IList<Expression>;
363                if (listItems != null) {
364                    for (int i = 0; i < listItems.Count; i++) {
365                        RequiresCanRead(listItems[i], paramName);
366                    }
367                    return;
368                }
369
370                foreach (var i in items) {
371                    RequiresCanRead(i, paramName);
372                }
373            }
374        }
375        private static void RequiresCanWrite(Expression expression, string paramName) {
376            if (expression == null) {
377                throw new ArgumentNullException(paramName);
378            }
379
380            bool canWrite = false;
381            switch (expression.NodeType) {
382                case ExpressionType.Index:
383                    IndexExpression index = (IndexExpression)expression;
384                    if (index.Indexer != null) {
385                        canWrite = index.Indexer.CanWrite;
386                    } else {
387                        canWrite = true;
388                    }
389                    break;
390                case ExpressionType.MemberAccess:
391                    MemberExpression member = (MemberExpression)expression;
392                    switch (member.Member.MemberType) {
393                        case MemberTypes.Property:
394                            PropertyInfo prop = (PropertyInfo)member.Member;
395                            canWrite = prop.CanWrite;
396                            break;
397                        case MemberTypes.Field:
398                            FieldInfo field = (FieldInfo)member.Member;
399                            canWrite = !(field.IsInitOnly || field.IsLiteral);
400                            break;
401                    }
402                    break;
403                case ExpressionType.Parameter:
404                    canWrite = true;
405                    break;
406            }
407
408            if (!canWrite) {
409                throw new ArgumentException(Strings.ExpressionMustBeWriteable, paramName);
410            }
411        }
412    }
413}