PageRenderTime 46ms CodeModel.GetById 5ms app.highlight 30ms RepoModel.GetById 3ms app.codeStats 1ms

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

#
C# | 247 lines | 171 code | 27 blank | 49 comment | 34 complexity | 8243f3290a8b7bda724371131d5f6fbc 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
 16#if CLR2
 17using Microsoft.Scripting.Ast;
 18using Microsoft.Scripting.Ast.Compiler;
 19#else
 20using System.Linq.Expressions;
 21using System.Linq.Expressions.Compiler;
 22#endif
 23
 24using System.Collections.Generic;
 25using System.ComponentModel;
 26using System.Diagnostics;
 27using System.Dynamic.Utils;
 28
 29namespace System.Runtime.CompilerServices {
 30    public partial class RuntimeOps {
 31        /// <summary>
 32        /// Quotes the provided expression tree.
 33        /// </summary>
 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);
 43        }
 44
 45        /// <summary>
 46        /// Combines two runtime variable lists and returns a new list.
 47        /// </summary>
 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);
 55        }
 56
 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.
 61        //
 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;
 66
 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>>();
 71
 72            internal ExpressionQuoter(HoistedLocals scope, object[] locals) {
 73                _scope = scope;
 74                _locals = locals;
 75            }
 76
 77            protected internal override Expression VisitLambda<T>(Expression<T> node) {
 78                _shadowedVars.Push(new Set<ParameterExpression>(node.Parameters));
 79                Expression b = Visit(node.Body);
 80                _shadowedVars.Pop();
 81                if (b == node.Body) {
 82                    return node;
 83                }
 84                return Expression.Lambda<T>(b, node.Name, node.TailCall, node.Parameters);
 85            }
 86
 87            protected internal override Expression VisitBlock(BlockExpression node) {
 88                if (node.Variables.Count > 0) {
 89                    _shadowedVars.Push(new Set<ParameterExpression>(node.Variables));
 90                }
 91                var b = Visit(node.Expressions);
 92                if (node.Variables.Count > 0) {
 93                    _shadowedVars.Pop();
 94                }
 95                if (b == node.Expressions) {
 96                    return node;
 97                }
 98                return Expression.Block(node.Variables, b);
 99            }
100
101            protected override CatchBlock VisitCatchBlock(CatchBlock node) {
102                if (node.Variable != null) {
103                    _shadowedVars.Push(new Set<ParameterExpression>(new[] { node.Variable }));
104                }
105                Expression b = Visit(node.Body);
106                Expression f = Visit(node.Filter);
107                if (node.Variable != null) {
108                    _shadowedVars.Pop();
109                }
110                if (b == node.Body && f == node.Filter) {
111                    return node;
112                }
113                return Expression.MakeCatchBlock(node.Test, node.Variable, b, f);
114            }
115
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]);
123                    if (box == null) {
124                        indexes[i] = vars.Count;
125                        vars.Add(node.Variables[i]);
126                    } else {
127                        indexes[i] = -1 - boxes.Count;
128                        boxes.Add(box);
129                    }
130                }
131
132                // No variables were rewritten. Just return the original node
133                if (boxes.Count == 0) {
134                    return node;
135                }
136
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) {
140                    return boxesConst;
141                }
142
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())),
147                    boxesConst,
148                    Expression.Constant(indexes)
149                );
150            }
151
152            protected internal override Expression VisitParameter(ParameterExpression node) {
153                IStrongBox box = GetBox(node);
154                if (box == null) {
155                    return node;
156                }
157                return Expression.Field(Expression.Constant(box), "Value");
158            }
159
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)) {
164                        return null;
165                    }
166                }
167
168                HoistedLocals scope = _scope;
169                object[] locals = _locals;
170                while (true) {
171                    int hoistIndex;
172                    if (scope.Indexes.TryGetValue(variable, out hoistIndex)) {
173                        return (IStrongBox)locals[hoistIndex];
174                    }
175                    scope = scope.Parent;
176                    if (scope == null) {
177                        break;
178                    }
179                    locals = HoistedLocals.GetParent(locals);
180                }
181
182                // Unbound variable: an error should've been thrown already
183                // from VariableBinder
184                throw ContractUtils.Unreachable;
185            }
186        }
187
188        private sealed class RuntimeVariables : IRuntimeVariables {
189            private readonly IStrongBox[] _boxes;
190
191            internal RuntimeVariables(IStrongBox[] boxes) {
192                _boxes = boxes;
193            }
194
195            int IRuntimeVariables.Count {
196                get { return _boxes.Length; }
197            }
198
199            object IRuntimeVariables.this[int index] {
200                get {
201                    return _boxes[index].Value;
202                }
203                set {
204                    _boxes[index].Value = value;
205                }
206            }
207        }
208
209        /// <summary>
210        /// Provides a list of variables, supporing read/write of the values
211        /// Exposed via RuntimeVariablesExpression
212        /// </summary>
213        private sealed class MergedRuntimeVariables : IRuntimeVariables {
214            private readonly IRuntimeVariables _first;
215            private readonly IRuntimeVariables _second;
216
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;
220
221            internal MergedRuntimeVariables(IRuntimeVariables first, IRuntimeVariables second, int[] indexes) {
222                _first = first;
223                _second = second;
224                _indexes = indexes;
225            }
226
227            public int Count {
228                get { return _indexes.Length; }
229            }
230
231            public object this[int index] {
232                get {
233                    index = _indexes[index];
234                    return (index >= 0) ? _first[index] : _second[-1 - index];
235                }
236                set {
237                    index = _indexes[index];
238                    if (index >= 0) {
239                        _first[index] = value;
240                    } else {
241                        _second[-1 - index] = value;
242                    }
243                }
244            }
245        }
246    }
247}