1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
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.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
17 using Microsoft.Scripting.Ast;
18 using Microsoft.Scripting.Ast.Compiler;
20 using System.Linq.Expressions;
21 using System.Linq.Expressions.Compiler;
24 using System.Collections.Generic;
25 using System.ComponentModel;
26 using System.Diagnostics;
27 using System.Dynamic.Utils;
29 namespace System.Runtime.CompilerServices {
30 public partial class RuntimeOps {
32 /// Quotes the provided expression tree.
34 /// <param name="expression">The expression to quote.</param>
35 /// <param name="hoistedLocals">The hoisted local state provided by the compiler.</param>
36 /// <param name="locals">The actual hoisted local values.</param>
37 /// <returns>The quoted expression.</returns>
38 [Obsolete("do not use this method", true), EditorBrowsable(EditorBrowsableState.Never)]
39 public static Expression Quote(Expression expression, object hoistedLocals, object[] locals) {
40 Debug.Assert(hoistedLocals != null && locals != null);
41 var quoter = new ExpressionQuoter((HoistedLocals)hoistedLocals, locals);
42 return quoter.Visit(expression);
46 /// Combines two runtime variable lists and returns a new list.
48 /// <param name="first">The first list.</param>
49 /// <param name="second">The second list.</param>
50 /// <param name="indexes">The index array indicating which list to get variables from.</param>
51 /// <returns>The merged runtime variables.</returns>
52 [Obsolete("do not use this method", true), EditorBrowsable(EditorBrowsableState.Never)]
53 public static IRuntimeVariables MergeRuntimeVariables(IRuntimeVariables first, IRuntimeVariables second, int[] indexes) {
54 return new MergedRuntimeVariables(first, second, indexes);
57 // Modifies a quoted Expression instance by changing hoisted variables and
58 // parameters into hoisted local references. The variable's StrongBox is
59 // burned as a constant, and all hoisted variables/parameters are rewritten
60 // as indexing expressions.
62 // The behavior of Quote is indended to be like C# and VB expression quoting
63 private sealed class ExpressionQuoter : ExpressionVisitor {
64 private readonly HoistedLocals _scope;
65 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>>();
72 internal ExpressionQuoter(HoistedLocals scope, object[] locals) {
77 protected internal override Expression VisitLambda<T>(Expression<T> node) {
78 _shadowedVars.Push(new Set<ParameterExpression>(node.Parameters));
79 Expression b = Visit(node.Body);
84 return Expression.Lambda<T>(b, node.Name, node.TailCall, node.Parameters);
87 protected internal override Expression VisitBlock(BlockExpression node) {
88 if (node.Variables.Count > 0) {
89 _shadowedVars.Push(new Set<ParameterExpression>(node.Variables));
91 var b = Visit(node.Expressions);
92 if (node.Variables.Count > 0) {
95 if (b == node.Expressions) {
98 return Expression.Block(node.Variables, b);
101 protected override CatchBlock VisitCatchBlock(CatchBlock node) {
102 if (node.Variable != null) {
103 _shadowedVars.Push(new Set<ParameterExpression>(new[] { node.Variable }));
105 Expression b = Visit(node.Body);
106 Expression f = Visit(node.Filter);
107 if (node.Variable != null) {
110 if (b == node.Body && f == node.Filter) {
113 return Expression.MakeCatchBlock(node.Test, node.Variable, b, f);
116 protected internal override Expression VisitRuntimeVariables(RuntimeVariablesExpression node) {
117 int count = node.Variables.Count;
118 var boxes = new List<IStrongBox>();
119 var vars = new List<ParameterExpression>();
120 var indexes = new int[count];
121 for (int i = 0; i < count; i++) {
122 IStrongBox box = GetBox(node.Variables[i]);
124 indexes[i] = vars.Count;
125 vars.Add(node.Variables[i]);
127 indexes[i] = -1 - boxes.Count;
132 // No variables were rewritten. Just return the original node
133 if (boxes.Count == 0) {
137 var boxesConst = Expression.Constant(new RuntimeVariables(boxes.ToArray()), typeof(IRuntimeVariables));
138 // All of them were rewritten. Just return the array as a constant
139 if (vars.Count == 0) {
143 // Otherwise, we need to return an object that merges them
144 return Expression.Call(
145 typeof(RuntimeOps).GetMethod("MergeRuntimeVariables"),
146 Expression.RuntimeVariables(new TrueReadOnlyCollection<ParameterExpression>(vars.ToArray())),
148 Expression.Constant(indexes)
152 protected internal override Expression VisitParameter(ParameterExpression node) {
153 IStrongBox box = GetBox(node);
157 return Expression.Field(Expression.Constant(box), "Value");
160 private IStrongBox GetBox(ParameterExpression variable) {
161 // Skip variables that are shadowed by a nested scope/lambda
162 foreach (Set<ParameterExpression> hidden in _shadowedVars) {
163 if (hidden.Contains(variable)) {
168 HoistedLocals scope = _scope;
169 object[] locals = _locals;
172 if (scope.Indexes.TryGetValue(variable, out hoistIndex)) {
173 return (IStrongBox)locals[hoistIndex];
175 scope = scope.Parent;
179 locals = HoistedLocals.GetParent(locals);
182 // Unbound variable: an error should've been thrown already
183 // from VariableBinder
184 throw ContractUtils.Unreachable;
188 private sealed class RuntimeVariables : IRuntimeVariables {
189 private readonly IStrongBox[] _boxes;
191 internal RuntimeVariables(IStrongBox[] boxes) {
195 int IRuntimeVariables.Count {
196 get { return _boxes.Length; }
199 object IRuntimeVariables.this[int index] {
201 return _boxes[index].Value;
204 _boxes[index].Value = value;
210 /// Provides a list of variables, supporing read/write of the values
211 /// Exposed via RuntimeVariablesExpression
213 private sealed class MergedRuntimeVariables : IRuntimeVariables {
214 private readonly IRuntimeVariables _first;
215 private readonly IRuntimeVariables _second;
217 // For reach item, the index into the first or second list
218 // Positive values mean the first array, negative means the second
219 private readonly int[] _indexes;
221 internal MergedRuntimeVariables(IRuntimeVariables first, IRuntimeVariables second, int[] indexes) {
228 get { return _indexes.Length; }
231 public object this[int index] {
233 index = _indexes[index];
234 return (index >= 0) ? _first[index] : _second[-1 - index];
237 index = _indexes[index];
239 _first[index] = value;
241 _second[-1 - index] = value;