Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes308downloads
ControlFlowInstructions.cs796 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#endif19using System.Collections.Generic;20using System.Diagnostics;21using System.Management.Automation.Language;22using System.Runtime.CompilerServices;23using System.Threading;24 25namespace System.Management.Automation.Interpreter26{27    using LoopFunc = Func<object[], StrongBox<object>[], InterpretedFrame, int>;28 29    internal abstract class OffsetInstruction : Instruction30    {31        internal const int Unknown = Int32.MinValue;32        internal const int CacheSize = 32;33 34        // the offset to jump to (relative to this instruction):35        protected int _offset = Unknown;36 37        public int Offset { get { return _offset; } }38 39        public abstract Instruction[] Cache { get; }40 41        public Instruction Fixup(int offset)42        {43            Debug.Assert(_offset == Unknown && offset != Unknown);44            _offset = offset;45 46            var cache = Cache;47            if (cache != null && offset >= 0 && offset < cache.Length)48            {49                return cache[offset] ?? (cache[offset] = this);50            }51 52            return this;53        }54 55        public override string ToDebugString(int instructionIndex, object cookie, Func<int, int> labelIndexer, IList<object> objects)56        {57            return ToString() + (_offset != Unknown ? " -> " + (instructionIndex + _offset) : string.Empty);58        }59 60        public override string ToString()61        {62            return InstructionName + (_offset == Unknown ? "(?)" : "(" + _offset + ")");63        }64    }65 66    internal sealed class BranchFalseInstruction : OffsetInstruction67    {68        private static Instruction[] s_cache;69 70        public override Instruction[] Cache71        {72            get { return s_cache ??= new Instruction[CacheSize]; }73        }74 75        internal BranchFalseInstruction()76        {77        }78 79        public override int ConsumedStack { get { return 1; } }80 81        public override int Run(InterpretedFrame frame)82        {83            Debug.Assert(_offset != Unknown);84 85            if (!(bool)frame.Pop())86            {87                return _offset;88            }89 90            return +1;91        }92    }93 94    internal sealed class BranchTrueInstruction : OffsetInstruction95    {96        private static Instruction[] s_cache;97 98        public override Instruction[] Cache99        {100            get { return s_cache ??= new Instruction[CacheSize]; }101        }102 103        internal BranchTrueInstruction()104        {105        }106 107        public override int ConsumedStack { get { return 1; } }108 109        public override int Run(InterpretedFrame frame)110        {111            Debug.Assert(_offset != Unknown);112 113            if ((bool)frame.Pop())114            {115                return _offset;116            }117 118            return +1;119        }120    }121 122    internal sealed class CoalescingBranchInstruction : OffsetInstruction123    {124        private static Instruction[] s_cache;125 126        public override Instruction[] Cache127        {128            get { return s_cache ??= new Instruction[CacheSize]; }129        }130 131        internal CoalescingBranchInstruction()132        {133        }134 135        public override int ConsumedStack { get { return 1; } }136 137        public override int ProducedStack { get { return 1; } }138 139        public override int Run(InterpretedFrame frame)140        {141            Debug.Assert(_offset != Unknown);142 143            if (frame.Peek() != null)144            {145                return _offset;146            }147 148            return +1;149        }150    }151 152    internal class BranchInstruction : OffsetInstruction153    {154        private static Instruction[][][] s_caches;155 156        public override Instruction[] Cache157        {158            get159            {160                s_caches ??= new Instruction[2][][] { new Instruction[2][], new Instruction[2][] };161 162                return s_caches[ConsumedStack][ProducedStack] ?? (s_caches[ConsumedStack][ProducedStack] = new Instruction[CacheSize]);163            }164        }165 166        internal readonly bool _hasResult;167        internal readonly bool _hasValue;168 169        internal BranchInstruction()170            : this(false, false)171        {172        }173 174        public BranchInstruction(bool hasResult, bool hasValue)175        {176            _hasResult = hasResult;177            _hasValue = hasValue;178        }179 180        public override int ConsumedStack181        {182            get { return _hasValue ? 1 : 0; }183        }184 185        public override int ProducedStack186        {187            get { return _hasResult ? 1 : 0; }188        }189 190        public override int Run(InterpretedFrame frame)191        {192            Debug.Assert(_offset != Unknown);193 194            return _offset;195        }196    }197 198    internal abstract class IndexedBranchInstruction : Instruction199    {200        protected const int CacheSize = 32;201 202        internal readonly int _labelIndex;203 204        protected IndexedBranchInstruction(int labelIndex)205        {206            _labelIndex = labelIndex;207        }208 209        public RuntimeLabel GetLabel(InterpretedFrame frame)210        {211            Debug.Assert(_labelIndex != UnknownInstrIndex);212            return frame.Interpreter._labels[_labelIndex];213        }214 215        public override string ToDebugString(int instructionIndex, object cookie, Func<int, int> labelIndexer, IList<object> objects)216        {217            Debug.Assert(_labelIndex != UnknownInstrIndex);218            int targetIndex = labelIndexer(_labelIndex);219            return ToString() + (targetIndex != BranchLabel.UnknownIndex ? " -> " + targetIndex : string.Empty);220        }221 222        public override string ToString()223        {224            Debug.Assert(_labelIndex != UnknownInstrIndex);225            return InstructionName + "[" + _labelIndex + "]";226        }227    }228 229    /// <summary>230    /// This instruction implements a goto expression that can jump out of any expression.231    /// It pops values (arguments) from the evaluation stack that the expression tree nodes in between232    /// the goto expression and the target label node pushed and not consumed yet.233    /// A goto expression can jump into a node that evaluates arguments only if it carries234    /// a value and jumps right after the first argument (the carried value will be used as the first argument).235    /// Goto can jump into an arbitrary child of a BlockExpression since the block doesn't accumulate values236    /// on evaluation stack as its child expressions are being evaluated.237    ///238    /// Goto needs to execute any finally blocks on the way to the target label.239    /// <example>240    /// {241    ///     f(1, 2, try { g(3, 4, try { goto L } finally { ... }, 6) } finally { ... }, 7, 8)242    ///     L: ...243    /// }244    /// </example>245    /// The goto expression here jumps to label L while having 4 items on evaluation stack (1, 2, 3 and 4).246    /// The jump needs to execute both finally blocks, the first one on stack level 4 the247    /// second one on stack level 2. So, it needs to jump the first finally block, pop 2 items from the stack,248    /// run second finally block and pop another 2 items from the stack and set instruction pointer to label L.249    /// </summary>250    internal sealed class GotoInstruction : IndexedBranchInstruction251    {252        private const int Variants = 4;253 254        private static readonly GotoInstruction[] s_cache = new GotoInstruction[Variants * CacheSize];255 256        private readonly bool _hasResult;257 258        // TODO: We can remember hasValue in label and look it up when calculating stack balance. That would save some cache.259        private readonly bool _hasValue;260 261        // The values should technically be Consumed = 1, Produced = 1 for gotos that target a label whose continuation depth262        // is different from the current continuation depth. This is because we will consume one continuation from the _continuations263        // and at meantime produce a new _pendingContinuation. However, in case of forward gotos, we don't not know that is the264        // case until the label is emitted. By then the consumed and produced stack information is useless.265        // The important thing here is that the stack balance is 0.266        public override int ConsumedContinuations { get { return 0; } }267 268        public override int ProducedContinuations { get { return 0; } }269 270        public override int ConsumedStack271        {272            get { return _hasValue ? 1 : 0; }273        }274 275        public override int ProducedStack276        {277            get { return _hasResult ? 1 : 0; }278        }279 280        private GotoInstruction(int targetIndex, bool hasResult, bool hasValue)281            : base(targetIndex)282        {283            _hasResult = hasResult;284            _hasValue = hasValue;285        }286 287        internal static GotoInstruction Create(int labelIndex, bool hasResult, bool hasValue)288        {289            if (labelIndex < CacheSize)290            {291                var index = Variants * labelIndex | (hasResult ? 2 : 0) | (hasValue ? 1 : 0);292                return s_cache[index] ?? (s_cache[index] = new GotoInstruction(labelIndex, hasResult, hasValue));293            }294 295            return new GotoInstruction(labelIndex, hasResult, hasValue);296        }297 298        public override int Run(InterpretedFrame frame)299        {300            // goto the target label or the current finally continuation:301            return frame.Goto(_labelIndex, _hasValue ? frame.Pop() : Interpreter.NoValue, gotoExceptionHandler: false);302        }303    }304 305    internal sealed class EnterTryCatchFinallyInstruction : IndexedBranchInstruction306    {307        private readonly bool _hasFinally = false;308        private TryCatchFinallyHandler _tryHandler;309 310        internal void SetTryHandler(TryCatchFinallyHandler tryHandler)311        {312            Debug.Assert(_tryHandler == null && tryHandler != null, "the tryHandler can be set only once");313            _tryHandler = tryHandler;314        }315 316        public override int ProducedContinuations { get { return _hasFinally ? 1 : 0; } }317 318        private EnterTryCatchFinallyInstruction(int targetIndex, bool hasFinally)319            : base(targetIndex)320        {321            _hasFinally = hasFinally;322        }323 324        internal static EnterTryCatchFinallyInstruction CreateTryFinally(int labelIndex)325        {326            return new EnterTryCatchFinallyInstruction(labelIndex, true);327        }328 329        internal static EnterTryCatchFinallyInstruction CreateTryCatch()330        {331            return new EnterTryCatchFinallyInstruction(UnknownInstrIndex, false);332        }333 334        public override int Run(InterpretedFrame frame)335        {336            Debug.Assert(_tryHandler != null, "the tryHandler must be set already");337 338            if (_hasFinally)339            {340                // Push finally.341                frame.PushContinuation(_labelIndex);342            }343 344            int prevInstrIndex = frame.InstructionIndex;345            frame.InstructionIndex++;346 347            // Start to run the try/catch/finally blocks348            var instructions = frame.Interpreter.Instructions.Instructions;349            try350            {351                // run the try block352                int index = frame.InstructionIndex;353                while (index >= _tryHandler.TryStartIndex && index < _tryHandler.TryEndIndex)354                {355                    index += instructions[index].Run(frame);356                    frame.InstructionIndex = index;357                }358 359                // we finish the try block and is about to jump out of the try/catch blocks360                if (index == _tryHandler.GotoEndTargetIndex)361                {362                    // run the 'Goto' that jumps out of the try/catch/finally blocks363                    Debug.Assert(instructions[index] is GotoInstruction, "should be the 'Goto' instruction that jumps out the try/catch/finally");364                    frame.InstructionIndex += instructions[index].Run(frame);365                }366            }367            catch (RethrowException)368            {369                // a rethrow instruction in the try handler gets to run370                throw;371            }372            catch (Exception exception)373            {374                frame.SaveTraceToException(exception);375                // rethrow if there is no catch blocks defined for this try block376                if (!_tryHandler.IsCatchBlockExist) { throw; }377 378                // Search for the best handler in the TryCatchFinally block. If no suitable handler is found, rethrow379                ExceptionHandler exHandler;380                frame.InstructionIndex += _tryHandler.GotoHandler(frame, exception, out exHandler);381                if (exHandler == null) { throw; }382                bool rethrow = false;383                try384                {385                    // run the catch block386                    int index = frame.InstructionIndex;387                    while (index >= exHandler.HandlerStartIndex && index < exHandler.HandlerEndIndex)388                    {389                        index += instructions[index].Run(frame);390                        frame.InstructionIndex = index;391                    }392 393                    // we finish the catch block and is about to jump out of the try/catch blocks394                    if (index == _tryHandler.GotoEndTargetIndex)395                    {396                        // run the 'Goto' that jumps out of the try/catch/finally blocks397                        Debug.Assert(instructions[index] is GotoInstruction, "should be the 'Goto' instruction that jumps out the try/catch/finally");398                        frame.InstructionIndex += instructions[index].Run(frame);399                    }400                }401                catch (RethrowException)402                {403                    // a rethrow instruction in a catch block gets to run404                    rethrow = true;405                }406 407                if (rethrow) { throw; }408            }409            finally410            {411                if (_tryHandler.IsFinallyBlockExist)412                {413                    // We get to the finally block in two paths:414                    //  1. Jump from the try/catch blocks. This includes two sub-routes:415                    //        a. 'Goto' instruction in the middle of try/catch block416                    //        b. try/catch block runs to its end. Then the 'Goto(end)' will be trigger to jump out of the try/catch block417                    //  2. Exception thrown from the try/catch blocks418                    // In the first path, the continuation mechanism works and frame.InstructionIndex will be updated to point to the first instruction of the finally block419                    // In the second path, the continuation mechanism is not involved and frame.InstructionIndex is not updated420#if DEBUG421                    bool isFromJump = frame.IsJumpHappened();422                    Debug.Assert(!isFromJump || _tryHandler.FinallyStartIndex == frame.InstructionIndex, "we should already jump to the first instruction of the finally");423#endif424                    // run the finally block425                    // we cannot jump out of the finally block, and we cannot have an immediate rethrow in it426                    int index = frame.InstructionIndex = _tryHandler.FinallyStartIndex;427                    while (index >= _tryHandler.FinallyStartIndex && index < _tryHandler.FinallyEndIndex)428                    {429                        index += instructions[index].Run(frame);430                        frame.InstructionIndex = index;431                    }432                }433            }434 435            return frame.InstructionIndex - prevInstrIndex;436        }437 438        public override string InstructionName439        {440            get { return _hasFinally ? "EnterTryFinally" : "EnterTryCatch"; }441        }442 443        public override string ToString()444        {445            return _hasFinally ? "EnterTryFinally[" + _labelIndex + "]" : "EnterTryCatch";446        }447    }448 449    /// <summary>450    /// The first instruction of finally block.451    /// </summary>452    internal sealed class EnterFinallyInstruction : IndexedBranchInstruction453    {454        private static readonly EnterFinallyInstruction[] s_cache = new EnterFinallyInstruction[CacheSize];455 456        public override int ProducedStack { get { return 2; } }457 458        public override int ConsumedContinuations { get { return 1; } }459 460        private EnterFinallyInstruction(int labelIndex)461            : base(labelIndex)462        {463        }464 465        internal static EnterFinallyInstruction Create(int labelIndex)466        {467            if (labelIndex < CacheSize)468            {469                return s_cache[labelIndex] ?? (s_cache[labelIndex] = new EnterFinallyInstruction(labelIndex));470            }471 472            return new EnterFinallyInstruction(labelIndex);473        }474 475        public override int Run(InterpretedFrame frame)476        {477            // If _pendingContinuation == -1 then we were getting into the finally block because an exception was thrown478            //      in this case we need to set the stack depth479            // Else we were getting into this finally block from a 'Goto' jump, and the stack depth is already set properly480            if (!frame.IsJumpHappened())481            {482                frame.SetStackDepth(GetLabel(frame).StackDepth);483            }484 485            frame.PushPendingContinuation();486            frame.RemoveContinuation();487            return 1;488        }489    }490 491    /// <summary>492    /// The last instruction of finally block.493    /// </summary>494    internal sealed class LeaveFinallyInstruction : Instruction495    {496        internal static readonly Instruction Instance = new LeaveFinallyInstruction();497 498        public override int ConsumedStack { get { return 2; } }499 500        private LeaveFinallyInstruction()501        {502        }503 504        public override int Run(InterpretedFrame frame)505        {506            frame.PopPendingContinuation();507 508            // If _pendingContinuation == -1 then we were getting into the finally block because an exception was thrown509            // In this case we just return 1, and the real instruction index will be calculated by GotoHandler later510            if (!frame.IsJumpHappened()) { return 1; }511            // jump to goto target or to the next finally:512            return frame.YieldToPendingContinuation();513        }514    }515 516    // no-op: we need this just to balance the stack depth.517    internal sealed class EnterExceptionHandlerInstruction : Instruction518    {519        internal static readonly EnterExceptionHandlerInstruction Void = new EnterExceptionHandlerInstruction(false);520        internal static readonly EnterExceptionHandlerInstruction NonVoid = new EnterExceptionHandlerInstruction(true);521 522        // True if try-expression is non-void.523        private readonly bool _hasValue;524 525        private EnterExceptionHandlerInstruction(bool hasValue)526        {527            _hasValue = hasValue;528        }529 530        // If an exception is throws in try-body the expression result of try-body is not evaluated and loaded to the stack.531        // So the stack doesn't contain the try-body's value when we start executing the handler.532        // However, while emitting instructions try block falls thru the catch block with a value on stack.533        // We need to declare it consumed so that the stack state upon entry to the handler corresponds to the real534        // stack depth after throw jumped to this catch block.535        public override int ConsumedStack { get { return _hasValue ? 1 : 0; } }536 537        // A variable storing the current exception is pushed to the stack by exception handling.538        // Catch handlers: The value is immediately popped and stored into a local.539        // Fault handlers: The value is kept on stack during fault handler evaluation.540        public override int ProducedStack { get { return 1; } }541 542        public override int Run(InterpretedFrame frame)543        {544            // nop (the exception value is pushed by the interpreter in HandleCatch)545            return 1;546        }547    }548 549    /// <summary>550    /// The last instruction of a catch exception handler.551    /// </summary>552    internal sealed class LeaveExceptionHandlerInstruction : IndexedBranchInstruction553    {554        private static readonly LeaveExceptionHandlerInstruction[] s_cache = new LeaveExceptionHandlerInstruction[2 * CacheSize];555 556        private readonly bool _hasValue;557 558        // The catch block yields a value if the body is non-void. This value is left on the stack.559        public override int ConsumedStack560        {561            get { return _hasValue ? 1 : 0; }562        }563 564        public override int ProducedStack565        {566            get { return _hasValue ? 1 : 0; }567        }568 569        private LeaveExceptionHandlerInstruction(int labelIndex, bool hasValue)570            : base(labelIndex)571        {572            _hasValue = hasValue;573        }574 575        internal static LeaveExceptionHandlerInstruction Create(int labelIndex, bool hasValue)576        {577            if (labelIndex < CacheSize)578            {579                int index = (2 * labelIndex) | (hasValue ? 1 : 0);580                return s_cache[index] ?? (s_cache[index] = new LeaveExceptionHandlerInstruction(labelIndex, hasValue));581            }582 583            return new LeaveExceptionHandlerInstruction(labelIndex, hasValue);584        }585 586        public override int Run(InterpretedFrame frame)587        {588            return GetLabel(frame).Index - frame.InstructionIndex;589        }590    }591 592    /// <summary>593    /// The last instruction of a fault exception handler.594    /// </summary>595    internal sealed class LeaveFaultInstruction : Instruction596    {597        internal static readonly Instruction NonVoid = new LeaveFaultInstruction(true);598        internal static readonly Instruction Void = new LeaveFaultInstruction(false);599 600        private readonly bool _hasValue;601 602        // The fault block has a value if the body is non-void, but the value is never used.603        // We compile the body of a fault block as void.604        // However, we keep the exception object that was pushed upon entering the fault block on the stack during execution of the block605        // and pop it at the end.606        public override int ConsumedStack607        {608            get { return 1; }609        }610 611        // While emitting instructions a non-void try-fault expression is expected to produce a value.612        public override int ProducedStack613        {614            get { return _hasValue ? 1 : 0; }615        }616 617        private LeaveFaultInstruction(bool hasValue)618        {619            _hasValue = hasValue;620        }621 622        public override int Run(InterpretedFrame frame)623        {624            object exception = frame.Pop();625            throw new RethrowException();626        }627    }628 629    internal sealed class ThrowInstruction : Instruction630    {631        internal static readonly ThrowInstruction Throw = new ThrowInstruction(true, false);632        internal static readonly ThrowInstruction VoidThrow = new ThrowInstruction(false, false);633        internal static readonly ThrowInstruction Rethrow = new ThrowInstruction(true, true);634        internal static readonly ThrowInstruction VoidRethrow = new ThrowInstruction(false, true);635 636        private readonly bool _hasResult, _rethrow;637 638        private ThrowInstruction(bool hasResult, bool isRethrow)639        {640            _hasResult = hasResult;641            _rethrow = isRethrow;642        }643 644        public override int ProducedStack645        {646            get { return _hasResult ? 1 : 0; }647        }648 649        public override int ConsumedStack650        {651            get652            {653                return 1;654            }655        }656 657        public override int Run(InterpretedFrame frame)658        {659            var ex = (Exception)frame.Pop();660            if (_rethrow)661            {662                // ExceptionHandler handler;663                // return frame.Interpreter.GotoHandler(frame, ex, out handler);664                throw new RethrowException();665            }666 667            throw ex;668        }669    }670 671    internal sealed class SwitchInstruction : Instruction672    {673        private readonly Dictionary<int, int> _cases;674 675        internal SwitchInstruction(Dictionary<int, int> cases)676        {677            Assert.NotNull(cases);678            _cases = cases;679        }680 681        public override int ConsumedStack { get { return 1; } }682 683        public override int ProducedStack { get { return 0; } }684 685        public override int Run(InterpretedFrame frame)686        {687            int target;688            return _cases.TryGetValue((int)frame.Pop(), out target) ? target : 1;689        }690    }691 692    internal sealed class EnterLoopInstruction : Instruction693    {694        private readonly int _instructionIndex;695        private Dictionary<ParameterExpression, LocalVariable> _variables;696        private Dictionary<ParameterExpression, LocalVariable> _closureVariables;697        private PowerShellLoopExpression _loop;698        private int _loopEnd;699        private int _compilationThreshold;700 701        internal EnterLoopInstruction(PowerShellLoopExpression loop, LocalVariables locals, int compilationThreshold, int instructionIndex)702        {703            _loop = loop;704            _variables = locals.CopyLocals();705            _closureVariables = locals.ClosureVariables;706            _compilationThreshold = compilationThreshold;707            _instructionIndex = instructionIndex;708        }709 710        internal void FinishLoop(int loopEnd)711        {712            _loopEnd = loopEnd;713        }714 715        public override int Run(InterpretedFrame frame)716        {717            // Don't lock here, it's a frequently hit path.718            //719            // There could be multiple threads racing, but that is okay.720            // Two bad things can happen:721            //   * We miss decrements (some thread sets the counter forward)722            //   * We might enter the "if" branch more than once.723            //724            // The first is okay, it just means we take longer to compile.725            // The second we explicitly guard against inside of Compile().726            //727            // We can't miss 0. The first thread that writes -1 must have read 0 and hence start compilation.728            if (unchecked(_compilationThreshold--) == 0)729            {730                if (frame.Interpreter.CompileSynchronously)731                {732                    Compile(frame);733                }734                else735                {736                    // Kick off the compile on another thread so this one can keep going737                    ThreadPool.QueueUserWorkItem(Compile, frame);738                }739            }740 741            return 1;742        }743 744        private bool Compiled745        {746            get { return _loop == null; }747        }748 749        private void Compile(object frameObj)750        {751            if (Compiled)752            {753                return;754            }755 756            lock (this)757            {758                if (Compiled)759                {760                    return;761                }762 763                // PerfTrack.NoteEvent(PerfTrack.Categories.Compiler, "Interpreted loop compiled");764 765                InterpretedFrame frame = (InterpretedFrame)frameObj;766                var compiler = new LoopCompiler(_loop, frame.Interpreter.LabelMapping, _variables, _closureVariables, _instructionIndex, _loopEnd);767                var instructions = frame.Interpreter.Instructions.Instructions;768 769                // replace this instruction with an optimized one:770                instructions[_instructionIndex] = new CompiledLoopInstruction(compiler.CreateDelegate());771 772                // invalidate this instruction, some threads may still hold on it:773                _loop = null;774                _variables = null;775                _closureVariables = null;776            }777        }778    }779 780    internal sealed class CompiledLoopInstruction : Instruction781    {782        private readonly LoopFunc _compiledLoop;783 784        public CompiledLoopInstruction(LoopFunc compiledLoop)785        {786            Assert.NotNull(compiledLoop);787            _compiledLoop = compiledLoop;788        }789 790        public override int Run(InterpretedFrame frame)791        {792            return _compiledLoop(frame.Data, frame.Closure, frame);793        }794    }795}796