PageRenderTime 57ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/Microsoft.Scripting.Core/Compiler/ExpressionQuoter.cs

https://bitbucket.org/stefanrusek/xronos
C# | 253 lines | 177 code | 27 blank | 49 comment | 34 complexity | ccf3fd88ed6f96954f73dbda998a2978 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.ComponentModel;
  18. using System.Diagnostics;
  19. #if CODEPLEX_40
  20. using System.Dynamic.Utils;
  21. using System.Linq.Expressions;
  22. using System.Linq.Expressions.Compiler;
  23. #else
  24. using Microsoft.Scripting.Utils;
  25. using Microsoft.Linq.Expressions;
  26. using Microsoft.Linq.Expressions.Compiler;
  27. #endif
  28. #if CODEPLEX_40
  29. namespace System.Runtime.CompilerServices {
  30. #else
  31. namespace Microsoft.Runtime.CompilerServices {
  32. #endif
  33. public partial class RuntimeOps {
  34. /// <summary>
  35. /// Quotes the provided expression tree.
  36. /// </summary>
  37. /// <param name="expression">The expression to quote.</param>
  38. /// <param name="hoistedLocals">The hoisted local state provided by the compiler.</param>
  39. /// <param name="locals">The actual hoisted local values.</param>
  40. /// <returns>The quoted expression.</returns>
  41. [Obsolete("do not use this method", true), EditorBrowsable(EditorBrowsableState.Never)]
  42. public static Expression Quote(Expression expression, object hoistedLocals, object[] locals) {
  43. Debug.Assert(hoistedLocals != null && locals != null);
  44. var quoter = new ExpressionQuoter((HoistedLocals)hoistedLocals, locals);
  45. return quoter.Visit(expression);
  46. }
  47. /// <summary>
  48. /// Combines two runtime variable lists and returns a new list.
  49. /// </summary>
  50. /// <param name="first">The first list.</param>
  51. /// <param name="second">The second list.</param>
  52. /// <param name="indexes">The index array indicating which list to get variables from.</param>
  53. /// <returns>The merged runtime variables.</returns>
  54. [Obsolete("do not use this method", true), EditorBrowsable(EditorBrowsableState.Never)]
  55. public static IRuntimeVariables MergeRuntimeVariables(IRuntimeVariables first, IRuntimeVariables second, int[] indexes) {
  56. return new MergedRuntimeVariables(first, second, indexes);
  57. }
  58. // Modifies a quoted Expression instance by changing hoisted variables and
  59. // parameters into hoisted local references. The variable's StrongBox is
  60. // burned as a constant, and all hoisted variables/parameters are rewritten
  61. // as indexing expressions.
  62. //
  63. // The behavior of Quote is indended to be like C# and VB expression quoting
  64. private sealed class ExpressionQuoter : ExpressionVisitor {
  65. private readonly HoistedLocals _scope;
  66. private readonly object[] _locals;
  67. // A stack of variables that are defined in nested scopes. We search
  68. // this first when resolving a variable in case a nested scope shadows
  69. // one of our variable instances.
  70. private readonly Stack<Set<ParameterExpression>> _shadowedVars = new Stack<Set<ParameterExpression>>();
  71. internal ExpressionQuoter(HoistedLocals scope, object[] locals) {
  72. _scope = scope;
  73. _locals = locals;
  74. }
  75. protected internal override Expression VisitLambda<T>(Expression<T> node) {
  76. _shadowedVars.Push(new Set<ParameterExpression>(node.Parameters));
  77. Expression b = Visit(node.Body);
  78. _shadowedVars.Pop();
  79. if (b == node.Body) {
  80. return node;
  81. }
  82. return Expression.Lambda<T>(b, node.Name, node.Parameters);
  83. }
  84. protected internal override Expression VisitBlock(BlockExpression node) {
  85. if (node.Variables.Count > 0) {
  86. _shadowedVars.Push(new Set<ParameterExpression>(node.Variables));
  87. }
  88. var b = Visit(node.Expressions);
  89. if (node.Variables.Count > 0) {
  90. _shadowedVars.Pop();
  91. }
  92. if (b == node.Expressions) {
  93. return node;
  94. }
  95. return Expression.Block(node.Variables, b);
  96. }
  97. protected override CatchBlock VisitCatchBlock(CatchBlock node) {
  98. if (node.Variable != null) {
  99. _shadowedVars.Push(new Set<ParameterExpression>(new[] { node.Variable }));
  100. }
  101. Expression b = Visit(node.Body);
  102. Expression f = Visit(node.Filter);
  103. if (node.Variable != null) {
  104. _shadowedVars.Pop();
  105. }
  106. if (b == node.Body && f == node.Filter) {
  107. return node;
  108. }
  109. return Expression.MakeCatchBlock(node.Test, node.Variable, b, f);
  110. }
  111. protected internal override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) {
  112. int count = node.Variables.Count;
  113. var boxes = new List<IStrongBox>();
  114. var vars = new List<ParameterExpression>();
  115. var indexes = new int[count];
  116. for (int i = 0; i < count; i++) {
  117. IStrongBox box = GetBox(node.Variables[i]);
  118. if (box == null) {
  119. indexes[i] = vars.Count;
  120. vars.Add(node.Variables[i]);
  121. } else {
  122. indexes[i] = -1 - boxes.Count;
  123. boxes.Add(box);
  124. }
  125. }
  126. // No variables were rewritten. Just return the original node
  127. if (boxes.Count == 0) {
  128. return node;
  129. }
  130. var boxesConst = Expression.Constant(new RuntimeVariables(boxes.ToArray()), typeof(IRuntimeVariables));
  131. // All of them were rewritten. Just return the array as a constant
  132. if (vars.Count == 0) {
  133. return boxesConst;
  134. }
  135. // Otherwise, we need to return an object that merges them
  136. return Expression.Call(
  137. typeof(RuntimeOps).GetMethod("MergeRuntimeVariables"),
  138. Expression.RuntimeVariables(new TrueReadOnlyCollection<ParameterExpression>(vars.ToArray())),
  139. boxesConst,
  140. Expression.Constant(indexes)
  141. );
  142. }
  143. protected internal override Expression VisitParameter(ParameterExpression node) {
  144. IStrongBox box = GetBox(node);
  145. if (box == null) {
  146. return node;
  147. }
  148. return Expression.Field(Expression.Constant(box), "Value");
  149. }
  150. private IStrongBox GetBox(ParameterExpression variable) {
  151. // Skip variables that are shadowed by a nested scope/lambda
  152. foreach (Set<ParameterExpression> hidden in _shadowedVars) {
  153. if (hidden.Contains(variable)) {
  154. return null;
  155. }
  156. }
  157. HoistedLocals scope = _scope;
  158. object[] locals = _locals;
  159. while (true) {
  160. int hoistIndex;
  161. if (scope.Indexes.TryGetValue(variable, out hoistIndex)) {
  162. return (IStrongBox)locals[hoistIndex];
  163. }
  164. scope = scope.Parent;
  165. if (scope == null) {
  166. break;
  167. }
  168. locals = HoistedLocals.GetParent(locals);
  169. }
  170. // Unbound variable: an error should've been thrown already
  171. // from VariableBinder
  172. throw ContractUtils.Unreachable;
  173. }
  174. }
  175. private sealed class RuntimeVariables : IRuntimeVariables {
  176. private readonly IStrongBox[] _boxes;
  177. internal RuntimeVariables(IStrongBox[] boxes) {
  178. _boxes = boxes;
  179. }
  180. int IRuntimeVariables.Count {
  181. get { return _boxes.Length; }
  182. }
  183. object IRuntimeVariables.this[int index] {
  184. get {
  185. return _boxes[index].Value;
  186. }
  187. set {
  188. _boxes[index].Value = value;
  189. }
  190. }
  191. }
  192. /// <summary>
  193. /// Provides a list of variables, supporing read/write of the values
  194. /// Exposed via RuntimeVariablesExpression
  195. /// </summary>
  196. private sealed class MergedRuntimeVariables : IRuntimeVariables {
  197. private readonly IRuntimeVariables _first;
  198. private readonly IRuntimeVariables _second;
  199. // For reach item, the index into the first or second list
  200. // Positive values mean the first array, negative means the second
  201. private readonly int[] _indexes;
  202. internal MergedRuntimeVariables(IRuntimeVariables first, IRuntimeVariables second, int[] indexes) {
  203. _first = first;
  204. _second = second;
  205. _indexes = indexes;
  206. }
  207. public int Count {
  208. get { return _indexes.Length; }
  209. }
  210. public object this[int index] {
  211. get {
  212. index = _indexes[index];
  213. return (index >= 0) ? _first[index] : _second[-1 - index];
  214. }
  215. set {
  216. index = _indexes[index];
  217. if (index >= 0) {
  218. _first[index] = value;
  219. } else {
  220. _second[-1 - index] = value;
  221. }
  222. }
  223. }
  224. }
  225. }
  226. }