PageRenderTime 2102ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/src/System.Management.Automation/engine/interpreter/LightLambdaClosureVisitor.cs

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