PageRenderTime 53ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/Microsoft.Scripting/Interpreter/LightLambdaClosureVisitor.cs

https://bitbucket.org/stefanrusek/xronos
C# | 290 lines | 192 code | 38 blank | 60 comment | 41 complexity | 2be81123ed2e43f2516d927f6cab7211 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. #if CODEPLEX_40
  16. using System;
  17. #else
  18. using System; using Microsoft;
  19. #endif
  20. using System.Collections.Generic;
  21. #if CODEPLEX_40
  22. using System.Linq.Expressions;
  23. #else
  24. using Microsoft.Linq.Expressions;
  25. #endif
  26. using System.Runtime.CompilerServices;
  27. #if !CODEPLEX_40
  28. using Microsoft.Runtime.CompilerServices;
  29. #endif
  30. using Microsoft.Scripting.Generation;
  31. using Microsoft.Scripting.Utils;
  32. using AstUtils = Microsoft.Scripting.Ast.Utils;
  33. namespace Microsoft.Scripting.Interpreter {
  34. /// <summary>
  35. /// Visits a LambdaExpression, replacing the constants with direct accesses
  36. /// to their StrongBox fields. This is very similar to what
  37. /// ExpressionQuoter does for LambdaCompiler.
  38. ///
  39. /// Also inserts debug information tracking similar to what the interpreter
  40. /// would do.
  41. /// </summary>
  42. internal sealed class LightLambdaClosureVisitor : ExpressionVisitor {
  43. /// <summary>
  44. /// Indexes of variables into the closure array
  45. /// </summary>
  46. private readonly Dictionary<ParameterExpression, int> _closureVars;
  47. /// <summary>
  48. /// The variable that holds onto the StrongBox{object}[] closure from
  49. /// the interpreter
  50. /// </summary>
  51. private readonly ParameterExpression _closureArray;
  52. /// <summary>
  53. /// A stack of variables that are defined in nested scopes. We search
  54. /// this first when resolving a variable in case a nested scope shadows
  55. /// one of our variable instances.
  56. /// </summary>
  57. private readonly Stack<Set<ParameterExpression>> _shadowedVars = new Stack<Set<ParameterExpression>>();
  58. private LightLambdaClosureVisitor(IList<ParameterExpression> closureVars, ParameterExpression closureArray) {
  59. _closureArray = closureArray;
  60. _closureVars = new Dictionary<ParameterExpression, int>(closureVars.Count);
  61. for (int i = 0, n = closureVars.Count; i < n; i++) {
  62. _closureVars.Add(closureVars[i], i);
  63. }
  64. }
  65. /// <summary>
  66. /// Walks the lambda and produces a higher order function, which can be
  67. /// used to bind the lambda to a closure array from the interpreter.
  68. /// </summary>
  69. /// <param name="lambda">The lambda to bind.</param>
  70. /// <param name="closureVars">The variables that are closed over from an outer scope.</param>
  71. /// <param name="delegateTypeMatch">true if the delegate type is the same; false if it was changed to Func/Action.</param>
  72. /// <returns>A delegate that can be called to produce a delegate bound to the passed in closure array.</returns>
  73. internal static Func<StrongBox<object>[], Delegate> BindLambda(LambdaExpression lambda, IList<ParameterExpression> closureVars, out bool delegateTypeMatch) {
  74. var closure = Expression.Parameter(typeof(StrongBox<object>[]), "closure");
  75. var visitor = new LightLambdaClosureVisitor(closureVars, closure);
  76. LambdaExpression rewritten = visitor.VisitTopLambda(lambda);
  77. delegateTypeMatch = (rewritten.Type == lambda.Type);
  78. // Create a higher-order function which fills in the parameters
  79. var result = Expression.Lambda<Func<StrongBox<object>[], Delegate>>(rewritten, closure);
  80. return result.Compile();
  81. }
  82. private LambdaExpression VisitTopLambda(LambdaExpression lambda) {
  83. // 1. Rewrite the the tree
  84. lambda = (LambdaExpression)Visit(lambda);
  85. // 2. Fix the lambda's delegate type: it must be Func<...> or
  86. // Action<...> to be called from the generated Run methods.
  87. Type delegateType = GetDelegateType(lambda);
  88. // 4. Return the lambda with the handling and the (possibly new) delegate type
  89. return Expression.Lambda(delegateType, lambda.Body, lambda.Name, lambda.Parameters);
  90. }
  91. private static Type GetDelegateType(LambdaExpression lambda) {
  92. Type delegateType = lambda.Type;
  93. if (lambda.ReturnType == typeof(void) && lambda.Parameters.Count == 2 &&
  94. lambda.Parameters[0].IsByRef && lambda.Parameters[1].IsByRef) {
  95. delegateType = typeof(ActionRef<,>).MakeGenericType(lambda.Parameters.Map(p => p.Type));
  96. } else {
  97. Type[] types = lambda.Parameters.Map(p => p.IsByRef ? p.Type.MakeByRefType() : p.Type);
  98. delegateType = Expression.GetDelegateType(types.AddLast(lambda.ReturnType));
  99. }
  100. return delegateType;
  101. }
  102. #region closures
  103. protected override Expression VisitLambda<T>(Expression<T> node) {
  104. _shadowedVars.Push(new Set<ParameterExpression>(node.Parameters));
  105. Expression b = Visit(node.Body);
  106. _shadowedVars.Pop();
  107. if (b == node.Body) {
  108. return node;
  109. }
  110. return Expression.Lambda<T>(b, node.Name, node.Parameters);
  111. }
  112. protected override Expression VisitBlock(BlockExpression node) {
  113. if (node.Variables.Count > 0) {
  114. _shadowedVars.Push(new Set<ParameterExpression>(node.Variables));
  115. }
  116. var b = Visit(node.Expressions);
  117. if (node.Variables.Count > 0) {
  118. _shadowedVars.Pop();
  119. }
  120. if (b == node.Expressions) {
  121. return node;
  122. }
  123. return Expression.Block(node.Variables, b);
  124. }
  125. protected override CatchBlock VisitCatchBlock(CatchBlock node) {
  126. if (node.Variable != null) {
  127. _shadowedVars.Push(new Set<ParameterExpression>(new[] { node.Variable }));
  128. }
  129. Expression b = Visit(node.Body);
  130. Expression f = Visit(node.Filter);
  131. if (node.Variable != null) {
  132. _shadowedVars.Pop();
  133. }
  134. if (b == node.Body && f == node.Filter) {
  135. return node;
  136. }
  137. return Expression.MakeCatchBlock(node.Test, node.Variable, b, f);
  138. }
  139. protected override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) {
  140. int count = node.Variables.Count;
  141. var boxes = new List<Expression>();
  142. var vars = new List<ParameterExpression>();
  143. var indexes = new int[count];
  144. for (int i = 0; i < count; i++) {
  145. Expression box = GetBox(node.Variables[i]);
  146. if (box == null) {
  147. indexes[i] = vars.Count;
  148. vars.Add(node.Variables[i]);
  149. } else {
  150. indexes[i] = -1 - boxes.Count;
  151. boxes.Add(box);
  152. }
  153. }
  154. // No variables were rewritten. Just return the original node.
  155. if (boxes.Count == 0) {
  156. return node;
  157. }
  158. var boxesArray = Expression.NewArrayInit(typeof(IStrongBox), boxes);
  159. // All of them were rewritten. Just return the array, wrapped in a
  160. // read-only collection.
  161. if (vars.Count == 0) {
  162. return Expression.Invoke(
  163. Expression.Constant((Func<IStrongBox[], IRuntimeVariables>)RuntimeVariables.Create),
  164. boxesArray
  165. );
  166. }
  167. // Otherwise, we need to return an object that merges them
  168. Func<IRuntimeVariables, IRuntimeVariables, int[], IRuntimeVariables> helper = MergedRuntimeVariables.Create;
  169. return Expression.Invoke(AstUtils.Constant(helper), Expression.RuntimeVariables(vars), boxesArray, AstUtils.Constant(indexes));
  170. }
  171. protected override Expression VisitParameter(ParameterExpression node) {
  172. Expression box = GetBox(node);
  173. if (box == null) {
  174. return node;
  175. }
  176. // Convert can go away if we switch to strongly typed StrongBox
  177. return Ast.Utils.Convert(Expression.Field(box, "Value"), node.Type);
  178. }
  179. protected override Expression VisitBinary(BinaryExpression node) {
  180. if (node.NodeType == ExpressionType.Assign &&
  181. node.Left.NodeType == ExpressionType.Parameter) {
  182. var variable = (ParameterExpression)node.Left;
  183. Expression box = GetBox(variable);
  184. if (box != null) {
  185. // We need to convert to object to store the value in the box.
  186. return Expression.Block(
  187. new[] { variable },
  188. Expression.Assign(variable, Visit(node.Right)),
  189. Expression.Assign(Expression.Field(box, "Value"), Ast.Utils.Convert(variable, typeof(object))),
  190. variable
  191. );
  192. }
  193. }
  194. return base.VisitBinary(node);
  195. }
  196. private IndexExpression GetBox(ParameterExpression variable) {
  197. // Skip variables that are shadowed by a nested scope/lambda
  198. foreach (Set<ParameterExpression> hidden in _shadowedVars) {
  199. if (hidden.Contains(variable)) {
  200. return null;
  201. }
  202. }
  203. int index;
  204. if (_closureVars.TryGetValue(variable, out index)) {
  205. return Expression.ArrayAccess(_closureArray, AstUtils.Constant(index));
  206. }
  207. throw new InvalidOperationException("unbound variable: " + variable);
  208. }
  209. protected override Expression VisitExtension(Expression node) {
  210. // Reduce extensions now so we can find embedded variables
  211. return Visit(node.ReduceExtensions());
  212. }
  213. #region MergedRuntimeVariables
  214. /// <summary>
  215. /// Provides a list of variables, supporing read/write of the values
  216. /// </summary>
  217. private sealed class MergedRuntimeVariables : IRuntimeVariables {
  218. private readonly IRuntimeVariables _first;
  219. private readonly IRuntimeVariables _second;
  220. // For reach item, the index into the first or second list
  221. // Positive values mean the first array, negative means the second
  222. private readonly int[] _indexes;
  223. private MergedRuntimeVariables(IRuntimeVariables first, IRuntimeVariables second, int[] indexes) {
  224. _first = first;
  225. _second = second;
  226. _indexes = indexes;
  227. }
  228. internal static IRuntimeVariables Create(IRuntimeVariables first, IRuntimeVariables second, int[] indexes) {
  229. return new MergedRuntimeVariables(first, second, indexes);
  230. }
  231. int IRuntimeVariables.Count {
  232. get { return _indexes.Length; }
  233. }
  234. object IRuntimeVariables.this[int index] {
  235. get {
  236. index = _indexes[index];
  237. return (index >= 0) ? _first[index] : _second[-1 - index];
  238. }
  239. set {
  240. index = _indexes[index];
  241. if (index >= 0) {
  242. _first[index] = value;
  243. } else {
  244. _second[-1 - index] = value;
  245. }
  246. }
  247. }
  248. }
  249. #endregion
  250. #endregion
  251. }
  252. }