MegaBites-AI/Windows-powershell
0372
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 