PageRenderTime 105ms CodeModel.GetById 60ms app.highlight 17ms RepoModel.GetById 24ms app.codeStats 0ms

/Microsoft.Scripting/Interpreter/LightLambdaClosureVisitor.cs

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