Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
LightLambdaClosureVisitor.cs305 linesDownload Raw Back to interpreter
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. A6 * copy of the license can be found in the License.html file at the root of this distribution. If7 * you cannot locate the Apache License, Version 2.0, please send an email to8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound9 * 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 !CLR217using System.Linq.Expressions;18#else19using Microsoft.Scripting.Ast;20#endif21using System.Collections.Generic;22using System.Runtime.CompilerServices;23using AstUtils = System.Management.Automation.Interpreter.Utils;24 25namespace System.Management.Automation.Interpreter26{27    /// <summary>28    /// Visits a LambdaExpression, replacing the constants with direct accesses29    /// to their StrongBox fields. This is very similar to what30    /// ExpressionQuoter does for LambdaCompiler.31    ///32    /// Also inserts debug information tracking similar to what the interpreter33    /// would do.34    /// </summary>35    internal sealed class LightLambdaClosureVisitor : ExpressionVisitor36    {37        /// <summary>38        /// Local variable mapping.39        /// </summary>40        private readonly Dictionary<ParameterExpression, LocalVariable> _closureVars;41 42        /// <summary>43        /// The variable that holds onto the StrongBox{object}[] closure from44        /// the interpreter.45        /// </summary>46        private readonly ParameterExpression _closureArray;47 48        /// <summary>49        /// A stack of variables that are defined in nested scopes. We search50        /// this first when resolving a variable in case a nested scope shadows51        /// one of our variable instances.52        /// </summary>53        private readonly Stack<HashSet<ParameterExpression>> _shadowedVars = new Stack<HashSet<ParameterExpression>>();54 55        private LightLambdaClosureVisitor(Dictionary<ParameterExpression, LocalVariable> closureVariables, ParameterExpression closureArray)56        {57            Assert.NotNull(closureVariables, closureArray);58            _closureArray = closureArray;59            _closureVars = closureVariables;60        }61 62        /// <summary>63        /// Walks the lambda and produces a higher order function, which can be64        /// used to bind the lambda to a closure array from the interpreter.65        /// </summary>66        /// <param name="lambda">The lambda to bind.</param>67        /// <param name="closureVariables">Variables which are being accessed defined in the outer scope.</param>68        /// <returns>A delegate that can be called to produce a delegate bound to the passed in closure array.</returns>69        internal static Func<StrongBox<object>[], Delegate> BindLambda(LambdaExpression lambda, Dictionary<ParameterExpression, LocalVariable> closureVariables)70        {71            // 1. Create rewriter72            var closure = Expression.Parameter(typeof(StrongBox<object>[]), "closure");73            var visitor = new LightLambdaClosureVisitor(closureVariables, closure);74 75            // 2. Visit the lambda76            lambda = (LambdaExpression)visitor.Visit(lambda);77 78            // 3. Create a higher-order function which fills in the parameters79            var result = Expression.Lambda<Func<StrongBox<object>[], Delegate>>(lambda, closure);80 81            // 4. Compile it82            return result.Compile();83        }84 85        #region closures86 87        protected override Expression VisitLambda<T>(Expression<T> node)88        {89            _shadowedVars.Push(new HashSet<ParameterExpression>(node.Parameters));90            Expression b = Visit(node.Body);91            _shadowedVars.Pop();92            if (b == node.Body)93            {94                return node;95            }96 97            return Expression.Lambda<T>(b, node.Name, node.TailCall, node.Parameters);98        }99 100        protected override Expression VisitBlock(BlockExpression node)101        {102            if (node.Variables.Count > 0)103            {104                _shadowedVars.Push(new HashSet<ParameterExpression>(node.Variables));105            }106 107            var b = Visit(node.Expressions);108            if (node.Variables.Count > 0)109            {110                _shadowedVars.Pop();111            }112 113            if (b == node.Expressions)114            {115                return node;116            }117 118            return Expression.Block(node.Variables, b);119        }120 121        protected override CatchBlock VisitCatchBlock(CatchBlock node)122        {123            if (node.Variable != null)124            {125                _shadowedVars.Push(new HashSet<ParameterExpression>(new[] { node.Variable }));126            }127 128            Expression b = Visit(node.Body);129            Expression f = Visit(node.Filter);130            if (node.Variable != null)131            {132                _shadowedVars.Pop();133            }134 135            if (b == node.Body && f == node.Filter)136            {137                return node;138            }139 140            return Expression.MakeCatchBlock(node.Test, node.Variable, b, f);141        }142 143        protected override Expression VisitRuntimeVariables(RuntimeVariablesExpression node)144        {145            int count = node.Variables.Count;146            var boxes = new List<Expression>();147            var vars = new List<ParameterExpression>();148            var indexes = new int[count];149            for (int i = 0; i < count; i++)150            {151                Expression box = GetClosureItem(node.Variables[i], false);152                if (box == null)153                {154                    indexes[i] = vars.Count;155                    vars.Add(node.Variables[i]);156                }157                else158                {159                    indexes[i] = -1 - boxes.Count;160                    boxes.Add(box);161                }162            }163 164            // No variables were rewritten. Just return the original node.165            if (boxes.Count == 0)166            {167                return node;168            }169 170            var boxesArray = Expression.NewArrayInit(typeof(IStrongBox), boxes);171 172            // All of them were rewritten. Just return the array, wrapped in a173            // read-only collection.174            if (vars.Count == 0)175            {176                return Expression.Invoke(177                    Expression.Constant((Func<IStrongBox[], IRuntimeVariables>)RuntimeVariables.Create),178                    boxesArray179                );180            }181 182            // Otherwise, we need to return an object that merges them183            Func<IRuntimeVariables, IRuntimeVariables, int[], IRuntimeVariables> helper = MergedRuntimeVariables.Create;184            return Expression.Invoke(AstUtils.Constant(helper), Expression.RuntimeVariables(vars), boxesArray, AstUtils.Constant(indexes));185        }186 187        protected override Expression VisitParameter(ParameterExpression node)188        {189            Expression closureItem = GetClosureItem(node, true);190            if (closureItem == null)191            {192                return node;193            }194            // Convert can go away if we switch to strongly typed StrongBox195            return AstUtils.Convert(closureItem, node.Type);196        }197 198        protected override Expression VisitBinary(BinaryExpression node)199        {200            if (node.NodeType == ExpressionType.Assign &&201                node.Left.NodeType == ExpressionType.Parameter)202            {203                var variable = (ParameterExpression)node.Left;204                Expression closureItem = GetClosureItem(variable, true);205                if (closureItem != null)206                {207                    // We need to convert to object to store the value in the box.208                    return Expression.Block(209                        new[] { variable },210                        Expression.Assign(variable, Visit(node.Right)),211                        Expression.Assign(closureItem, AstUtils.Convert(variable, typeof(object))),212                        variable213                    );214                }215            }216 217            return base.VisitBinary(node);218        }219 220        private Expression GetClosureItem(ParameterExpression variable, bool unbox)221        {222            // Skip variables that are shadowed by a nested scope/lambda223            foreach (HashSet<ParameterExpression> hidden in _shadowedVars)224            {225                if (hidden.Contains(variable))226                {227                    return null;228                }229            }230 231            LocalVariable loc;232            if (!_closureVars.TryGetValue(variable, out loc))233            {234                throw new InvalidOperationException("unbound variable: " + variable.Name);235            }236 237            var result = loc.LoadFromArray(null, _closureArray);238            return (unbox) ? LightCompiler.Unbox(result) : result;239        }240 241        protected override Expression VisitExtension(Expression node)242        {243            // Reduce extensions now so we can find embedded variables244            return Visit(node.ReduceExtensions());245        }246 247        #region MergedRuntimeVariables248 249        /// <summary>250        /// Provides a list of variables, supporting read/write of the values.251        /// </summary>252        private sealed class MergedRuntimeVariables : IRuntimeVariables253        {254            private readonly IRuntimeVariables _first;255            private readonly IRuntimeVariables _second;256 257            // For reach item, the index into the first or second list258            // Positive values mean the first array, negative means the second259            private readonly int[] _indexes;260 261            private MergedRuntimeVariables(IRuntimeVariables first, IRuntimeVariables second, int[] indexes)262            {263                _first = first;264                _second = second;265                _indexes = indexes;266            }267 268            internal static IRuntimeVariables Create(IRuntimeVariables first, IRuntimeVariables second, int[] indexes)269            {270                return new MergedRuntimeVariables(first, second, indexes);271            }272 273            int IRuntimeVariables.Count274            {275                get { return _indexes.Length; }276            }277 278            object IRuntimeVariables.this[int index]279            {280                get281                {282                    index = _indexes[index];283                    return (index >= 0) ? _first[index] : _second[-1 - index];284                }285 286                set287                {288                    index = _indexes[index];289                    if (index >= 0)290                    {291                        _first[index] = value;292                    }293                    else294                    {295                        _second[-1 - index] = value;296                    }297                }298            }299        }300        #endregion301 302        #endregion303    }304}305