Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes308downloads
Breakpoint.cs613 linesDownload Raw Back to debugger
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.IO;6using System.Linq;7using System.Management.Automation.Internal;8using System.Management.Automation.Language;9using System.Threading;10 11namespace System.Management.Automation12{13    /// <summary>14    /// Holds the information for a given breakpoint.15    /// </summary>16    public abstract class Breakpoint17    {18        #region properties19 20        /// <summary>21        /// The action to take when the breakpoint is hit.22        /// </summary>23        public ScriptBlock Action { get; }24 25        /// <summary>26        /// Gets whether this breakpoint is enabled.27        /// </summary>28        public bool Enabled { get; private set; }29 30        internal void SetEnabled(bool value)31        {32            Enabled = value;33        }34 35        /// <summary>36        /// Records how many times this breakpoint has been triggered.37        /// </summary>38        public int HitCount { get; private set; }39 40        /// <summary>41        /// This breakpoint's Id.42        /// </summary>43        public int Id { get; }44 45        /// <summary>46        /// True if breakpoint is set on a script, false if the breakpoint is not scoped.47        /// </summary>48        internal bool IsScriptBreakpoint49        {50            get { return Script != null; }51        }52 53        /// <summary>54        /// The script this breakpoint is on, or null if the breakpoint is not scoped.55        /// </summary>56        public string Script { get; }57 58        #endregion properties59 60        #region constructors61 62        /// <summary>63        /// Creates a new instance of a <see cref="Breakpoint"/>64        /// </summary>65        protected Breakpoint(string script)66            : this(script, null)67        { }68 69        /// <summary>70        /// Creates a new instance of a <see cref="Breakpoint"/>71        /// </summary>72        protected Breakpoint(string script, ScriptBlock action)73        {74            Enabled = true;75            Script = string.IsNullOrEmpty(script) ? null : script;76            Id = Interlocked.Increment(ref s_lastID);77            Action = action;78            HitCount = 0;79        }80 81        /// <summary>82        /// Creates a new instance of a <see cref="Breakpoint"/>83        /// </summary>84        protected Breakpoint(string script, int id)85            : this(script, null, id)86        { }87 88        /// <summary>89        /// Creates a new instance of a <see cref="Breakpoint"/>90        /// </summary>91        protected Breakpoint(string script, ScriptBlock action, int id)92        {93            Enabled = true;94            Script = string.IsNullOrEmpty(script) ? null : script;95            Id = id;96            Action = action;97            HitCount = 0;98        }99 100        #endregion constructors101 102        #region methods103 104        internal BreakpointAction Trigger()105        {106            ++HitCount;107            if (Action == null)108            {109                return BreakpointAction.Break;110            }111 112            try113            {114                // Pass this to the action so the breakpoint.  This could be used to115                // implement a "trigger once" breakpoint that disables itself after first hit.116                // One could also share an action across many breakpoints - and hence needs117                // to know something about the breakpoint that is hit, e.g. in a poor mans code coverage tool.118                Action.DoInvoke(dollarUnder: this, input: null, args: Array.Empty<object>());119            }120            catch (BreakException)121            {122                return BreakpointAction.Break;123            }124            catch (Exception)125            {126            }127 128            return BreakpointAction.Continue;129        }130 131        internal virtual bool RemoveSelf(ScriptDebugger debugger) => false;132 133        #endregion methods134 135        #region enums136 137        internal enum BreakpointAction138        {139            Continue = 0x0,140            Break = 0x1141        }142 143        #endregion enums144 145        #region private members146 147        private static int s_lastID;148 149        #endregion private members150    }151 152    /// <summary>153    /// A breakpoint on a command.154    /// </summary>155    public class CommandBreakpoint : Breakpoint156    {157        /// <summary>158        /// Creates a new instance of a <see cref="CommandBreakpoint"/>159        /// </summary>160        public CommandBreakpoint(string script, WildcardPattern command, string commandString)161            : this(script, command, commandString, null)162        { }163 164        /// <summary>165        /// Creates a new instance of a <see cref="CommandBreakpoint"/>166        /// </summary>167        public CommandBreakpoint(string script, WildcardPattern command, string commandString, ScriptBlock action)168            : base(script, action)169        {170            CommandPattern = command;171            Command = commandString;172        }173 174        /// <summary>175        /// Creates a new instance of a <see cref="CommandBreakpoint"/>176        /// </summary>177        public CommandBreakpoint(string script, WildcardPattern command, string commandString, int id)178            : this(script, command, commandString, null, id)179        { }180 181        /// <summary>182        /// Creates a new instance of a <see cref="CommandBreakpoint"/>183        /// </summary>184        public CommandBreakpoint(string script, WildcardPattern command, string commandString, ScriptBlock action, int id)185            : base(script, action, id)186        {187            CommandPattern = command;188            Command = commandString;189        }190 191        /// <summary>192        /// Which command this breakpoint is on.193        /// </summary>194        public string Command { get; }195 196        internal WildcardPattern CommandPattern { get; }197 198        /// <summary>199        /// Gets a string representation of this breakpoint.200        /// </summary>201        /// <returns>A string representation of this breakpoint.</returns>202        public override string ToString()203        {204            return IsScriptBreakpoint205                       ? StringUtil.Format(DebuggerStrings.CommandScriptBreakpointString, Script, Command)206                       : StringUtil.Format(DebuggerStrings.CommandBreakpointString, Command);207        }208 209        internal override bool RemoveSelf(ScriptDebugger debugger) =>210            debugger.RemoveCommandBreakpoint(this);211 212        private bool CommandInfoMatches(CommandInfo commandInfo)213        {214            if (commandInfo == null)215                return false;216 217            if (CommandPattern.IsMatch(commandInfo.Name))218                return true;219 220            // If the breakpoint looks like it might have specified a module name and the command221            // we're checking is in a module, try matching the module\command against the pattern222            // in the breakpoint.223            if (!string.IsNullOrEmpty(commandInfo.ModuleName) && Command.Contains('\\'))224            {225                if (CommandPattern.IsMatch(commandInfo.ModuleName + "\\" + commandInfo.Name))226                    return true;227            }228 229            var externalScript = commandInfo as ExternalScriptInfo;230            if (externalScript != null)231            {232                if (externalScript.Path.Equals(Command, StringComparison.OrdinalIgnoreCase))233                    return true;234 235                if (CommandPattern.IsMatch(Path.GetFileNameWithoutExtension(externalScript.Path)))236                    return true;237            }238 239            return false;240        }241 242        internal bool Trigger(InvocationInfo invocationInfo)243        {244            // invocationInfo.MyCommand can be null when invoked via ScriptBlock.Invoke()245            if (CommandPattern.IsMatch(invocationInfo.InvocationName) || CommandInfoMatches(invocationInfo.MyCommand))246            {247                return (Script == null || Script.Equals(invocationInfo.ScriptName, StringComparison.OrdinalIgnoreCase));248            }249 250            return false;251        }252    }253 254    /// <summary>255    /// The access type for variable breakpoints to break on.256    /// </summary>257    public enum VariableAccessMode258    {259        /// <summary>260        /// Break on read access only.261        /// </summary>262        Read,263        /// <summary>264        /// Break on write access only (default).265        /// </summary>266        Write,267        /// <summary>268        /// Breakon read or write access.269        /// </summary>270        ReadWrite271    }272 273    /// <summary>274    /// A breakpoint on a variable.275    /// </summary>276    public class VariableBreakpoint : Breakpoint277    {278        /// <summary>279        /// Creates a new instance of a <see cref="VariableBreakpoint"/>.280        /// </summary>281        public VariableBreakpoint(string script, string variable, VariableAccessMode accessMode)282            : this(script, variable, accessMode, null)283        { }284 285        /// <summary>286        /// Creates a new instance of a <see cref="VariableBreakpoint"/>.287        /// </summary>288        public VariableBreakpoint(string script, string variable, VariableAccessMode accessMode, ScriptBlock action)289            : base(script, action)290        {291            Variable = variable;292            AccessMode = accessMode;293        }294 295        /// <summary>296        /// Creates a new instance of a <see cref="VariableBreakpoint"/>.297        /// </summary>298        public VariableBreakpoint(string script, string variable, VariableAccessMode accessMode, int id)299            : this(script, variable, accessMode, null, id)300        { }301 302        /// <summary>303        /// Creates a new instance of a <see cref="VariableBreakpoint"/>.304        /// </summary>305        public VariableBreakpoint(string script, string variable, VariableAccessMode accessMode, ScriptBlock action, int id)306            : base(script, action, id)307        {308            Variable = variable;309            AccessMode = accessMode;310        }311 312        /// <summary>313        /// The access mode to trigger this variable breakpoint on.314        /// </summary>315        public VariableAccessMode AccessMode { get; }316 317        /// <summary>318        /// Which variable this breakpoint is on.319        /// </summary>320        public string Variable { get; }321 322        /// <summary>323        /// Gets the string representation of this breakpoint.324        /// </summary>325        /// <returns>The string representation of this breakpoint.</returns>326        public override string ToString()327        {328            return IsScriptBreakpoint329                       ? StringUtil.Format(DebuggerStrings.VariableScriptBreakpointString, Script, Variable, AccessMode)330                       : StringUtil.Format(DebuggerStrings.VariableBreakpointString, Variable, AccessMode);331        }332 333        internal bool Trigger(string currentScriptFile, bool read)334        {335            if (!Enabled)336                return false;337 338            if (AccessMode != VariableAccessMode.ReadWrite && AccessMode != (read ? VariableAccessMode.Read : VariableAccessMode.Write))339                return false;340 341            if (Script == null || Script.Equals(currentScriptFile, StringComparison.OrdinalIgnoreCase))342            {343                return Trigger() == BreakpointAction.Break;344            }345 346            return false;347        }348 349        internal override bool RemoveSelf(ScriptDebugger debugger) =>350            debugger.RemoveVariableBreakpoint(this);351    }352 353    /// <summary>354    /// A breakpoint on a line or statement.355    /// </summary>356    public class LineBreakpoint : Breakpoint357    {358        /// <summary>359        /// Creates a new instance of a <see cref="LineBreakpoint"/>360        /// </summary>361        public LineBreakpoint(string script, int line)362            : this(script, line, null)363        { }364 365        /// <summary>366        /// Creates a new instance of a <see cref="LineBreakpoint"/>367        /// </summary>368        public LineBreakpoint(string script, int line, ScriptBlock action)369            : base(script, action)370        {371            Diagnostics.Assert(!string.IsNullOrEmpty(script), "Caller to verify script parameter is not null or empty.");372            Line = line;373            Column = 0;374            SequencePointIndex = -1;375        }376 377        /// <summary>378        /// Creates a new instance of a <see cref="LineBreakpoint"/>379        /// </summary>380        public LineBreakpoint(string script, int line, int column)381            : this(script, line, column, null)382        { }383 384        /// <summary>385        /// Creates a new instance of a <see cref="LineBreakpoint"/>386        /// </summary>387        public LineBreakpoint(string script, int line, int column, ScriptBlock action)388            : base(script, action)389        {390            Diagnostics.Assert(!string.IsNullOrEmpty(script), "Caller to verify script parameter is not null or empty.");391            Line = line;392            Column = column;393            SequencePointIndex = -1;394        }395 396        /// <summary>397        /// Creates a new instance of a <see cref="LineBreakpoint"/>398        /// </summary>399        public LineBreakpoint(string script, int line, int column, int id)400            : this(script, line, column, null, id)401        { }402 403        /// <summary>404        /// Creates a new instance of a <see cref="LineBreakpoint"/>405        /// </summary>406        public LineBreakpoint(string script, int line, int column, ScriptBlock action, int id)407            : base(script, action, id)408        {409            Diagnostics.Assert(!string.IsNullOrEmpty(script), "Caller to verify script parameter is not null or empty.");410            Line = line;411            Column = column;412            SequencePointIndex = -1;413        }414 415        /// <summary>416        /// Which column this breakpoint is on.417        /// </summary>418        public int Column { get; }419 420        /// <summary>421        /// Which line this breakpoint is on.422        /// </summary>423        public int Line { get; }424 425        /// <summary>426        /// Gets a string representation of this breakpoint.427        /// </summary>428        /// <returns>A string representation of this breakpoint.</returns>429        public override string ToString()430        {431            return Column == 0432                       ? StringUtil.Format(DebuggerStrings.LineBreakpointString, Script, Line)433                       : StringUtil.Format(DebuggerStrings.StatementBreakpointString, Script, Line, Column);434        }435 436        internal int SequencePointIndex { get; set; }437 438        internal IScriptExtent[] SequencePoints { get; set; }439 440        internal BitArray BreakpointBitArray { get; set; }441 442        private sealed class CheckBreakpointInScript : AstVisitor443        {444            public static bool IsInNestedScriptBlock(Ast ast, LineBreakpoint breakpoint)445            {446                var visitor = new CheckBreakpointInScript { _breakpoint = breakpoint };447                ast.InternalVisit(visitor);448                return visitor._result;449            }450 451            private LineBreakpoint _breakpoint;452            private bool _result;453 454            public override AstVisitAction VisitFunctionDefinition(FunctionDefinitionAst functionDefinitionAst)455            {456                if (functionDefinitionAst.Extent.ContainsLineAndColumn(_breakpoint.Line, _breakpoint.Column))457                {458                    _result = true;459                    return AstVisitAction.StopVisit;460                }461 462                // We don't need to visit the body, we're just checking extents of the topmost functions.463                // We'll visit the bodies eventually, but only when the nested function/script is executed.464                return AstVisitAction.SkipChildren;465            }466 467            public override AstVisitAction VisitScriptBlockExpression(ScriptBlockExpressionAst scriptBlockExpressionAst)468            {469                if (scriptBlockExpressionAst.Extent.ContainsLineAndColumn(_breakpoint.Line, _breakpoint.Column))470                {471                    _result = true;472                    return AstVisitAction.StopVisit;473                }474 475                // We don't need to visit the body, we're just checking extents of the topmost functions.476                // We'll visit the bodies eventually, but only when the nested function/script is executed.477                return AstVisitAction.SkipChildren;478            }479        }480 481        internal bool TrySetBreakpoint(string scriptFile, FunctionContext functionContext)482        {483            Diagnostics.Assert(SequencePointIndex == -1, "shouldn't be trying to set on a pending breakpoint");484 485            // A quick check to see if the breakpoint is within the scriptblock.486            bool couldBeInNestedScriptBlock;487            var scriptBlock = functionContext._scriptBlock;488            if (scriptBlock != null)489            {490                var ast = scriptBlock.Ast;491                if (!ast.Extent.ContainsLineAndColumn(Line, Column))492                    return false;493 494                var sequencePoints = functionContext._sequencePoints;495                if (sequencePoints.Length == 1 && sequencePoints[0] == scriptBlock.Ast.Extent)496                {497                    // If there was no real executable code in the function (e.g. only function definitions),498                    // we added the entire scriptblock as a sequence point, but it shouldn't be allowed as a breakpoint.499                    return false;500                }501 502                couldBeInNestedScriptBlock = CheckBreakpointInScript.IsInNestedScriptBlock(((IParameterMetadataProvider)ast).Body, this);503            }504            else505            {506                couldBeInNestedScriptBlock = false;507            }508 509            int sequencePointIndex;510            var sequencePoint = FindSequencePoint(functionContext, Line, Column, out sequencePointIndex);511            if (sequencePoint != null)512            {513                // If the bp could be in a nested script block, we want to be careful and get the bp in the correct script block.514                // If it's a simple line bp (no column specified), then the start line must match the bp line exactly, otherwise515                // we assume the bp is in the nested script block.516                if (!couldBeInNestedScriptBlock || (sequencePoint.StartLineNumber == Line && Column == 0))517                {518                    SetBreakpoint(functionContext, sequencePointIndex);519                    return true;520                }521            }522 523            // Before using heuristics, make sure the breakpoint isn't in a nested function/script block.524            if (couldBeInNestedScriptBlock)525            {526                return false;527            }528 529            // Not found.  First, we check if the line/column is before any real code.  If so, we'll530            // move the breakpoint to the first interesting sequence point (could be a dynamicparam,531            // begin, process, end, or clean block.)532            if (scriptBlock != null)533            {534                var ast = scriptBlock.Ast;535                var bodyAst = ((IParameterMetadataProvider)ast).Body;536                if ((bodyAst.DynamicParamBlock == null || bodyAst.DynamicParamBlock.Extent.IsAfter(Line, Column))537                    && (bodyAst.BeginBlock == null || bodyAst.BeginBlock.Extent.IsAfter(Line, Column))538                    && (bodyAst.ProcessBlock == null || bodyAst.ProcessBlock.Extent.IsAfter(Line, Column))539                    && (bodyAst.EndBlock == null || bodyAst.EndBlock.Extent.IsAfter(Line, Column))540                    && (bodyAst.CleanBlock == null || bodyAst.CleanBlock.Extent.IsAfter(Line, Column)))541                {542                    SetBreakpoint(functionContext, 0);543                    return true;544                }545            }546 547            // Still not found.  Try fudging a bit, but only if it's a simple line breakpoint.548            if (Column == 0 && FindSequencePoint(functionContext, Line + 1, 0, out sequencePointIndex) != null)549            {550                SetBreakpoint(functionContext, sequencePointIndex);551                return true;552            }553 554            return false;555        }556 557        private static IScriptExtent FindSequencePoint(FunctionContext functionContext, int line, int column, out int sequencePointIndex)558        {559            var sequencePoints = functionContext._sequencePoints;560 561            for (int i = 0; i < sequencePoints.Length; ++i)562            {563                var extent = sequencePoints[i];564                if (extent.ContainsLineAndColumn(line, column))565                {566                    sequencePointIndex = i;567                    return extent;568                }569            }570 571            sequencePointIndex = -1;572            return null;573        }574 575        private void SetBreakpoint(FunctionContext functionContext, int sequencePointIndex)576        {577            // Remember the bitarray so we when the last breakpoint is removed, we can avoid578            // stopping at the sequence point.579            this.BreakpointBitArray = functionContext._breakPoints;580            this.SequencePoints = functionContext._sequencePoints;581 582            SequencePointIndex = sequencePointIndex;583            this.BreakpointBitArray.Set(SequencePointIndex, true);584        }585 586        internal override bool RemoveSelf(ScriptDebugger debugger)587        {588            if (this.SequencePoints != null)589            {590                // Remove ourselves from the list of bound breakpoints in this script.  It's possible the breakpoint was never591                // bound, in which case there is nothing to do.592                var boundBreakPoints = debugger.GetBoundBreakpoints(this.SequencePoints);593                if (boundBreakPoints != null)594                {595                    Diagnostics.Assert(boundBreakPoints[this.SequencePointIndex].Contains(this),596                                       "If we set _scriptBlock, we should have also added the breakpoint to the bound breakpoint list");597                    boundBreakPoints[this.SequencePointIndex].Remove(this);598 599                    if (boundBreakPoints[this.SequencePointIndex].All(breakpoint => breakpoint.SequencePointIndex != this.SequencePointIndex))600                    {601                        // No other line breakpoints are at the same sequence point, so disable the breakpoint so602                        // we don't go looking for breakpoints the next time we hit the sequence point.603                        // This isn't strictly necessary, but script execution will be faster.604                        this.BreakpointBitArray.Set(SequencePointIndex, false);605                    }606                }607            }608 609            return debugger.RemoveLineBreakpoint(this);610        }611    }612}613