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