Merge branch 'cecil-light'
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Scripting.Core / Ast / Expression.cs
1 /* ****************************************************************************\r
2  *\r
3  * Copyright (c) Microsoft Corporation. \r
4  *\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
10  *\r
11  * You must not remove this notice, or any other, from this software.\r
12  *\r
13  *\r
14  * ***************************************************************************/\r
15 \r
16 using System;\r
17 using System.Collections.Generic;\r
18 using System.Collections.ObjectModel;\r
19 using System.Dynamic.Utils;\r
20 using System.Globalization;\r
21 using System.IO;\r
22 using System.Reflection;\r
23 using System.Runtime.CompilerServices;\r
24 using System.Threading;\r
25 \r
26 #if SILVERLIGHT\r
27 using System.Core;\r
28 #endif\r
29 \r
30 #if CLR2\r
31 namespace Microsoft.Scripting.Ast {\r
32     using Microsoft.Scripting.Utils;\r
33 #else\r
34 namespace System.Linq.Expressions {\r
35 #endif\r
36     /// <summary>\r
37     /// The base type for all nodes in Expression Trees.\r
38     /// </summary>\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
42 \r
43         private static readonly CacheDict<Type, MethodInfo> _LambdaDelegateCache = new CacheDict<Type, MethodInfo>(40);\r
44         private static CacheDict<Type, LambdaFactory> _LambdaFactories;\r
45 \r
46         // LINQ protected ctor from 3.5\r
47 \r
48 #if !CLR2 // needs ConditionWeakTable in 4.0\r
49 \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
53         //\r
54         // To support the 3.5 protected constructor, we store the fields that\r
55         // used to be here in a ConditionalWeakTable.\r
56 \r
57         private class ExtensionInfo {\r
58             public ExtensionInfo(ExpressionType nodeType, Type type) {\r
59                 NodeType = nodeType;\r
60                 Type = type;\r
61             }\r
62 \r
63             internal readonly ExpressionType NodeType;\r
64             internal readonly Type Type;\r
65         }\r
66 \r
67         private static ConditionalWeakTable<Expression, ExtensionInfo> _legacyCtorSupportTable;\r
68 \r
69         /// <summary>\r
70         /// Constructs a new instance of <see cref="Expression"/>.\r
71         /// </summary>\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
81                     null\r
82                 );\r
83             }\r
84 \r
85             _legacyCtorSupportTable.Add(this, new ExtensionInfo(nodeType, type));\r
86         }\r
87 #endif\r
88 \r
89         /// <summary>\r
90         /// Constructs a new instance of <see cref="Expression"/>.\r
91         /// </summary>\r
92         protected Expression() {\r
93         }\r
94 \r
95         /// <summary>\r
96         /// The <see cref="ExpressionType"/> of the <see cref="Expression"/>.\r
97         /// </summary>\r
98         public virtual ExpressionType NodeType {\r
99             get {\r
100 #if !CLR2\r
101                 ExtensionInfo extInfo;\r
102                 if (_legacyCtorSupportTable != null && _legacyCtorSupportTable.TryGetValue(this, out extInfo)) {\r
103                     return extInfo.NodeType;\r
104                 }\r
105 #endif\r
106                 // the extension expression failed to override NodeType\r
107                 throw Error.ExtensionNodeMustOverrideProperty("Expression.NodeType");\r
108             }\r
109         }\r
110 \r
111 \r
112         /// <summary>\r
113         /// The <see cref="Type"/> of the value represented by this <see cref="Expression"/>.\r
114         /// </summary>\r
115         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")]\r
116         public virtual Type Type {\r
117             get {\r
118 #if !CLR2\r
119                 ExtensionInfo extInfo;\r
120                 if (_legacyCtorSupportTable != null && _legacyCtorSupportTable.TryGetValue(this, out extInfo)) {\r
121                     return extInfo.Type;\r
122                 }\r
123 #endif\r
124                 // the extension expression failed to override Type\r
125                 throw Error.ExtensionNodeMustOverrideProperty("Expression.Type");\r
126             }\r
127         }\r
128 \r
129         /// <summary>\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
132         /// </summary>\r
133         public virtual bool CanReduce {\r
134             get { return false; }\r
135         }\r
136 \r
137         /// <summary>\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
141         /// </summary>\r
142         /// <returns>The reduced expression.</returns>\r
143         public virtual Expression Reduce() {\r
144             if (CanReduce) throw Error.ReducibleMustOverrideReduce();\r
145             return this;\r
146         }\r
147 \r
148         /// <summary>\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
151         /// </summary>\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
154         /// <remarks>\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
159         /// </remarks>\r
160         protected internal virtual Expression VisitChildren(ExpressionVisitor visitor) {\r
161             if (!CanReduce) throw Error.MustBeReducible();\r
162             return visitor.Visit(ReduceAndCheck());\r
163         }\r
164 \r
165         /// <summary>\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
169         /// </summary>\r
170         /// <param name="visitor">The visitor to visit this node with.</param>\r
171         /// <returns>The result of visiting this node.</returns>\r
172         /// <remarks>\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
178         /// </remarks>\r
179         protected internal virtual Expression Accept(ExpressionVisitor visitor) {\r
180             return visitor.VisitExtension(this);\r
181         }\r
182 \r
183         /// <summary>\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
187         /// </summary>\r
188         /// <returns>The reduced expression.</returns>\r
189         /// <remarks >\r
190         /// Unlike Reduce, this method checks that the reduced node satisfies\r
191         /// certain invariants.\r
192         /// </remarks>\r
193         public Expression ReduceAndCheck() {\r
194             if (!CanReduce) throw Error.MustBeReducible();\r
195 \r
196             var newNode = Reduce();\r
197 \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
202             return newNode;\r
203         }\r
204 \r
205         /// <summary>\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
208         /// </summary>\r
209         /// <returns>The reduced expression.</returns>\r
210         public Expression ReduceExtensions() {\r
211             var node = this;\r
212             while (node.NodeType == ExpressionType.Extension) {\r
213                 node = node.ReduceAndCheck();\r
214             }\r
215             return node;\r
216         }\r
217 \r
218 \r
219         /// <summary>\r
220         /// Creates a <see cref="String"/> representation of the Expression.\r
221         /// </summary>\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
225         }\r
226 \r
227 #if CLR2\r
228         /// <summary>\r
229         /// Writes a <see cref="String"/> representation of the <see cref="Expression"/> to a <see cref="TextWriter"/>.\r
230         /// </summary>\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
234         }\r
235 \r
236         /// <summary>\r
237         /// Creates a <see cref="String"/> representation of the Expression.\r
238         /// </summary>\r
239         /// <returns>A <see cref="String"/> representation of the Expression.</returns>\r
240         public string DebugView {\r
241 #else\r
242         private string DebugView {\r
243 #endif\r
244             get {\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
248                 }\r
249             }\r
250         }\r
251 \r
252         /// <summary>\r
253         /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T.\r
254         /// \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
259         /// \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
262         /// </summary>\r
263         internal static ReadOnlyCollection<T> ReturnReadOnly<T>(ref IList<T> collection) {\r
264             IList<T> value = collection;\r
265 \r
266             // if it's already read-only just return it.\r
267             ReadOnlyCollection<T> res = value as ReadOnlyCollection<T>;\r
268             if (res != null) {\r
269                 return res;\r
270             }\r
271 \r
272             // otherwise make sure only readonly collection every gets exposed\r
273             Interlocked.CompareExchange<IList<T>>(\r
274                 ref collection,\r
275                 value.ToReadOnly(),\r
276                 value\r
277             );\r
278 \r
279             // and return it\r
280             return (ReadOnlyCollection<T>)collection;\r
281         }\r
282 \r
283         /// <summary>\r
284         /// Helper used for ensuring we only return 1 instance of a ReadOnlyCollection of T.\r
285         /// \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
292         /// \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
296         /// </summary>\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
302                     ref collection,\r
303                     new ReadOnlyCollection<Expression>(new ListArgumentProvider(provider, tObj)),\r
304                     tObj\r
305                 );\r
306             }\r
307 \r
308             // and return what is not guaranteed to be a readonly collection\r
309             return (ReadOnlyCollection<Expression>)collection;\r
310         }\r
311 \r
312         /// <summary>\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
315         /// \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
319         /// </summary>\r
320         internal static T ReturnObject<T>(object collectionOrT) where T : class {\r
321             T t = collectionOrT as T;\r
322             if (t != null) {\r
323                 return t;\r
324             }\r
325 \r
326             return ((ReadOnlyCollection<T>)collectionOrT)[0];\r
327         }\r
328 \r
329 #if SILVERLIGHT\r
330 #if !CLR2\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
334 #else\r
335         internal readonly static bool SilverlightQuirks = true;\r
336 #endif\r
337 #endif\r
338 \r
339         private static void RequiresCanRead(Expression expression, string paramName) {\r
340             if (expression == null) {\r
341                 throw new ArgumentNullException(paramName);\r
342             }\r
343 \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
350                     }\r
351                     break;\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
359                         }\r
360                     }\r
361                     break;\r
362             }\r
363         }\r
364 \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
372                     }\r
373                     return;\r
374                 }\r
375 \r
376                 foreach (var i in items) {\r
377                     RequiresCanRead(i, paramName);\r
378                 }\r
379             }\r
380         }\r
381         private static void RequiresCanWrite(Expression expression, string paramName) {\r
382             if (expression == null) {\r
383                 throw new ArgumentNullException(paramName);\r
384             }\r
385 \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
392                     } else {\r
393                         canWrite = true;\r
394                     }\r
395                     break;\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
402                             break;\r
403                         case MemberTypes.Field:\r
404                             FieldInfo field = (FieldInfo)member.Member;\r
405                             canWrite = !(field.IsInitOnly || field.IsLiteral);\r
406                             break;\r
407                     }\r
408                     break;\r
409                 case ExpressionType.Parameter:\r
410                     canWrite = true;\r
411                     break;\r
412             }\r
413 \r
414             if (!canWrite) {\r
415                 throw new ArgumentException(Strings.ExpressionMustBeWriteable, paramName);\r
416             }\r
417         }\r
418     }\r
419 }\r