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