PageRenderTime 71ms CodeModel.GetById 23ms RepoModel.GetById 2ms app.codeStats 0ms

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

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