From 95faef4ab21f0f2215e0002934be72e232c1ccdc Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Wed, 7 Nov 2018 21:00:36 -0400 Subject: [PATCH 01/44] added Break to ActionPreference enum --- src/System.Management.Automation/engine/CommandBase.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/CommandBase.cs b/src/System.Management.Automation/engine/CommandBase.cs index 433ca142e8d..5ac4e8ff871 100644 --- a/src/System.Management.Automation/engine/CommandBase.cs +++ b/src/System.Management.Automation/engine/CommandBase.cs @@ -273,12 +273,14 @@ public enum ActionPreference Stop, /// Handle this event as normal and continue Continue, - /// Ask whether to stop or continue + /// Ask whether to stop or continue (legacy; use Break instead) Inquire, /// Ignore the event completely (not even logging it to the target stream) Ignore, /// Suspend the command for further diagnosis. Supported only for workflows. Suspend, + /// Enter the debugger. + Break, } // enum ActionPreference #endregion ActionPreference From ff98f4135e5170f1eadb8571d4d6ce651c41124f Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Wed, 7 Nov 2018 21:01:43 -0400 Subject: [PATCH 02/44] add Break ActionPreference support --- .../EnableDisableRunspaceDebugCommand.cs | 14 +--- .../engine/MshCommandRuntime.cs | 43 +++++++++- .../engine/debugger/debugger.cs | 79 +++++++++++++++++-- .../hostifaces/InternalHostUserInterface.cs | 1 + .../engine/parser/TypeResolver.cs | 1 + 5 files changed, 116 insertions(+), 22 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/EnableDisableRunspaceDebugCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/EnableDisableRunspaceDebugCommand.cs index df47dbdbf13..902db0bc862 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/EnableDisableRunspaceDebugCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/EnableDisableRunspaceDebugCommand.cs @@ -535,19 +535,11 @@ protected override void EndProcessing() { Runspace currentRunspace = this.Context.CurrentRunspace; - if ((currentRunspace != null) && (currentRunspace.Debugger != null)) + if (currentRunspace != null && currentRunspace.Debugger != null) { - if (!currentRunspace.Debugger.IsDebugHandlerSubscribed && - (currentRunspace.Debugger.UnhandledBreakpointMode == UnhandledBreakpointProcessingMode.Ignore)) - { - // No debugger attached and runspace debugging is not enabled. Enable runspace debugging here - // so that this command is effective. - currentRunspace.Debugger.UnhandledBreakpointMode = UnhandledBreakpointProcessingMode.Wait; - } - - // Set debugger to step mode so that a break occurs immediately. - currentRunspace.Debugger.SetDebuggerStepMode(true); WriteVerbose(string.Format(CultureInfo.InvariantCulture, Debugger.DebugBreakMessage, MyInvocation.ScriptLineNumber, MyInvocation.ScriptName)); + + currentRunspace.Debugger.Break(); } } diff --git a/src/System.Management.Automation/engine/MshCommandRuntime.cs b/src/System.Management.Automation/engine/MshCommandRuntime.cs index c24844be846..c0ad80f6fcc 100644 --- a/src/System.Management.Automation/engine/MshCommandRuntime.cs +++ b/src/System.Management.Automation/engine/MshCommandRuntime.cs @@ -5,13 +5,12 @@ using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Threading; -using System.Security; using System.Management.Automation.Host; -using System.Management.Automation.Internal.Host; using System.Management.Automation.Internal; +using System.Management.Automation.Internal.Host; using System.Management.Automation.Remoting; using System.Management.Automation.Runspaces; +using System.Threading; using Dbg = System.Management.Automation.Diagnostics; @@ -409,6 +408,12 @@ internal void WriteProgress( if (WriteHelper_ShouldWrite( preference, lastProgressContinueStatus)) { + // Break into the debugger if requested + if (preference == ActionPreference.Break) + { + CBhost?.Runspace?.Debugger?.Break(progressRecord); + } + ui.WriteProgress(sourceId, progressRecord); } @@ -474,6 +479,12 @@ internal void WriteDebug(DebugRecord record, bool overrideInquire = false) record.SetInvocationInfo(MyInvocation); } + // Break into the debugger if requested + if (preference == ActionPreference.Break) + { + CBhost?.Runspace?.Debugger?.Break(record); + } + if (DebugOutputPipe != null) { if (CBhost != null && CBhost.InternalUI != null && @@ -565,6 +576,12 @@ internal void WriteVerbose(VerboseRecord record, bool overrideInquire = false) record.SetInvocationInfo(MyInvocation); } + // Break into the debugger if requested + if (preference == ActionPreference.Break) + { + CBhost?.Runspace?.Debugger?.Break(record); + } + if (VerboseOutputPipe != null) { if (CBhost != null && CBhost.InternalUI != null && @@ -656,6 +673,12 @@ internal void WriteWarning(WarningRecord record, bool overrideInquire = false) record.SetInvocationInfo(MyInvocation); } + // Break into the debugger if requested + if (preference == ActionPreference.Break) + { + CBhost?.Runspace?.Debugger?.Break(record); + } + if (WarningOutputPipe != null) { if (CBhost != null && CBhost.InternalUI != null && @@ -719,6 +742,12 @@ internal void WriteInformation(InformationRecord record, bool overrideInquire = if (overrideInquire && preference == ActionPreference.Inquire) preference = ActionPreference.Continue; + // Break into the debugger if requested + if (preference == ActionPreference.Break) + { + CBhost?.Runspace?.Debugger?.Break(record); + } + if (preference != ActionPreference.Ignore) { if (InformationOutputPipe != null) @@ -2663,6 +2692,12 @@ internal void WriteError(ErrorRecord errorRecord, bool overrideInquire) preference = ActionPreference.Continue; } + // Break into the debugger if requested + if (preference == ActionPreference.Break) + { + CBhost?.Runspace?.Debugger?.Break(errorRecord); + } + #if CORECLR // SecurityContext is not supported in CoreCLR DoWriteError(new KeyValuePair(errorRecord, preference)); @@ -3361,6 +3396,7 @@ internal bool WriteHelper_ShouldWrite( case ActionPreference.Continue: case ActionPreference.Stop: case ActionPreference.Inquire: + case ActionPreference.Break: return true; default: @@ -3413,6 +3449,7 @@ internal ContinueStatus WriteHelper( case ActionPreference.Ignore: // YesToAll case ActionPreference.SilentlyContinue: case ActionPreference.Continue: + case ActionPreference.Break: return ContinueStatus.Yes; case ActionPreference.Stop: diff --git a/src/System.Management.Automation/engine/debugger/debugger.cs b/src/System.Management.Automation/engine/debugger/debugger.cs index a3be0cdebbf..8af39bec654 100644 --- a/src/System.Management.Automation/engine/debugger/debugger.cs +++ b/src/System.Management.Automation/engine/debugger/debugger.cs @@ -2,22 +2,22 @@ // Licensed under the MIT License. using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; -using System.Collections.ObjectModel; -using System.Collections.Generic; -using System.Collections.Concurrent; using System.Linq; -using System.Threading; using System.Management.Automation.Host; +using System.Management.Automation.Internal; using System.Management.Automation.Internal.Host; using System.Management.Automation.Language; using System.Management.Automation.Runspaces; -using System.Management.Automation.Internal; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; -using System.Diagnostics.CodeAnalysis; +using System.Threading; namespace System.Management.Automation { @@ -467,6 +467,14 @@ public virtual bool InBreakpoint get { return DebuggerStopped; } } + /// + /// The object that triggered the current breakpoint. + /// + protected object TriggerObject + { + get; private set; + } + #endregion #region Protected Methods @@ -644,6 +652,27 @@ public virtual void SetDebuggerStepMode(bool enabled) #region Internal Methods + /// + /// Breaks into the debugger. + /// + /// The object that triggered the breakpoint, if there is one. + internal virtual void Break(object triggerObject = null) + { + if (!IsDebugHandlerSubscribed && + (UnhandledBreakpointMode == UnhandledBreakpointProcessingMode.Ignore)) + { + // No debugger attached and runspace debugging is not enabled. Enable runspace debugging here + // so that this command is effective. + UnhandledBreakpointMode = UnhandledBreakpointProcessingMode.Wait; + } + + // Store the triggerObject so that we can add it to PSDebugContext + TriggerObject = triggerObject; + + // Set debugger to step mode so that a break can occur. + SetDebuggerStepMode(true); + } + /// /// Passes the debugger command to the internal script debugger command processor. This /// is used internally to handle debugger commands such as list, help, etc. @@ -1699,7 +1728,7 @@ private void OnDebuggerStop(InvocationInfo invocationInfo, List brea return; } - _context.SetVariable(SpecialVariables.PSDebugContextVarPath, new PSDebugContext(invocationInfo, breakpoints)); + _context.SetVariable(SpecialVariables.PSDebugContextVarPath, new PSDebugContext(invocationInfo, breakpoints, TriggerObject)); FunctionInfo defaultPromptInfo = null; string originalPromptString = null; @@ -2415,6 +2444,24 @@ public override void SetDebuggerStepMode(bool enabled) } } + /// + /// Breaks into the debugger. + /// + /// The object that triggered the breakpoint, if there is one. + internal override void Break(object triggerObject = null) + { + base.Break(triggerObject); + + // If the debugger is running and we are not in a breakpoint, trigger an immediate break in the current location + using (IEnumerator enumerator = GetCallStack().GetEnumerator()) + { + if (enumerator.MoveNext() && _context._debuggingMode > 0) + { + OnSequencePointHit(enumerator.Current.FunctionContext); + } + } + } + /// /// Passes the debugger command to the internal script debugger command processor. This /// is used internally to handle debugger commands such as list, help, etc. @@ -4152,6 +4199,15 @@ public override bool IsActive get { return _wrappedDebugger.IsActive; } } + /// + /// Breaks into the debugger. + /// + /// The object that triggered the breakpoint, if there is one. + internal override void Break(object triggerObject = null) + { + _wrappedDebugger.Break(triggerObject); + } + #endregion #region IDisposable @@ -5179,7 +5235,8 @@ public class PSDebugContext /// /// InvocationInfo /// Breakpoints - public PSDebugContext(InvocationInfo invocationInfo, List breakpoints) + /// TriggerObject + public PSDebugContext(InvocationInfo invocationInfo, List breakpoints, object triggerObject = null) { if (breakpoints == null) { @@ -5188,6 +5245,7 @@ public PSDebugContext(InvocationInfo invocationInfo, List breakpoint this.InvocationInfo = invocationInfo; this.Breakpoints = breakpoints.ToArray(); + this.Trigger = triggerObject; } /// @@ -5200,6 +5258,11 @@ public PSDebugContext(InvocationInfo invocationInfo, List breakpoint /// were hit. Otherwise, the execution was suspended as part of a step operation. /// public Breakpoint[] Breakpoints { get; private set; } + + /// + /// The object that triggered the current dynamic breakpoint. + /// + public object Trigger { get; private set; } } #endregion diff --git a/src/System.Management.Automation/engine/hostifaces/InternalHostUserInterface.cs b/src/System.Management.Automation/engine/hostifaces/InternalHostUserInterface.cs index 413c731754d..d140a72a792 100644 --- a/src/System.Management.Automation/engine/hostifaces/InternalHostUserInterface.cs +++ b/src/System.Management.Automation/engine/hostifaces/InternalHostUserInterface.cs @@ -376,6 +376,7 @@ internal void WriteDebugInfoBuffers(DebugRecord record) switch (preference) { case ActionPreference.Continue: + case ActionPreference.Break: WriteDebugLineHelper(message); break; case ActionPreference.SilentlyContinue: diff --git a/src/System.Management.Automation/engine/parser/TypeResolver.cs b/src/System.Management.Automation/engine/parser/TypeResolver.cs index 3128d30162c..c208c75cd3f 100644 --- a/src/System.Management.Automation/engine/parser/TypeResolver.cs +++ b/src/System.Management.Automation/engine/parser/TypeResolver.cs @@ -871,6 +871,7 @@ static TypeAccelerators() builtinTypeAccelerators.Add("psnoteproperty", typeof(PSNoteProperty)); builtinTypeAccelerators.Add("psaliasproperty", typeof(PSAliasProperty)); builtinTypeAccelerators.Add("psvariableproperty", typeof(PSVariableProperty)); + builtinTypeAccelerators.Add("actionpreference", typeof(ActionPreference)); } internal static string FindBuiltinAccelerator(Type type, string expectedKey = null) From b2e32be613e4d8b99c7e2ff959aaef622f1455f5 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Wed, 7 Nov 2018 21:27:26 -0400 Subject: [PATCH 03/44] CodeFactor changes --- .../engine/CommandBase.cs | 6 ++++++ .../engine/debugger/debugger.cs | 12 ++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandBase.cs b/src/System.Management.Automation/engine/CommandBase.cs index 5ac4e8ff871..6e5a6676dd7 100644 --- a/src/System.Management.Automation/engine/CommandBase.cs +++ b/src/System.Management.Automation/engine/CommandBase.cs @@ -269,16 +269,22 @@ public enum ActionPreference { /// Ignore this event and continue SilentlyContinue, + /// Stop the command Stop, + /// Handle this event as normal and continue Continue, + /// Ask whether to stop or continue (legacy; use Break instead) Inquire, + /// Ignore the event completely (not even logging it to the target stream) Ignore, + /// Suspend the command for further diagnosis. Supported only for workflows. Suspend, + /// Enter the debugger. Break, } // enum ActionPreference diff --git a/src/System.Management.Automation/engine/debugger/debugger.cs b/src/System.Management.Automation/engine/debugger/debugger.cs index 8af39bec654..84b0738973b 100644 --- a/src/System.Management.Automation/engine/debugger/debugger.cs +++ b/src/System.Management.Automation/engine/debugger/debugger.cs @@ -468,7 +468,7 @@ public virtual bool InBreakpoint } /// - /// The object that triggered the current breakpoint. + /// Gets the object that triggered the current breakpoint. /// protected object TriggerObject { @@ -5231,11 +5231,11 @@ public DebuggerCommand(string command, DebuggerResumeAction? action, bool repeat public class PSDebugContext { /// - /// Constructor. + /// Initializes a new instance of the class. /// - /// InvocationInfo - /// Breakpoints - /// TriggerObject + /// The invocation information for the current command. + /// The breakpoint(s) that caused the script to break in the debugger. + /// The object that caused the script to break in the debugger. public PSDebugContext(InvocationInfo invocationInfo, List breakpoints, object triggerObject = null) { if (breakpoints == null) @@ -5260,7 +5260,7 @@ public PSDebugContext(InvocationInfo invocationInfo, List breakpoint public Breakpoint[] Breakpoints { get; private set; } /// - /// The object that triggered the current dynamic breakpoint. + /// Gets the object that triggered the current dynamic breakpoint. /// public object Trigger { get; private set; } } From 37bbbde56365ef65da8e0f29a57a781311fff710 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Wed, 7 Nov 2018 22:05:03 -0400 Subject: [PATCH 04/44] updated type accelerator pester test --- test/powershell/Language/Parser/TypeAccelerator.Tests.ps1 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/powershell/Language/Parser/TypeAccelerator.Tests.ps1 b/test/powershell/Language/Parser/TypeAccelerator.Tests.ps1 index 747ccaae0a3..1499d41fbdf 100644 --- a/test/powershell/Language/Parser/TypeAccelerator.Tests.ps1 +++ b/test/powershell/Language/Parser/TypeAccelerator.Tests.ps1 @@ -402,15 +402,19 @@ Describe "Type accelerators" -Tags "CI" { Accelerator = 'pspropertyexpression' Type = [Microsoft.PowerShell.Commands.PSPropertyExpression] } + @{ + Accelerator = 'actionpreference' + Type = [System.Management.Automation.ActionPreference] + } ) if ( !$IsWindows ) { - $totalAccelerators = 98 + $totalAccelerators = 99 } else { - $totalAccelerators = 103 + $totalAccelerators = 104 $extraFullPSAcceleratorTestCases = @( @{ From f7240d2562d80d16aab57fb6f5d75ac9bf0d6978 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Tue, 13 Nov 2018 13:30:21 -0400 Subject: [PATCH 05/44] Changes requested by @PaulHigin in code review --- .../engine/debugger/debugger.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/System.Management.Automation/engine/debugger/debugger.cs b/src/System.Management.Automation/engine/debugger/debugger.cs index 84b0738973b..1acbed04811 100644 --- a/src/System.Management.Automation/engine/debugger/debugger.cs +++ b/src/System.Management.Automation/engine/debugger/debugger.cs @@ -658,19 +658,7 @@ public virtual void SetDebuggerStepMode(bool enabled) /// The object that triggered the breakpoint, if there is one. internal virtual void Break(object triggerObject = null) { - if (!IsDebugHandlerSubscribed && - (UnhandledBreakpointMode == UnhandledBreakpointProcessingMode.Ignore)) - { - // No debugger attached and runspace debugging is not enabled. Enable runspace debugging here - // so that this command is effective. - UnhandledBreakpointMode = UnhandledBreakpointProcessingMode.Wait; - } - - // Store the triggerObject so that we can add it to PSDebugContext - TriggerObject = triggerObject; - - // Set debugger to step mode so that a break can occur. - SetDebuggerStepMode(true); + throw new PSNotImplementedException(); } /// @@ -2450,9 +2438,21 @@ public override void SetDebuggerStepMode(bool enabled) /// The object that triggered the breakpoint, if there is one. internal override void Break(object triggerObject = null) { - base.Break(triggerObject); + if (!IsDebugHandlerSubscribed && + (UnhandledBreakpointMode == UnhandledBreakpointProcessingMode.Ignore)) + { + // No debugger attached and runspace debugging is not enabled. Enable runspace debugging here + // so that this command is effective. + UnhandledBreakpointMode = UnhandledBreakpointProcessingMode.Wait; + } + + // Store the triggerObject so that we can add it to PSDebugContext + TriggerObject = triggerObject; + + // Set debugger to step mode so that a break can occur. + SetDebuggerStepMode(true); - // If the debugger is running and we are not in a breakpoint, trigger an immediate break in the current location + // If the debugger is enabled and we are not in a breakpoint, trigger an immediate break in the current location using (IEnumerator enumerator = GetCallStack().GetEnumerator()) { if (enumerator.MoveNext() && _context._debuggingMode > 0) From f0fcb9640ac8c622c9aa7d821ae7954c04c775e6 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Tue, 13 Nov 2018 18:58:58 -0400 Subject: [PATCH 06/44] Moving TriggerObject into ScriptDebugger class This change should have been made with the last commit --- .../engine/debugger/debugger.cs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/System.Management.Automation/engine/debugger/debugger.cs b/src/System.Management.Automation/engine/debugger/debugger.cs index 1acbed04811..98ebc390050 100644 --- a/src/System.Management.Automation/engine/debugger/debugger.cs +++ b/src/System.Management.Automation/engine/debugger/debugger.cs @@ -467,14 +467,6 @@ public virtual bool InBreakpoint get { return DebuggerStopped; } } - /// - /// Gets the object that triggered the current breakpoint. - /// - protected object TriggerObject - { - get; private set; - } - #endregion #region Protected Methods @@ -931,6 +923,11 @@ private bool IsLocalSession } } + /// + /// Gets the object that triggered the current breakpoint. + /// + private object TriggerObject{ get; set; } + #endregion properties #region internal methods From e7a640244569da3c8b3b2e3bf9ad631faa825d21 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Wed, 14 Nov 2018 09:17:19 -0400 Subject: [PATCH 07/44] CodeFactor changes --- src/System.Management.Automation/engine/debugger/debugger.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/engine/debugger/debugger.cs b/src/System.Management.Automation/engine/debugger/debugger.cs index 98ebc390050..fac0a53eaf0 100644 --- a/src/System.Management.Automation/engine/debugger/debugger.cs +++ b/src/System.Management.Automation/engine/debugger/debugger.cs @@ -924,9 +924,9 @@ private bool IsLocalSession } /// - /// Gets the object that triggered the current breakpoint. + /// Gets or sets the object that triggered the current breakpoint. /// - private object TriggerObject{ get; set; } + private object TriggerObject { get; set; } #endregion properties From bdcfd7f6ecffac359a5b79a202969ede6a60b574 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Thu, 15 Nov 2018 11:07:54 -0400 Subject: [PATCH 08/44] updated typeaccelerator count for test --- test/powershell/Language/Parser/TypeAccelerator.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/powershell/Language/Parser/TypeAccelerator.Tests.ps1 b/test/powershell/Language/Parser/TypeAccelerator.Tests.ps1 index 1499d41fbdf..cbb8cecb7d4 100644 --- a/test/powershell/Language/Parser/TypeAccelerator.Tests.ps1 +++ b/test/powershell/Language/Parser/TypeAccelerator.Tests.ps1 @@ -410,11 +410,11 @@ Describe "Type accelerators" -Tags "CI" { if ( !$IsWindows ) { - $totalAccelerators = 99 + $totalAccelerators = 100 } else { - $totalAccelerators = 104 + $totalAccelerators = 105 $extraFullPSAcceleratorTestCases = @( @{ From 1200839b4ddc7993a79cf8b402d724c88e99e473 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Tue, 9 Apr 2019 15:17:57 -0300 Subject: [PATCH 09/44] CodeFactor changes --- .../engine/MshCommandRuntime.cs | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/System.Management.Automation/engine/MshCommandRuntime.cs b/src/System.Management.Automation/engine/MshCommandRuntime.cs index 9059750498f..aa0455ff815 100644 --- a/src/System.Management.Automation/engine/MshCommandRuntime.cs +++ b/src/System.Management.Automation/engine/MshCommandRuntime.cs @@ -268,6 +268,12 @@ public void WriteObject(object sendToPipeline, bool enumerateCollection) #endif } + /// + /// Writes an object enumerated from a collection to the output pipe. + /// + /// + /// The enumerated object that needs to be written to the pipeline. + /// /// /// The pipeline has already been terminated, or was terminated /// during the execution of this method. @@ -275,7 +281,7 @@ public void WriteObject(object sendToPipeline, bool enumerateCollection) /// to percolate up to the caller of ProcessRecord etc. /// /// - /// Not permitted at this time or from this thread + /// Not permitted at this time or from this thread. /// private void DoWriteEnumeratedObject(object sendToPipeline) { @@ -2580,8 +2586,12 @@ internal void AppendInformationVarList(object obj) #region Write internal bool UseSecurityContextRun = true; - // NOTICE-2004/06/08-JonN 959638 - // Use this variant to skip the ThrowIfWriteNotPermitted check + /// + /// Writes an object to the output pipe, skipping the ThrowIfWriteNotPermitted check. + /// + /// + /// The object to write to the output pipe. + /// /// /// The pipeline has already been terminated, or was terminated /// during the execution of this method. @@ -2600,8 +2610,12 @@ internal void _WriteObjectSkipAllowCheck(object sendToPipeline) this.OutputPipe.Add(sendToPipeline); } - // NOTICE-2004/06/08-JonN 959638 - // Use this variant to skip the ThrowIfWriteNotPermitted check + /// + /// Enumerates and writes an object to the output pipe, skipping the ThrowIfWriteNotPermitted check. + /// + /// + /// The object to enumerate and write to the output pipe. + /// /// /// The pipeline has already been terminated, or was terminated /// during the execution of this method. @@ -2623,7 +2637,9 @@ internal void _EnumerateAndWriteObjectSkipAllowCheck(object sendToPipeline) foreach (object toConvert in enumerable) { if (AutomationNull.Value == toConvert) + { continue; + } object converted = LanguagePrimitives.AsPSObjectOrNull(toConvert); convertedList.Add(converted); From dcfe3f20784a5a82f5f899ba103e6400b3d145ca Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Mon, 15 Apr 2019 14:44:52 -0300 Subject: [PATCH 10/44] add break on exception when using EAP.Break --- .../engine/MshCommandRuntime.cs | 8 ++++++++ .../engine/runtime/Operations/MiscOps.cs | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/src/System.Management.Automation/engine/MshCommandRuntime.cs b/src/System.Management.Automation/engine/MshCommandRuntime.cs index aa0455ff815..5d9b5f2ddd8 100644 --- a/src/System.Management.Automation/engine/MshCommandRuntime.cs +++ b/src/System.Management.Automation/engine/MshCommandRuntime.cs @@ -2086,6 +2086,14 @@ public void ThrowTerminatingError(ErrorRecord errorRecord) CmdletInvocationException e = new CmdletInvocationException(errorRecord); + + // If the error action preference is set to break, break immediately + // into the debugger + if (ErrorAction == ActionPreference.Break) + { + Context?.Debugger?.Break(e.InnerException ?? e); + } + // Code sees only that execution stopped throw ManageException(e); } diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index d4c6f8f10a6..3a9a39e74a5 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -1610,6 +1610,13 @@ internal static void CheckActionPreference(FunctionContext funcContext, Exceptio preference = ExceptionHandlingOps.QueryForAction(rte, rte.Message, context); } + // if the error action preference is set to break, then break + // immediately into the debugger + if (preference == ActionPreference.Break) + { + funcContext._executionContext?.Debugger?.Break(rte); + } + // set the value of $? here in case it is reset in trap handling. context.QuestionMarkVariableValue = false; From 2d5209b7afcf9a5faf0e1b1c818a4a231a355840 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Mon, 15 Apr 2019 15:01:21 -0300 Subject: [PATCH 11/44] CodeFactor changes --- .../EnableDisableRunspaceDebugCommand.cs | 2 +- .../engine/runtime/Operations/MiscOps.cs | 21 +++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/EnableDisableRunspaceDebugCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/EnableDisableRunspaceDebugCommand.cs index be5b0d647e8..a9192ae2da9 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/EnableDisableRunspaceDebugCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/EnableDisableRunspaceDebugCommand.cs @@ -351,7 +351,7 @@ public SwitchParameter BreakAll } /// - /// The optional breakpoint objects to use for debugging. + /// Gets or sets the optional breakpoint objects to use for debugging. /// [Experimental("Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints", ExperimentAction.Show)] [Parameter(Position = 1, diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index 3a9a39e74a5..2f364dc8db9 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -107,10 +107,13 @@ private static CommandProcessorBase AddCommand(PipelineProcessor pipe, if (string.IsNullOrEmpty(commandName)) { - throw InterpreterError.NewInterpreterException(command, typeof(RuntimeException), - commandExtent, "BadExpression", - ParserStrings.BadExpression, - dotSource ? "." : "&"); + throw InterpreterError.NewInterpreterException( + command, + typeof(RuntimeException), + commandExtent, + "BadExpression", + ParserStrings.BadExpression, + dotSource ? "." : "&"); } try @@ -328,7 +331,10 @@ internal static IEnumerable Splat(object splattedValue object parameterValue = de.Value; string parameterText = GetParameterText(parameterName); - if (markUntrustedData) { ExecutionContext.MarkObjectAsUntrusted(parameterValue); } + if (markUntrustedData) + { + ExecutionContext.MarkObjectAsUntrusted(parameterValue); + } yield return CommandParameterInternal.CreateParameterWithArgument( splatAst, parameterName, parameterText, @@ -342,7 +348,10 @@ internal static IEnumerable Splat(object splattedValue { foreach (object obj in enumerableValue) { - if (markUntrustedData) { ExecutionContext.MarkObjectAsUntrusted(obj); } + if (markUntrustedData) + { + ExecutionContext.MarkObjectAsUntrusted(obj); + } yield return SplatEnumerableElement(obj, splatAst); } From 9e4c895ff2b4e029ee18756eb8ff089c47c8be23 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Mon, 15 Apr 2019 15:05:26 -0300 Subject: [PATCH 12/44] CodeFactor changes --- .../engine/runtime/Operations/MiscOps.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index 2f364dc8db9..153b6bb9933 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -1343,15 +1343,15 @@ internal static object Add(IDictionary lvalDict, IDictionary rvalDict) } // Add key and values from left hand side... - foreach (object myKey in lvalDict.Keys) + foreach (object key in lvalDict.Keys) { - newDictionary.Add(myKey, lvalDict[myKey]); + newDictionary.Add(key, lvalDict[key]); } // and the right-hand side - foreach (object myKey in rvalDict.Keys) + foreach (object key in rvalDict.Keys) { - newDictionary.Add(myKey, rvalDict[myKey]); + newDictionary.Add(key, rvalDict[key]); } return newDictionary; From 06220cc8faf706ea84f6455dd2444b68b76f6362 Mon Sep 17 00:00:00 2001 From: Ilya Date: Tue, 16 Apr 2019 08:50:50 -0300 Subject: [PATCH 13/44] Code review change Co-Authored-By: KirkMunro --- .../engine/runtime/Operations/MiscOps.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index 153b6bb9933..e7ac4636f07 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -1623,7 +1623,7 @@ internal static void CheckActionPreference(FunctionContext funcContext, Exceptio // immediately into the debugger if (preference == ActionPreference.Break) { - funcContext._executionContext?.Debugger?.Break(rte); + context.Debugger?.Break(rte); } // set the value of $? here in case it is reset in trap handling. From 2af4fffc6c34e7aaeed02213f04032ac8983927a Mon Sep 17 00:00:00 2001 From: Ilya Date: Tue, 16 Apr 2019 08:51:22 -0300 Subject: [PATCH 14/44] Code review change Co-Authored-By: KirkMunro --- src/System.Management.Automation/engine/MshCommandRuntime.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/MshCommandRuntime.cs b/src/System.Management.Automation/engine/MshCommandRuntime.cs index 5d9b5f2ddd8..d70989e1a5b 100644 --- a/src/System.Management.Automation/engine/MshCommandRuntime.cs +++ b/src/System.Management.Automation/engine/MshCommandRuntime.cs @@ -2091,7 +2091,7 @@ public void ThrowTerminatingError(ErrorRecord errorRecord) // into the debugger if (ErrorAction == ActionPreference.Break) { - Context?.Debugger?.Break(e.InnerException ?? e); + Context.Debugger?.Break(e.InnerException ?? e); } // Code sees only that execution stopped From 638711023636c5ce3dc04877ca9670fd2a604b6b Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Fri, 26 Apr 2019 14:55:48 -0300 Subject: [PATCH 15/44] fixed to break even with trap or try/catch --- .../engine/parser/Compiler.cs | 3 +- .../engine/runtime/Operations/MiscOps.cs | 62 ++++++++++--------- .../utils/RuntimeException.cs | 2 + 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/System.Management.Automation/engine/parser/Compiler.cs b/src/System.Management.Automation/engine/parser/Compiler.cs index 38be1893896..7c8f8fc98fe 100644 --- a/src/System.Management.Automation/engine/parser/Compiler.cs +++ b/src/System.Management.Automation/engine/parser/Compiler.cs @@ -4953,7 +4953,8 @@ public object VisitThrowStatement(ThrowStatementAst throwStatementAst) UpdatePosition(throwStatementAst), Expression.Throw(Expression.Call(CachedReflectionInfo.ExceptionHandlingOps_ConvertToException, throwExpr.Convert(typeof(object)), - Expression.Constant(throwStatementAst.Extent)))); + Expression.Constant(throwStatementAst.Extent), + Expression.Constant(throwStatementAst.IsRethrow)))); } #endregion Statements diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index 153b6bb9933..dff5c7e37c5 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -1602,33 +1602,38 @@ internal static void CheckActionPreference(FunctionContext funcContext, Exceptio // set $? to false indicating an error context.QuestionMarkVariableValue = false; - bool anyTrapHandlers = funcContext._traps.Any() && funcContext._traps.Last().Item2 != null; + ActionPreference preference = ExceptionHandlingOps.QueryForAction(rte, rte.Message, context); - if (!anyTrapHandlers && !ExceptionHandlingOps.NeedToQueryForActionPreference(rte, context)) + // if the exception is new (i.e. it was not rethrown from a naked throw + // statement inside of a catch block, and it is not currently being + // handled by the system), and if ErrorActionPreference is set to break, + // then break immediately into the debugger. + if (!rte.WasRethrown && + context.CurrentExceptionBeingHandled == null && + preference == ActionPreference.Break) { - throw rte; + funcContext._executionContext?.Debugger?.Break(rte); + + // set the value of $? here in case it was reset in the debugger. + context.QuestionMarkVariableValue = false; } - ActionPreference preference; + bool anyTrapHandlers = funcContext._traps.Any() && funcContext._traps.Last().Item2 != null; + if (anyTrapHandlers) { + // update the action preference according to how the exception is + // handled in the trap statement(s). preference = ProcessTraps(funcContext, rte); - } - else - { - preference = ExceptionHandlingOps.QueryForAction(rte, rte.Message, context); - } - // if the error action preference is set to break, then break - // immediately into the debugger - if (preference == ActionPreference.Break) + // set the value of $? here in case it was reset in trap handling. + context.QuestionMarkVariableValue = false; + } + else if (ExceptionHandlingOps.ExceptionCannotBeStoppedContinuedOrIgnored(rte, context)) { - funcContext._executionContext?.Debugger?.Break(rte); + throw rte; } - // set the value of $? here in case it is reset in trap handling. - context.QuestionMarkVariableValue = false; - if ((preference == ActionPreference.SilentlyContinue) || (preference == ActionPreference.Ignore)) { @@ -1651,12 +1656,12 @@ internal static void CheckActionPreference(FunctionContext funcContext, Exceptio throw rte; } - bool b = ExceptionHandlingOps.ReportErrorRecord(extent, rte, context); + bool errorReportedSuccessfully = ExceptionHandlingOps.ReportErrorRecord(extent, rte, context); // set the value of $? here in case it is reset in error reporting context.QuestionMarkVariableValue = false; - if (!b) + if (!errorReportedSuccessfully) { throw rte; } @@ -1877,13 +1882,13 @@ internal static void SetErrorVariables(IScriptExtent extent, RuntimeException rt } } - internal static bool NeedToQueryForActionPreference(RuntimeException rte, ExecutionContext context) + internal static bool ExceptionCannotBeStoppedContinuedOrIgnored(RuntimeException rte, ExecutionContext context) { - return !context.PropagateExceptionsToEnclosingStatementBlock - && context.ShellFunctionErrorOutputPipe != null - && !context.CurrentPipelineStopping - && !rte.SuppressPromptInInterpreter - && !(rte is PipelineStoppedException); + return context.PropagateExceptionsToEnclosingStatementBlock + || context.ShellFunctionErrorOutputPipe == null + || context.CurrentPipelineStopping + || rte.SuppressPromptInInterpreter + || rte is PipelineStoppedException; } /// @@ -1918,7 +1923,7 @@ internal static bool ReportErrorRecord(IScriptExtent extent, RuntimeException rt return true; } - internal static RuntimeException ConvertToException(object result, IScriptExtent extent) + internal static RuntimeException ConvertToException(object result, IScriptExtent extent, bool rethrow) { result = PSObject.Base(result); @@ -1927,13 +1932,14 @@ internal static RuntimeException ConvertToException(object result, IScriptExtent { InterpreterError.UpdateExceptionErrorRecordPosition(runtimeException, extent); runtimeException.WasThrownFromThrowStatement = true; + runtimeException.WasRethrown = rethrow; return runtimeException; } ErrorRecord er = result as ErrorRecord; if (er != null) { - runtimeException = new RuntimeException(er.ToString(), er.Exception, er) { WasThrownFromThrowStatement = true }; + runtimeException = new RuntimeException(er.ToString(), er.Exception, er) { WasThrownFromThrowStatement = true, WasRethrown = rethrow }; InterpreterError.UpdateExceptionErrorRecordPosition(runtimeException, extent); return runtimeException; @@ -1943,7 +1949,7 @@ internal static RuntimeException ConvertToException(object result, IScriptExtent if (exception != null) { er = new ErrorRecord(exception, exception.Message, ErrorCategory.OperationStopped, null); - runtimeException = new RuntimeException(exception.Message, exception, er) { WasThrownFromThrowStatement = true }; + runtimeException = new RuntimeException(exception.Message, exception, er) { WasThrownFromThrowStatement = true, WasRethrown = rethrow }; InterpreterError.UpdateExceptionErrorRecordPosition(runtimeException, extent); return runtimeException; } @@ -1954,7 +1960,7 @@ internal static RuntimeException ConvertToException(object result, IScriptExtent exception = new RuntimeException(message, null); er = new ErrorRecord(exception, message, ErrorCategory.OperationStopped, null); - runtimeException = new RuntimeException(message, exception, er) { WasThrownFromThrowStatement = true }; + runtimeException = new RuntimeException(message, exception, er) { WasThrownFromThrowStatement = true, WasRethrown = rethrow }; runtimeException.SetTargetObject(result); InterpreterError.UpdateExceptionErrorRecordPosition(runtimeException, extent); diff --git a/src/System.Management.Automation/utils/RuntimeException.cs b/src/System.Management.Automation/utils/RuntimeException.cs index 2035ffec168..5591174c36b 100644 --- a/src/System.Management.Automation/utils/RuntimeException.cs +++ b/src/System.Management.Automation/utils/RuntimeException.cs @@ -284,6 +284,8 @@ public bool WasThrownFromThrowStatement private bool _thrownByThrowStatement; + internal bool WasRethrown { get; set; } = false; + /// /// Fix for BUG: Windows Out Of Band Releases: 906263 and 906264 /// The interpreter prompt CommandBaseStrings:InquireHalt From 2ff53acf5ebf56d2bd1ad1234519564bf8e2d966 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Fri, 26 Apr 2019 15:13:42 -0300 Subject: [PATCH 16/44] codacy changes --- src/System.Management.Automation/utils/RuntimeException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/utils/RuntimeException.cs b/src/System.Management.Automation/utils/RuntimeException.cs index 5591174c36b..d3fc5e95201 100644 --- a/src/System.Management.Automation/utils/RuntimeException.cs +++ b/src/System.Management.Automation/utils/RuntimeException.cs @@ -284,7 +284,7 @@ public bool WasThrownFromThrowStatement private bool _thrownByThrowStatement; - internal bool WasRethrown { get; set; } = false; + internal bool WasRethrown { get; set; } /// /// Fix for BUG: Windows Out Of Band Releases: 906263 and 906264 From 0127e349d23e7ca59ba5df978fd2acd1832711db Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Fri, 26 Apr 2019 22:18:49 -0300 Subject: [PATCH 17/44] refactor to fix broken Pester tests --- .../engine/ExecutionContext.cs | 94 +++++++++++++++---- .../engine/MshCommandRuntime.cs | 14 +-- .../engine/runtime/Operations/MiscOps.cs | 49 ++++++++-- 3 files changed, 125 insertions(+), 32 deletions(-) diff --git a/src/System.Management.Automation/engine/ExecutionContext.cs b/src/System.Management.Automation/engine/ExecutionContext.cs index 2f7d0a7a448..516c652c20a 100644 --- a/src/System.Management.Automation/engine/ExecutionContext.cs +++ b/src/System.Management.Automation/engine/ExecutionContext.cs @@ -567,29 +567,51 @@ internal void SetVariable(VariablePath path, object newValue) EngineSessionState.SetVariable(path, newValue, true, CommandOrigin.Internal); } - internal T GetEnumPreference(VariablePath preferenceVariablePath, T defaultPref, out bool defaultUsed) + internal bool IsBreakOnErrorEnabled() { + // Unlike the GetEnumPreference method, which can throw an exception, this + // method is used to specifically check if ErrorActionPreference is set to + // ActionPreference.Break so that it can CmdletProviderContext context = null; SessionStateScope scope = null; - object val = EngineSessionState.GetVariableValue(preferenceVariablePath, out context, out scope); - if (val is T) + object val = EngineSessionState.GetVariableValue(SpecialVariables.ErrorActionPreferenceVarPath, out context, out scope); + if (val is ActionPreference) + { + return ((ActionPreference)val) == ActionPreference.Break; + } + + if (val != null) { - // We don't want to support "Ignore" as action preferences, as it leads to bad - // scripting habits. They are only supported as cmdlet overrides. - if (val is ActionPreference) + try { - ActionPreference preference = (ActionPreference)val; - if ((preference == ActionPreference.Ignore) || (preference == ActionPreference.Suspend)) + string valString = val as string; + if (valString != null) { - // Reset the variable value - EngineSessionState.SetVariableValue(preferenceVariablePath.UserPath, defaultPref); - string message = StringUtil.Format(ErrorPackage.UnsupportedPreferenceError, preference); - throw new NotSupportedException(message); + var result = (ActionPreference)Enum.Parse(typeof(ActionPreference), valString, true); + return (result == ActionPreference.Break); } } + catch (InvalidCastException) + { + // default value is used + } + catch (ArgumentException) + { + // default value is used + } + } - T convertedResult = (T)val; + return false; + } + internal T GetEnumPreference(VariablePath preferenceVariablePath, T defaultPref, out bool defaultUsed) + { + CmdletProviderContext context = null; + SessionStateScope scope = null; + object val = EngineSessionState.GetVariableValue(preferenceVariablePath, out context, out scope); + if (val is T) + { + T convertedResult = (T)val; defaultUsed = false; return convertedResult; } @@ -626,6 +648,40 @@ internal T GetEnumPreference(VariablePath preferenceVariablePath, T defaultPr return result; } + /// + /// Checks that an action preference is an allowed value. + /// + /// + /// + /// + /// + internal void CheckActionPreference(ActionPreference currentValue, VariablePath preferenceVariablePath, ActionPreference defaultValue) + { + // We don't want to support "Ignore" as action preferences, as it leads to bad + // scripting habits. They are only supported as cmdlet overrides. + if ((currentValue == ActionPreference.Ignore) || (currentValue == ActionPreference.Suspend)) + { + // Reset the variable value + EngineSessionState.SetVariableValue(preferenceVariablePath.UserPath, defaultValue); + string message = StringUtil.Format(ErrorPackage.UnsupportedPreferenceError, currentValue); + throw new NotSupportedException(message); + } + } + + /// + /// Gets an action preference and validates that it is an allowed value. + /// + /// + /// + /// + /// + internal ActionPreference GetAndCheckActionPreference(VariablePath preferenceVariablePath, ActionPreference defaultPref, out bool defaultUsed) + { + var val = GetEnumPreference(preferenceVariablePath, defaultPref, out defaultUsed); + CheckActionPreference(val, preferenceVariablePath, defaultPref); + return val; + } + /// /// Same as GetEnumPreference, but for boolean values. /// @@ -1047,7 +1103,7 @@ internal ActionPreference DebugPreferenceVariable get { bool defaultUsed = false; - return this.GetEnumPreference( + return this.GetAndCheckActionPreference( SpecialVariables.DebugPreferenceVarPath, InitialSessionState.defaultDebugPreference, out defaultUsed); @@ -1068,7 +1124,7 @@ internal ActionPreference VerbosePreferenceVariable get { bool defaultUsed = false; - return this.GetEnumPreference( + return this.GetAndCheckActionPreference( SpecialVariables.VerbosePreferenceVarPath, InitialSessionState.defaultVerbosePreference, out defaultUsed); @@ -1089,7 +1145,7 @@ internal ActionPreference ErrorActionPreferenceVariable get { bool defaultUsed = false; - return this.GetEnumPreference( + return this.GetAndCheckActionPreference( SpecialVariables.ErrorActionPreferenceVarPath, InitialSessionState.defaultErrorActionPreference, out defaultUsed); @@ -1110,7 +1166,7 @@ internal ActionPreference WarningActionPreferenceVariable get { bool defaultUsed = false; - return this.GetEnumPreference( + return this.GetAndCheckActionPreference( SpecialVariables.WarningPreferenceVarPath, InitialSessionState.defaultWarningPreference, out defaultUsed); @@ -1131,7 +1187,7 @@ internal ActionPreference InformationActionPreferenceVariable get { bool defaultUsed = false; - return this.GetEnumPreference( + return this.GetAndCheckActionPreference( SpecialVariables.InformationPreferenceVarPath, InitialSessionState.defaultInformationPreference, out defaultUsed); @@ -1177,7 +1233,7 @@ internal ConfirmImpact ConfirmPreferenceVariable get { bool defaultUsed = false; - return this.GetEnumPreference( + return this.GetEnumPreference( SpecialVariables.ConfirmPreferenceVarPath, InitialSessionState.defaultConfirmPreference, out defaultUsed); diff --git a/src/System.Management.Automation/engine/MshCommandRuntime.cs b/src/System.Management.Automation/engine/MshCommandRuntime.cs index d70989e1a5b..e1460ee619b 100644 --- a/src/System.Management.Automation/engine/MshCommandRuntime.cs +++ b/src/System.Management.Automation/engine/MshCommandRuntime.cs @@ -2957,7 +2957,7 @@ internal ConfirmImpact ConfirmPreference if (!_isConfirmPreferenceCached) { bool defaultUsed = false; - _confirmPreference = Context.GetEnumPreference(SpecialVariables.ConfirmPreferenceVarPath, _confirmPreference, out defaultUsed); + _confirmPreference = Context.GetEnumPreference(SpecialVariables.ConfirmPreferenceVarPath, _confirmPreference, out defaultUsed); _isConfirmPreferenceCached = true; } @@ -2992,7 +2992,7 @@ internal ActionPreference DebugPreference { bool defaultUsed = false; - _debugPreference = Context.GetEnumPreference(SpecialVariables.DebugPreferenceVarPath, _debugPreference, out defaultUsed); + _debugPreference = Context.GetAndCheckActionPreference(SpecialVariables.DebugPreferenceVarPath, _debugPreference, out defaultUsed); // If the host couldn't prompt for the debug action anyways, change it to 'Continue'. // This lets hosts still see debug output without having to implement the prompting logic. @@ -3051,7 +3051,7 @@ internal ActionPreference VerbosePreference if (!_isVerbosePreferenceCached) { bool defaultUsed = false; - _verbosePreference = Context.GetEnumPreference( + _verbosePreference = Context.GetAndCheckActionPreference( SpecialVariables.VerbosePreferenceVarPath, _verbosePreference, out defaultUsed); @@ -3088,7 +3088,7 @@ internal ActionPreference WarningPreference if (!_isWarningPreferenceCached) { bool defaultUsed = false; - _warningPreference = Context.GetEnumPreference(SpecialVariables.WarningPreferenceVarPath, _warningPreference, out defaultUsed); + _warningPreference = Context.GetAndCheckActionPreference(SpecialVariables.WarningPreferenceVarPath, _warningPreference, out defaultUsed); } return _warningPreference; @@ -3257,7 +3257,7 @@ internal ActionPreference ErrorAction if (!_isErrorActionPreferenceCached) { bool defaultUsed = false; - _errorAction = Context.GetEnumPreference(SpecialVariables.ErrorActionPreferenceVarPath, _errorAction, out defaultUsed); + _errorAction = Context.GetAndCheckActionPreference(SpecialVariables.ErrorActionPreferenceVarPath, _errorAction, out defaultUsed); _isErrorActionPreferenceCached = true; } @@ -3292,7 +3292,7 @@ internal ActionPreference ProgressPreference if (!_isProgressPreferenceCached) { bool defaultUsed = false; - _progressPreference = Context.GetEnumPreference(SpecialVariables.ProgressPreferenceVarPath, _progressPreference, out defaultUsed); + _progressPreference = Context.GetAndCheckActionPreference(SpecialVariables.ProgressPreferenceVarPath, _progressPreference, out defaultUsed); _isProgressPreferenceCached = true; } @@ -3324,7 +3324,7 @@ internal ActionPreference InformationPreference if (!_isInformationPreferenceCached) { bool defaultUsed = false; - _informationPreference = Context.GetEnumPreference(SpecialVariables.InformationPreferenceVarPath, _informationPreference, out defaultUsed); + _informationPreference = Context.GetAndCheckActionPreference(SpecialVariables.InformationPreferenceVarPath, _informationPreference, out defaultUsed); _isInformationPreferenceCached = true; } diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index b03ec61d1db..62c02077286 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -1603,7 +1603,7 @@ internal static void CheckActionPreference(FunctionContext funcContext, Exceptio // set $? to false indicating an error context.QuestionMarkVariableValue = false; - ActionPreference preference = ExceptionHandlingOps.QueryForAction(rte, rte.Message, context); + ActionPreference preference = GetErrorActionPreference(context); // if the exception is new (i.e. it was not rethrown from a naked throw // statement inside of a catch block, and it is not currently being @@ -1630,10 +1630,27 @@ internal static void CheckActionPreference(FunctionContext funcContext, Exceptio // set the value of $? here in case it was reset in trap handling. context.QuestionMarkVariableValue = false; } - else if (ExceptionHandlingOps.ExceptionCannotBeStoppedContinuedOrIgnored(rte, context)) + else if (ExceptionCannotBeStoppedContinuedOrIgnored(rte, context)) { throw rte; } + else + { + if (preference == ActionPreference.Inquire && !rte.SuppressPromptInInterpreter) + { + preference = InquireForActionPreference(rte.Message, context); + } + + // If we don't have any trap handlers and the exception can be stopped, + // continued or ignored, check the action preference to ensure it has a + // valid value. Ideally this method call can go away entirely once issue + // #4348 (https://github.com/PowerShell/PowerShell/issues/4348) is + // approved. + context.CheckActionPreference( + preference, + SpecialVariables.ErrorActionPreferenceVarPath, + ActionPreference.Continue); + } if ((preference == ActionPreference.SilentlyContinue) || (preference == ActionPreference.Ignore)) @@ -1657,7 +1674,7 @@ internal static void CheckActionPreference(FunctionContext funcContext, Exceptio throw rte; } - bool errorReportedSuccessfully = ExceptionHandlingOps.ReportErrorRecord(extent, rte, context); + bool errorReportedSuccessfully = ReportErrorRecord(extent, rte, context); // set the value of $? here in case it is reset in error reporting context.QuestionMarkVariableValue = false; @@ -1773,7 +1790,25 @@ private static ActionPreference ProcessTraps(FunctionContext funcContext, } /// - /// Determine if we should continue or not after and error or exception.... + /// Gets the current error action preference value. + /// + /// The execution context. + /// The preference the user selected. + /// + /// Error action is decided by error action preference. If preference is inquire, we will + /// prompt user for their preference. + /// + internal static ActionPreference GetErrorActionPreference(ExecutionContext context) + { + bool defaultUsed; + return context.GetEnumPreference( + SpecialVariables.ErrorActionPreferenceVarPath, + ActionPreference.Continue, + out defaultUsed); + } + + /// + /// Determine if we should continue or not after an error or exception. /// /// The RuntimeException which was reported. /// The message to display. @@ -1788,8 +1823,10 @@ internal static ActionPreference QueryForAction(RuntimeException rte, string mes // 906264 "$ErrorActionPreference="Inquire" prevents original non-terminating error from being reported to $error" bool defaultUsed; ActionPreference preference = - context.GetEnumPreference(SpecialVariables.ErrorActionPreferenceVarPath, - ActionPreference.Continue, out defaultUsed); + context.GetAndCheckActionPreference( + SpecialVariables.ErrorActionPreferenceVarPath, + ActionPreference.Continue, + out defaultUsed); if (preference != ActionPreference.Inquire || rte.SuppressPromptInInterpreter) return preference; From 31c5d5265330c0711aeefce8c84e633823aa94a1 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Wed, 8 May 2019 22:13:28 -0300 Subject: [PATCH 18/44] removed checks for ActionPreference.Ignore and Suspend --- .../engine/ArgumentTypeConverterAttribute.cs | 2 +- .../engine/ExecutionContext.cs | 44 +++---------------- .../engine/MshCommandRuntime.cs | 12 ++--- .../engine/runtime/Operations/MiscOps.cs | 21 ++------- .../Scripting/ActionPreference.Tests.ps1 | 35 ++++++++++----- 5 files changed, 39 insertions(+), 75 deletions(-) diff --git a/src/System.Management.Automation/engine/ArgumentTypeConverterAttribute.cs b/src/System.Management.Automation/engine/ArgumentTypeConverterAttribute.cs index 19c383e6cd2..ce29cc47b0d 100644 --- a/src/System.Management.Automation/engine/ArgumentTypeConverterAttribute.cs +++ b/src/System.Management.Automation/engine/ArgumentTypeConverterAttribute.cs @@ -148,7 +148,7 @@ internal object Transform(EngineIntrinsics engineIntrinsics, object inputData, b if ((!bindingScriptCmdlet) && (!bindingParameters)) { // ActionPreference of Suspend is not supported as a preference variable. We can only block "Suspend" - // during variable assignment (here) - "Ignore" is blocked during variable retrieval. + // during variable assignment (here). if (_convertTypes[i] == typeof(ActionPreference)) { ActionPreference resultPreference = (ActionPreference)result; diff --git a/src/System.Management.Automation/engine/ExecutionContext.cs b/src/System.Management.Automation/engine/ExecutionContext.cs index c1a45eff838..314a6308bcd 100644 --- a/src/System.Management.Automation/engine/ExecutionContext.cs +++ b/src/System.Management.Automation/engine/ExecutionContext.cs @@ -649,40 +649,6 @@ internal T GetEnumPreference(VariablePath preferenceVariablePath, T defaultPr return result; } - /// - /// Checks that an action preference is an allowed value. - /// - /// - /// - /// - /// - internal void CheckActionPreference(ActionPreference currentValue, VariablePath preferenceVariablePath, ActionPreference defaultValue) - { - // We don't want to support "Ignore" as action preferences, as it leads to bad - // scripting habits. They are only supported as cmdlet overrides. - if ((currentValue == ActionPreference.Ignore) || (currentValue == ActionPreference.Suspend)) - { - // Reset the variable value - EngineSessionState.SetVariableValue(preferenceVariablePath.UserPath, defaultValue); - string message = StringUtil.Format(ErrorPackage.UnsupportedPreferenceError, currentValue); - throw new NotSupportedException(message); - } - } - - /// - /// Gets an action preference and validates that it is an allowed value. - /// - /// - /// - /// - /// - internal ActionPreference GetAndCheckActionPreference(VariablePath preferenceVariablePath, ActionPreference defaultPref, out bool defaultUsed) - { - var val = GetEnumPreference(preferenceVariablePath, defaultPref, out defaultUsed); - CheckActionPreference(val, preferenceVariablePath, defaultPref); - return val; - } - /// /// Same as GetEnumPreference, but for boolean values. /// @@ -1104,7 +1070,7 @@ internal ActionPreference DebugPreferenceVariable get { bool defaultUsed = false; - return this.GetAndCheckActionPreference( + return this.GetEnumPreference( SpecialVariables.DebugPreferenceVarPath, InitialSessionState.defaultDebugPreference, out defaultUsed); @@ -1125,7 +1091,7 @@ internal ActionPreference VerbosePreferenceVariable get { bool defaultUsed = false; - return this.GetAndCheckActionPreference( + return this.GetEnumPreference( SpecialVariables.VerbosePreferenceVarPath, InitialSessionState.defaultVerbosePreference, out defaultUsed); @@ -1146,7 +1112,7 @@ internal ActionPreference ErrorActionPreferenceVariable get { bool defaultUsed = false; - return this.GetAndCheckActionPreference( + return this.GetEnumPreference( SpecialVariables.ErrorActionPreferenceVarPath, InitialSessionState.defaultErrorActionPreference, out defaultUsed); @@ -1167,7 +1133,7 @@ internal ActionPreference WarningActionPreferenceVariable get { bool defaultUsed = false; - return this.GetAndCheckActionPreference( + return this.GetEnumPreference( SpecialVariables.WarningPreferenceVarPath, InitialSessionState.defaultWarningPreference, out defaultUsed); @@ -1188,7 +1154,7 @@ internal ActionPreference InformationActionPreferenceVariable get { bool defaultUsed = false; - return this.GetAndCheckActionPreference( + return this.GetEnumPreference( SpecialVariables.InformationPreferenceVarPath, InitialSessionState.defaultInformationPreference, out defaultUsed); diff --git a/src/System.Management.Automation/engine/MshCommandRuntime.cs b/src/System.Management.Automation/engine/MshCommandRuntime.cs index e1460ee619b..f2d0d7d6a1b 100644 --- a/src/System.Management.Automation/engine/MshCommandRuntime.cs +++ b/src/System.Management.Automation/engine/MshCommandRuntime.cs @@ -2992,7 +2992,7 @@ internal ActionPreference DebugPreference { bool defaultUsed = false; - _debugPreference = Context.GetAndCheckActionPreference(SpecialVariables.DebugPreferenceVarPath, _debugPreference, out defaultUsed); + _debugPreference = Context.GetEnumPreference(SpecialVariables.DebugPreferenceVarPath, _debugPreference, out defaultUsed); // If the host couldn't prompt for the debug action anyways, change it to 'Continue'. // This lets hosts still see debug output without having to implement the prompting logic. @@ -3051,7 +3051,7 @@ internal ActionPreference VerbosePreference if (!_isVerbosePreferenceCached) { bool defaultUsed = false; - _verbosePreference = Context.GetAndCheckActionPreference( + _verbosePreference = Context.GetEnumPreference( SpecialVariables.VerbosePreferenceVarPath, _verbosePreference, out defaultUsed); @@ -3088,7 +3088,7 @@ internal ActionPreference WarningPreference if (!_isWarningPreferenceCached) { bool defaultUsed = false; - _warningPreference = Context.GetAndCheckActionPreference(SpecialVariables.WarningPreferenceVarPath, _warningPreference, out defaultUsed); + _warningPreference = Context.GetEnumPreference(SpecialVariables.WarningPreferenceVarPath, _warningPreference, out defaultUsed); } return _warningPreference; @@ -3257,7 +3257,7 @@ internal ActionPreference ErrorAction if (!_isErrorActionPreferenceCached) { bool defaultUsed = false; - _errorAction = Context.GetAndCheckActionPreference(SpecialVariables.ErrorActionPreferenceVarPath, _errorAction, out defaultUsed); + _errorAction = Context.GetEnumPreference(SpecialVariables.ErrorActionPreferenceVarPath, _errorAction, out defaultUsed); _isErrorActionPreferenceCached = true; } @@ -3292,7 +3292,7 @@ internal ActionPreference ProgressPreference if (!_isProgressPreferenceCached) { bool defaultUsed = false; - _progressPreference = Context.GetAndCheckActionPreference(SpecialVariables.ProgressPreferenceVarPath, _progressPreference, out defaultUsed); + _progressPreference = Context.GetEnumPreference(SpecialVariables.ProgressPreferenceVarPath, _progressPreference, out defaultUsed); _isProgressPreferenceCached = true; } @@ -3324,7 +3324,7 @@ internal ActionPreference InformationPreference if (!_isInformationPreferenceCached) { bool defaultUsed = false; - _informationPreference = Context.GetAndCheckActionPreference(SpecialVariables.InformationPreferenceVarPath, _informationPreference, out defaultUsed); + _informationPreference = Context.GetEnumPreference(SpecialVariables.InformationPreferenceVarPath, _informationPreference, out defaultUsed); _isInformationPreferenceCached = true; } diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index dd1985ad4e5..2bb7a9c1762 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -1634,22 +1634,9 @@ internal static void CheckActionPreference(FunctionContext funcContext, Exceptio { throw rte; } - else + else if (preference == ActionPreference.Inquire && !rte.SuppressPromptInInterpreter) { - if (preference == ActionPreference.Inquire && !rte.SuppressPromptInInterpreter) - { - preference = InquireForActionPreference(rte.Message, context); - } - - // If we don't have any trap handlers and the exception can be stopped, - // continued or ignored, check the action preference to ensure it has a - // valid value. Ideally this method call can go away entirely once issue - // #4348 (https://github.com/PowerShell/PowerShell/issues/4348) is - // approved. - context.CheckActionPreference( - preference, - SpecialVariables.ErrorActionPreferenceVarPath, - ActionPreference.Continue); + preference = InquireForActionPreference(rte.Message, context); } if ((preference == ActionPreference.SilentlyContinue) || @@ -1807,7 +1794,7 @@ private static ActionPreference ProcessTraps(FunctionContext funcContext, internal static ActionPreference GetErrorActionPreference(ExecutionContext context) { bool defaultUsed; - return context.GetEnumPreference( + return context.GetEnumPreference( SpecialVariables.ErrorActionPreferenceVarPath, ActionPreference.Continue, out defaultUsed); @@ -1829,7 +1816,7 @@ internal static ActionPreference QueryForAction(RuntimeException rte, string mes // 906264 "$ErrorActionPreference="Inquire" prevents original non-terminating error from being reported to $error" bool defaultUsed; ActionPreference preference = - context.GetAndCheckActionPreference( + context.GetEnumPreference( SpecialVariables.ErrorActionPreferenceVarPath, ActionPreference.Continue, out defaultUsed); diff --git a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 index 134d4d1836d..61320249c27 100644 --- a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 +++ b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 @@ -33,22 +33,33 @@ Describe "Tests for (error, warning, etc) action preference" -Tags "CI" { $error.Count | Should -BeExactly $errorCount } - It 'action preference of Ignore cannot be set as a preference variable' { - $e = { - $GLOBAL:errorActionPreference = "Ignore" - Get-Process -Name asdfasdfasdf - } | Should -Throw -ErrorId 'System.NotSupportedException,Microsoft.PowerShell.Commands.GetProcessCommand' -PassThru - $e.CategoryInfo.Reason | Should -BeExactly 'NotSupportedException' + It 'action preference of Ignore can be set as a preference variable using a string value' { + Remove-Variable -Name ErrorActionPreference -Scope Global -Force + $GLOBAL:errorActionPreference = "Ignore" + $errorCount = $error.Count + Get-Process -Name asdfasdfasdf + + $error.Count | Should -BeExactly $errorCount + + $GLOBAL:errorActionPreference = $orgin + } + + It 'action preference of Ignore can be set as a preference variable using an enumerated value' { + $GLOBAL:errorActionPreference = [actionpreference]::Ignore + $errorCount = $error.Count + Get-Process -Name asdfasdfasdf + + $error.Count | Should -BeExactly $errorCount $GLOBAL:errorActionPreference = $orgin } - It 'action preference of Suspend cannot be set as a preference variable' { - $e = { - $GLOBAL:errorActionPreference = "Suspend" - Get-Process -Name asdfasdfasdf - } | Should -Throw -ErrorId 'RuntimeException' -PassThru - $e.CategoryInfo.Reason | Should -BeExactly 'ArgumentTransformationMetadataException' + It 'action preference of Suspend can be set as a preference variable' { + $errorCount = $error.Count + $GLOBAL:errorActionPreference = "Suspend" + Get-Process -Name asdfasdfasdf 2>$null + + $error.Count | Should -BeExactly ($errorCount + 1) $GLOBAL:errorActionPreference = $orgin } From 58afb061a3a793468b146b1bcc75681a150ef044 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Thu, 9 May 2019 20:44:56 -0300 Subject: [PATCH 19/44] code review changes --- .../engine/ExecutionContext.cs | 37 ------------------- .../engine/debugger/debugger.cs | 9 +++-- .../engine/runtime/Operations/MiscOps.cs | 15 ++++---- 3 files changed, 14 insertions(+), 47 deletions(-) diff --git a/src/System.Management.Automation/engine/ExecutionContext.cs b/src/System.Management.Automation/engine/ExecutionContext.cs index 314a6308bcd..16e3efac86d 100644 --- a/src/System.Management.Automation/engine/ExecutionContext.cs +++ b/src/System.Management.Automation/engine/ExecutionContext.cs @@ -568,43 +568,6 @@ internal void SetVariable(VariablePath path, object newValue) EngineSessionState.SetVariable(path, newValue, true, CommandOrigin.Internal); } - internal bool IsBreakOnErrorEnabled() - { - // Unlike the GetEnumPreference method, which can throw an exception, this - // method is used to specifically check if ErrorActionPreference is set to - // ActionPreference.Break so that it can - CmdletProviderContext context = null; - SessionStateScope scope = null; - object val = EngineSessionState.GetVariableValue(SpecialVariables.ErrorActionPreferenceVarPath, out context, out scope); - if (val is ActionPreference) - { - return ((ActionPreference)val) == ActionPreference.Break; - } - - if (val != null) - { - try - { - string valString = val as string; - if (valString != null) - { - var result = (ActionPreference)Enum.Parse(typeof(ActionPreference), valString, true); - return (result == ActionPreference.Break); - } - } - catch (InvalidCastException) - { - // default value is used - } - catch (ArgumentException) - { - // default value is used - } - } - - return false; - } - internal T GetEnumPreference(VariablePath preferenceVariablePath, T defaultPref, out bool defaultUsed) { CmdletProviderContext context = null; diff --git a/src/System.Management.Automation/engine/debugger/debugger.cs b/src/System.Management.Automation/engine/debugger/debugger.cs index ce2c22aaa6f..e9e9b545c76 100644 --- a/src/System.Management.Automation/engine/debugger/debugger.cs +++ b/src/System.Management.Automation/engine/debugger/debugger.cs @@ -2471,11 +2471,14 @@ internal override void Break(object triggerObject = null) SetDebuggerStepMode(true); // If the debugger is enabled and we are not in a breakpoint, trigger an immediate break in the current location - using (IEnumerator enumerator = GetCallStack().GetEnumerator()) + if (_context._debuggingMode > 0) { - if (enumerator.MoveNext() && _context._debuggingMode > 0) + using (IEnumerator enumerator = GetCallStack().GetEnumerator()) { - OnSequencePointHit(enumerator.Current.FunctionContext); + if (enumerator.MoveNext()) + { + OnSequencePointHit(enumerator.Current.FunctionContext); + } } } } diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index 2bb7a9c1762..0034f0ad56b 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -1606,7 +1606,11 @@ internal static void CheckActionPreference(FunctionContext funcContext, Exceptio ActionPreference preference = GetErrorActionPreference(context); - if (IsExceptionNew(rte, context) && + // If the exception was not rethrown and we are not currently + // handling an exception, then the exception is new, and we + // can break on it if requested. + if (!rte.WasRethrown && + context.CurrentExceptionBeingHandled == null && preference == ActionPreference.Break) { context.Debugger?.Break(rte); @@ -1637,6 +1641,9 @@ internal static void CheckActionPreference(FunctionContext funcContext, Exceptio else if (preference == ActionPreference.Inquire && !rte.SuppressPromptInInterpreter) { preference = InquireForActionPreference(rte.Message, context); + + // set the value of $? here in case it was reset in inquire. + context.QuestionMarkVariableValue = false; } if ((preference == ActionPreference.SilentlyContinue) || @@ -1672,12 +1679,6 @@ internal static void CheckActionPreference(FunctionContext funcContext, Exceptio } } - private static bool IsExceptionNew(RuntimeException rte, ExecutionContext context) - { - return !rte.WasRethrown && - context.CurrentExceptionBeingHandled == null; - } - private static ActionPreference ProcessTraps(FunctionContext funcContext, RuntimeException rte) { From a5309ced2315644b4bb945029a440cf3de1b2fe7 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Thu, 16 May 2019 09:31:47 -0300 Subject: [PATCH 20/44] removed ActionPreference.Suspend (per #9570) --- .../engine/ArgumentTypeConverterAttribute.cs | 19 ------------------- .../engine/CommandBase.cs | 3 --- .../engine/MshCommandRuntime.cs | 15 --------------- 3 files changed, 37 deletions(-) diff --git a/src/System.Management.Automation/engine/ArgumentTypeConverterAttribute.cs b/src/System.Management.Automation/engine/ArgumentTypeConverterAttribute.cs index ce29cc47b0d..2b8a39e82f2 100644 --- a/src/System.Management.Automation/engine/ArgumentTypeConverterAttribute.cs +++ b/src/System.Management.Automation/engine/ArgumentTypeConverterAttribute.cs @@ -140,25 +140,6 @@ internal object Transform(EngineIntrinsics engineIntrinsics, object inputData, b } result = LanguagePrimitives.ConvertTo(result, _convertTypes[i], CultureInfo.InvariantCulture); - - // Do validation of invalid direct variable assignments which are allowed to - // be used for parameters. - // - // Note - this is duplicated in ExecutionContext.cs as parameter binding for script cmdlets can avoid this code path. - if ((!bindingScriptCmdlet) && (!bindingParameters)) - { - // ActionPreference of Suspend is not supported as a preference variable. We can only block "Suspend" - // during variable assignment (here). - if (_convertTypes[i] == typeof(ActionPreference)) - { - ActionPreference resultPreference = (ActionPreference)result; - - if (resultPreference == ActionPreference.Suspend) - { - throw new PSInvalidCastException("InvalidActionPreference", null, ErrorPackage.UnsupportedPreferenceVariable, resultPreference); - } - } - } } } catch (PSInvalidCastException e) diff --git a/src/System.Management.Automation/engine/CommandBase.cs b/src/System.Management.Automation/engine/CommandBase.cs index 1a81deac932..91bbbfc1350 100644 --- a/src/System.Management.Automation/engine/CommandBase.cs +++ b/src/System.Management.Automation/engine/CommandBase.cs @@ -291,9 +291,6 @@ public enum ActionPreference /// Ignore the event completely (not even logging it to the target stream) Ignore, - /// Suspend the command for further diagnosis. Supported only for workflows. - Suspend, - /// Enter the debugger. Break, } // enum ActionPreference diff --git a/src/System.Management.Automation/engine/MshCommandRuntime.cs b/src/System.Management.Automation/engine/MshCommandRuntime.cs index f2d0d7d6a1b..4632ff23775 100644 --- a/src/System.Management.Automation/engine/MshCommandRuntime.cs +++ b/src/System.Management.Automation/engine/MshCommandRuntime.cs @@ -3096,11 +3096,6 @@ internal ActionPreference WarningPreference set { - if (value == ActionPreference.Suspend) - { - throw PSTraceSource.NewNotSupportedException(ErrorPackage.SuspendActionPreferenceErrorActionOnly); - } - _warningPreference = value; IsWarningActionSet = true; } @@ -3266,11 +3261,6 @@ internal ActionPreference ErrorAction set { - if (value == ActionPreference.Suspend) - { - throw PSTraceSource.NewNotSupportedException(ErrorPackage.SuspendActionPreferenceSupportedOnlyOnWorkflow); - } - _errorAction = value; IsErrorActionSet = true; } @@ -3333,11 +3323,6 @@ internal ActionPreference InformationPreference set { - if (value == ActionPreference.Suspend) - { - throw PSTraceSource.NewNotSupportedException(ErrorPackage.SuspendActionPreferenceErrorActionOnly); - } - _informationPreference = value; IsInformationActionSet = true; } From ed43f0f4130373476f4825c9e28822c23718509e Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Thu, 16 May 2019 10:05:12 -0300 Subject: [PATCH 21/44] removed EA.Suspend use from tests --- .../Scripting/ActionPreference.Tests.ps1 | 45 ------------------- .../Language/Scripting/TestsOnWinFullOnly.ps1 | 8 ---- 2 files changed, 53 deletions(-) diff --git a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 index 61320249c27..abfb292e032 100644 --- a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 +++ b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 @@ -54,16 +54,6 @@ Describe "Tests for (error, warning, etc) action preference" -Tags "CI" { $GLOBAL:errorActionPreference = $orgin } - It 'action preference of Suspend can be set as a preference variable' { - $errorCount = $error.Count - $GLOBAL:errorActionPreference = "Suspend" - Get-Process -Name asdfasdfasdf 2>$null - - $error.Count | Should -BeExactly ($errorCount + 1) - - $GLOBAL:errorActionPreference = $orgin - } - It 'enum disambiguation works' { $errorCount = $error.Count Get-Process -Name asdfasdfsadfsadf -ErrorAction Ig @@ -71,42 +61,7 @@ Describe "Tests for (error, warning, etc) action preference" -Tags "CI" { $error.Count | Should -BeExactly $errorCount } - It 'ErrorAction = Suspend works on Workflow' -Skip:$IsCoreCLR { - . .\TestsOnWinFullOnly.ps1 - Run-TestOnWinFull "ActionPreference:ErrorAction=SuspendOnWorkflow" - } - - It 'ErrorAction = Suspend does not work on functions' { - function MyHelperFunction { - [CmdletBinding()] - param() - "Hello" - } - - { MyHelperFunction -ErrorAction Suspend } | Should -Throw -ErrorId "ParameterBindingFailed,MyHelperFunction" - } - - It 'ErrorAction = Suspend does not work on cmdlets' { - { Get-Process -ErrorAction Suspend } | Should -Throw -ErrorId "ParameterBindingFailed,Microsoft.PowerShell.Commands.GetProcessCommand" - } - - It 'WarningAction = Suspend does not work' { - { Get-Process -WarningAction Suspend } | Should -Throw -ErrorId "ParameterBindingFailed,Microsoft.PowerShell.Commands.GetProcessCommand" - } - #issue 2076 - It 'ErrorAction and WarningAction are the only action preferences do not support suspend' -Pending{ - $params = [System.Management.Automation.Internal.CommonParameters].GetProperties().Name | Select-String Action - - $suspendErrors = $null - $num=0 - - $params | ForEach-Object { - $input=@{'InputObject' = 'Test';$_='Suspend'} - { Write-Output @input } | Should -Throw -ErrorId "ParameterBindingFailed,Microsoft.PowerShell.Commands.WriteOutputCommand" - } - } - It ' does not take precedence over $ErrorActionPreference' -TestCases @( @{switch="Verbose"}, @{switch="Debug"} diff --git a/test/powershell/Language/Scripting/TestsOnWinFullOnly.ps1 b/test/powershell/Language/Scripting/TestsOnWinFullOnly.ps1 index 3a273c02b8a..ca352a08a18 100644 --- a/test/powershell/Language/Scripting/TestsOnWinFullOnly.ps1 +++ b/test/powershell/Language/Scripting/TestsOnWinFullOnly.ps1 @@ -7,14 +7,6 @@ function Run-TestOnWinFull switch ($name) { - "ActionPreference:ErrorAction=SuspendOnWorkflow" { - workflow TestErrorActionSuspend { "Hello" } - - $r = TestErrorActionSuspend -ErrorAction Suspend - - $r | Should -BeExactly 'Hello' - break; } - "ForeachParallel:ASTOfParallelForeachOnWorkflow" { Import-Module PSWorkflow $errors = @() From f1b1e516d57686fed664389cfb9d6d0b48994e1c Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Thu, 30 May 2019 10:47:39 -0300 Subject: [PATCH 22/44] review feedback; refactored $? reassignments --- .../engine/debugger/debugger.cs | 4 +++ .../engine/runtime/Operations/MiscOps.cs | 32 +++++++++---------- .../Scripting/ActionPreference.Tests.ps1 | 30 +++++++++-------- 3 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/System.Management.Automation/engine/debugger/debugger.cs b/src/System.Management.Automation/engine/debugger/debugger.cs index 02a9ea0b09b..476e434f5a9 100644 --- a/src/System.Management.Automation/engine/debugger/debugger.cs +++ b/src/System.Management.Automation/engine/debugger/debugger.cs @@ -1748,6 +1748,8 @@ private void OnDebuggerStop(InvocationInfo invocationInfo, List brea return; } + bool oldQuestionMarkVariableValue = _context.QuestionMarkVariableValue; + _context.SetVariable(SpecialVariables.PSDebugContextVarPath, new PSDebugContext(invocationInfo, breakpoints, TriggerObject)); FunctionInfo defaultPromptInfo = null; @@ -1862,6 +1864,8 @@ private void OnDebuggerStop(InvocationInfo invocationInfo, List brea DebuggerStopEventArgs oldArgs; _debuggerStopEventArgs.TryPop(out oldArgs); + _context.QuestionMarkVariableValue = oldQuestionMarkVariableValue; + _inBreakpoint = false; } } diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index 0034f0ad56b..907de374fa8 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -1614,9 +1614,6 @@ internal static void CheckActionPreference(FunctionContext funcContext, Exceptio preference == ActionPreference.Break) { context.Debugger?.Break(rte); - - // set the value of $? here in case it was reset in the debugger. - context.QuestionMarkVariableValue = false; } // Item2 in the trap tuples is the action (script) for the trap. @@ -1630,9 +1627,6 @@ internal static void CheckActionPreference(FunctionContext funcContext, Exceptio // update the action preference according to how the exception is // handled in the trap statement(s). preference = ProcessTraps(funcContext, rte); - - // set the value of $? here in case it was reset in trap handling. - context.QuestionMarkVariableValue = false; } else if (ExceptionCannotBeStoppedContinuedOrIgnored(rte, context)) { @@ -1641,9 +1635,6 @@ internal static void CheckActionPreference(FunctionContext funcContext, Exceptio else if (preference == ActionPreference.Inquire && !rte.SuppressPromptInInterpreter) { preference = InquireForActionPreference(rte.Message, context); - - // set the value of $? here in case it was reset in inquire. - context.QuestionMarkVariableValue = false; } if ((preference == ActionPreference.SilentlyContinue) || @@ -1668,12 +1659,7 @@ internal static void CheckActionPreference(FunctionContext funcContext, Exceptio throw rte; } - bool errorReportedSuccessfully = ReportErrorRecord(extent, rte, context); - - // set the value of $? here in case it is reset in error reporting - context.QuestionMarkVariableValue = false; - - if (!errorReportedSuccessfully) + if (!ReportErrorRecord(extent, rte, context)) { throw rte; } @@ -1711,10 +1697,12 @@ private static ActionPreference ProcessTraps(FunctionContext funcContext, if (handler != -1) { Diagnostics.Assert(exception != null, "Exception object can't be null."); + + var context = funcContext._executionContext; + try { ErrorRecord err = rte.ErrorRecord; - var context = funcContext._executionContext; // CurrentCommandProcessor is normally not null, but it is null // when executing some unit tests through reflection. if (context.CurrentCommandProcessor != null) @@ -1778,6 +1766,13 @@ private static ActionPreference ProcessTraps(FunctionContext funcContext, // Terminate this block of statements. return ActionPreference.Stop; } + finally + { + // The questionmark variable will always be false when we process a trap, so + // set it to false to ensure it didn't change as a result of anything done + // inside the trap + context.QuestionMarkVariableValue = false; + } } return ActionPreference.Stop; @@ -1860,10 +1855,12 @@ internal static ActionPreference InquireForActionPreference(string message, Exec string caption = ParserStrings.ExceptionActionPromptCaption; int choice; + bool oldQuestionMarkVariableValue = context.QuestionMarkVariableValue; while ((choice = ui.PromptForChoice(caption, message, choices, 0)) == 3) { context.EngineHostInterface.EnterNestedPrompt(); } + context.QuestionMarkVariableValue = oldQuestionMarkVariableValue; if (choice == 0) return ActionPreference.Continue; @@ -1952,6 +1949,9 @@ internal static bool ReportErrorRecord(IScriptExtent extent, RuntimeException rt context.ShellFunctionErrorOutputPipe.Add(errorWrap); + // set the value of $? here in case it is reset in error reporting. + context.QuestionMarkVariableValue = false; + return true; } diff --git a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 index abfb292e032..ee6d799ae38 100644 --- a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 +++ b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 @@ -34,24 +34,28 @@ Describe "Tests for (error, warning, etc) action preference" -Tags "CI" { } It 'action preference of Ignore can be set as a preference variable using a string value' { - Remove-Variable -Name ErrorActionPreference -Scope Global -Force - $GLOBAL:errorActionPreference = "Ignore" - $errorCount = $error.Count - Get-Process -Name asdfasdfasdf - - $error.Count | Should -BeExactly $errorCount + try { + Remove-Variable -Name ErrorActionPreference -Scope Global -Force + $GLOBAL:errorActionPreference = "Ignore" + $errorCount = $error.Count + Get-Process -Name asdfasdfasdf - $GLOBAL:errorActionPreference = $orgin + $error.Count | Should -BeExactly $errorCount + } finally { + $GLOBAL:errorActionPreference = $orgin + } } It 'action preference of Ignore can be set as a preference variable using an enumerated value' { - $GLOBAL:errorActionPreference = [actionpreference]::Ignore - $errorCount = $error.Count - Get-Process -Name asdfasdfasdf - - $error.Count | Should -BeExactly $errorCount + try { + $GLOBAL:errorActionPreference = [actionpreference]::Ignore + $errorCount = $error.Count + Get-Process -Name asdfasdfasdf - $GLOBAL:errorActionPreference = $orgin + $error.Count | Should -BeExactly $errorCount + } finally { + $GLOBAL:errorActionPreference = $orgin + } } It 'enum disambiguation works' { From 4bbc564d27bbc7208116deb638fb62bbaed0b3f4 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Mon, 3 Jun 2019 13:18:13 -0300 Subject: [PATCH 23/44] add argument labels for self-documenting code --- .../engine/debugger/debugger.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/System.Management.Automation/engine/debugger/debugger.cs b/src/System.Management.Automation/engine/debugger/debugger.cs index 476e434f5a9..bd3a680886b 100644 --- a/src/System.Management.Automation/engine/debugger/debugger.cs +++ b/src/System.Management.Automation/engine/debugger/debugger.cs @@ -4867,15 +4867,15 @@ internal class DebuggerCommandProcessor public DebuggerCommandProcessor() { _commandTable = new Dictionary(StringComparer.OrdinalIgnoreCase); - _commandTable[StepCommand] = _commandTable[StepShortcut] = new DebuggerCommand(StepCommand, DebuggerResumeAction.StepInto, true, false); - _commandTable[StepOutCommand] = _commandTable[StepOutShortcut] = new DebuggerCommand(StepOutCommand, DebuggerResumeAction.StepOut, false, false); - _commandTable[StepOverCommand] = _commandTable[StepOverShortcut] = new DebuggerCommand(StepOverCommand, DebuggerResumeAction.StepOver, true, false); - _commandTable[ContinueCommand] = _commandTable[ContinueShortcut] = new DebuggerCommand(ContinueCommand, DebuggerResumeAction.Continue, false, false); - _commandTable[StopCommand] = _commandTable[StopShortcut] = new DebuggerCommand(StopCommand, DebuggerResumeAction.Stop, false, false); - _commandTable[GetStackTraceShortcut] = new DebuggerCommand("get-pscallstack", null, false, false); - _commandTable[HelpCommand] = _commandTable[HelpShortcut] = _helpCommand = new DebuggerCommand(HelpCommand, null, false, true); - _commandTable[ListCommand] = _commandTable[ListShortcut] = _listCommand = new DebuggerCommand(ListCommand, null, true, true); - _commandTable[string.Empty] = new DebuggerCommand(string.Empty, null, false, true); + _commandTable[StepCommand] = _commandTable[StepShortcut] = new DebuggerCommand(StepCommand, DebuggerResumeAction.StepInto, repeatOnEnter:true, executedByDebugger:false); + _commandTable[StepOutCommand] = _commandTable[StepOutShortcut] = new DebuggerCommand(StepOutCommand, DebuggerResumeAction.StepOut, repeatOnEnter: false, executedByDebugger: false); + _commandTable[StepOverCommand] = _commandTable[StepOverShortcut] = new DebuggerCommand(StepOverCommand, DebuggerResumeAction.StepOver, repeatOnEnter: true, executedByDebugger: false); + _commandTable[ContinueCommand] = _commandTable[ContinueShortcut] = new DebuggerCommand(ContinueCommand, DebuggerResumeAction.Continue, repeatOnEnter: false, executedByDebugger: false); + _commandTable[StopCommand] = _commandTable[StopShortcut] = new DebuggerCommand(StopCommand, DebuggerResumeAction.Stop, repeatOnEnter: false, executedByDebugger: false); + _commandTable[GetStackTraceShortcut] = new DebuggerCommand("get-pscallstack", null, repeatOnEnter: false, executedByDebugger: false); + _commandTable[HelpCommand] = _commandTable[HelpShortcut] = _helpCommand = new DebuggerCommand(HelpCommand, null, repeatOnEnter: false, executedByDebugger: true); + _commandTable[ListCommand] = _commandTable[ListShortcut] = _listCommand = new DebuggerCommand(ListCommand, null, repeatOnEnter: true, executedByDebugger: true); + _commandTable[string.Empty] = new DebuggerCommand(string.Empty, null, repeatOnEnter: false, executedByDebugger: true); } /// From 3c8cf823f2156ac185aea1d6f00a4f1c01a7815a Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Sun, 4 Aug 2019 16:01:41 -0300 Subject: [PATCH 24/44] replace AP.Suspend (it's being removed in #10223) --- .../engine/ArgumentTypeConverterAttribute.cs | 19 +++++++++++++++++++ .../engine/CommandBase.cs | 5 ++++- .../engine/ExecutionContext.cs | 15 +++++++++++++++ .../engine/MshCommandRuntime.cs | 15 +++++++++++++++ .../Language/Scripting/TestsOnWinFullOnly.ps1 | 9 +++++++++ 5 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/ArgumentTypeConverterAttribute.cs b/src/System.Management.Automation/engine/ArgumentTypeConverterAttribute.cs index 2b8a39e82f2..19c383e6cd2 100644 --- a/src/System.Management.Automation/engine/ArgumentTypeConverterAttribute.cs +++ b/src/System.Management.Automation/engine/ArgumentTypeConverterAttribute.cs @@ -140,6 +140,25 @@ internal object Transform(EngineIntrinsics engineIntrinsics, object inputData, b } result = LanguagePrimitives.ConvertTo(result, _convertTypes[i], CultureInfo.InvariantCulture); + + // Do validation of invalid direct variable assignments which are allowed to + // be used for parameters. + // + // Note - this is duplicated in ExecutionContext.cs as parameter binding for script cmdlets can avoid this code path. + if ((!bindingScriptCmdlet) && (!bindingParameters)) + { + // ActionPreference of Suspend is not supported as a preference variable. We can only block "Suspend" + // during variable assignment (here) - "Ignore" is blocked during variable retrieval. + if (_convertTypes[i] == typeof(ActionPreference)) + { + ActionPreference resultPreference = (ActionPreference)result; + + if (resultPreference == ActionPreference.Suspend) + { + throw new PSInvalidCastException("InvalidActionPreference", null, ErrorPackage.UnsupportedPreferenceVariable, resultPreference); + } + } + } } } catch (PSInvalidCastException e) diff --git a/src/System.Management.Automation/engine/CommandBase.cs b/src/System.Management.Automation/engine/CommandBase.cs index 91bbbfc1350..3091fb5673f 100644 --- a/src/System.Management.Automation/engine/CommandBase.cs +++ b/src/System.Management.Automation/engine/CommandBase.cs @@ -285,12 +285,15 @@ public enum ActionPreference /// Handle this event as normal and continue Continue, - /// Ask whether to stop or continue (legacy; use Break instead) + /// Ask whether to stop or continue. Inquire, /// Ignore the event completely (not even logging it to the target stream) Ignore, + /// Suspend the command for further diagnosis. Supported only for workflows. + Suspend, + /// Enter the debugger. Break, } // enum ActionPreference diff --git a/src/System.Management.Automation/engine/ExecutionContext.cs b/src/System.Management.Automation/engine/ExecutionContext.cs index 16e3efac86d..4281b848da2 100644 --- a/src/System.Management.Automation/engine/ExecutionContext.cs +++ b/src/System.Management.Automation/engine/ExecutionContext.cs @@ -575,7 +575,22 @@ internal T GetEnumPreference(VariablePath preferenceVariablePath, T defaultPr object val = EngineSessionState.GetVariableValue(preferenceVariablePath, out context, out scope); if (val is T) { + // We don't want to support "Ignore" as action preferences, as it leads to bad + // scripting habits. They are only supported as cmdlet overrides. + if (val is ActionPreference) + { + ActionPreference preference = (ActionPreference)val; + if ((preference == ActionPreference.Ignore) || (preference == ActionPreference.Suspend)) + { + // Reset the variable value + EngineSessionState.SetVariableValue(preferenceVariablePath.UserPath, defaultPref); + string message = StringUtil.Format(ErrorPackage.UnsupportedPreferenceError, preference); + throw new NotSupportedException(message); + } + } + T convertedResult = (T)val; + defaultUsed = false; return convertedResult; } diff --git a/src/System.Management.Automation/engine/MshCommandRuntime.cs b/src/System.Management.Automation/engine/MshCommandRuntime.cs index ac23c9752d8..07208dc3435 100644 --- a/src/System.Management.Automation/engine/MshCommandRuntime.cs +++ b/src/System.Management.Automation/engine/MshCommandRuntime.cs @@ -3096,6 +3096,11 @@ internal ActionPreference WarningPreference set { + if (value == ActionPreference.Suspend) + { + throw PSTraceSource.NewNotSupportedException(ErrorPackage.SuspendActionPreferenceErrorActionOnly); + } + _warningPreference = value; IsWarningActionSet = true; } @@ -3261,6 +3266,11 @@ internal ActionPreference ErrorAction set { + if (value == ActionPreference.Suspend) + { + throw PSTraceSource.NewNotSupportedException(ErrorPackage.SuspendActionPreferenceSupportedOnlyOnWorkflow); + } + _errorAction = value; IsErrorActionSet = true; } @@ -3323,6 +3333,11 @@ internal ActionPreference InformationPreference set { + if (value == ActionPreference.Suspend) + { + throw PSTraceSource.NewNotSupportedException(ErrorPackage.SuspendActionPreferenceErrorActionOnly); + } + _informationPreference = value; IsInformationActionSet = true; } diff --git a/test/powershell/Language/Scripting/TestsOnWinFullOnly.ps1 b/test/powershell/Language/Scripting/TestsOnWinFullOnly.ps1 index ca352a08a18..d29428f91d3 100644 --- a/test/powershell/Language/Scripting/TestsOnWinFullOnly.ps1 +++ b/test/powershell/Language/Scripting/TestsOnWinFullOnly.ps1 @@ -7,6 +7,15 @@ function Run-TestOnWinFull switch ($name) { + "ActionPreference:ErrorAction=SuspendOnWorkflow" { + workflow TestErrorActionSuspend { "Hello" } + + $r = TestErrorActionSuspend -ErrorAction Suspend + + $r | Should -BeExactly 'Hello' + break; + } + "ForeachParallel:ASTOfParallelForeachOnWorkflow" { Import-Module PSWorkflow $errors = @() From e840416d624d5cd8324577d7a456ff7ffd27a9bc Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Sun, 4 Aug 2019 16:05:55 -0300 Subject: [PATCH 25/44] remove accidental changes --- src/System.Management.Automation/engine/CommandBase.cs | 2 +- test/powershell/Language/Scripting/TestsOnWinFullOnly.ps1 | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandBase.cs b/src/System.Management.Automation/engine/CommandBase.cs index 3091fb5673f..dbf80b2b448 100644 --- a/src/System.Management.Automation/engine/CommandBase.cs +++ b/src/System.Management.Automation/engine/CommandBase.cs @@ -285,7 +285,7 @@ public enum ActionPreference /// Handle this event as normal and continue Continue, - /// Ask whether to stop or continue. + /// Ask whether to stop or continue Inquire, /// Ignore the event completely (not even logging it to the target stream) diff --git a/test/powershell/Language/Scripting/TestsOnWinFullOnly.ps1 b/test/powershell/Language/Scripting/TestsOnWinFullOnly.ps1 index d29428f91d3..3a273c02b8a 100644 --- a/test/powershell/Language/Scripting/TestsOnWinFullOnly.ps1 +++ b/test/powershell/Language/Scripting/TestsOnWinFullOnly.ps1 @@ -13,8 +13,7 @@ function Run-TestOnWinFull $r = TestErrorActionSuspend -ErrorAction Suspend $r | Should -BeExactly 'Hello' - break; - } + break; } "ForeachParallel:ASTOfParallelForeachOnWorkflow" { Import-Module PSWorkflow From f1b226c4a8319f378674a317f3cc0856748bc7ea Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Sun, 4 Aug 2019 16:06:52 -0300 Subject: [PATCH 26/44] minor correction to previous commit --- src/System.Management.Automation/engine/CommandBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/CommandBase.cs b/src/System.Management.Automation/engine/CommandBase.cs index dbf80b2b448..30b5910088a 100644 --- a/src/System.Management.Automation/engine/CommandBase.cs +++ b/src/System.Management.Automation/engine/CommandBase.cs @@ -285,7 +285,7 @@ public enum ActionPreference /// Handle this event as normal and continue Continue, - /// Ask whether to stop or continue + /// Ask whether to stop or continue Inquire, /// Ignore the event completely (not even logging it to the target stream) From 4c4f20e0e23a386f5788e6bfa105fb8479163496 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Sun, 4 Aug 2019 16:16:01 -0300 Subject: [PATCH 27/44] CodeFactor --- .../engine/runtime/Operations/MiscOps.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index 907de374fa8..22a90096ea3 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -1854,12 +1854,14 @@ internal static ActionPreference InquireForActionPreference(string message, Exec string caption = ParserStrings.ExceptionActionPromptCaption; - int choice; bool oldQuestionMarkVariableValue = context.QuestionMarkVariableValue; + + int choice; while ((choice = ui.PromptForChoice(caption, message, choices, 0)) == 3) { context.EngineHostInterface.EnterNestedPrompt(); } + context.QuestionMarkVariableValue = oldQuestionMarkVariableValue; if (choice == 0) From cf2db5c1f29cdbacfd7fb44c4fff705d3012e6ff Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Mon, 5 Aug 2019 11:18:52 -0300 Subject: [PATCH 28/44] replace tests that are updated elsewhere --- .../Scripting/ActionPreference.Tests.ps1 | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 index ee6d799ae38..a0516585320 100644 --- a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 +++ b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 @@ -4,7 +4,6 @@ Describe "Tests for (error, warning, etc) action preference" -Tags "CI" { BeforeAll { $orgin = $GLOBAL:errorActionPreference } - AfterAll { if ($GLOBAL:errorActionPreference -ne $orgin) { @@ -18,44 +17,36 @@ Describe "Tests for (error, warning, etc) action preference" -Tags "CI" { get-childitem nosuchfile.nosuchextension -ErrorAction stop -ErrorVariable err } catch {} - It '$err.Count' { $err.Count | Should -Be 1 } It '$err[0] should not be $null' { $err[0] | Should -Not -BeNullOrEmpty } It '$err[0].GetType().Name' { $err[0] | Should -BeOfType "System.Management.Automation.ActionPreferenceStopException" } It '$err[0].ErrorRecord' { $err[0].ErrorRecord | Should -Not -BeNullOrEmpty } It '$err[0].ErrorRecord.Exception.GetType().Name' { $err[0].ErrorRecord.Exception | Should -BeOfType "System.Management.Automation.ItemNotFoundException" } } - It 'ActionPreference Ignore Works' { $errorCount = $error.Count Get-Process -Name asdfasdfsadfsadf -ErrorAction Ignore - $error.Count | Should -BeExactly $errorCount } - It 'action preference of Ignore can be set as a preference variable using a string value' { - try { - Remove-Variable -Name ErrorActionPreference -Scope Global -Force + It 'action preference of Ignore cannot be set as a preference variable' { + $e = { $GLOBAL:errorActionPreference = "Ignore" - $errorCount = $error.Count Get-Process -Name asdfasdfasdf + } | Should -Throw -ErrorId 'System.NotSupportedException,Microsoft.PowerShell.Commands.GetProcessCommand' -PassThru + $e.CategoryInfo.Reason | Should -BeExactly 'NotSupportedException' - $error.Count | Should -BeExactly $errorCount - } finally { - $GLOBAL:errorActionPreference = $orgin - } + $GLOBAL:errorActionPreference = $orgin } - It 'action preference of Ignore can be set as a preference variable using an enumerated value' { - try { - $GLOBAL:errorActionPreference = [actionpreference]::Ignore - $errorCount = $error.Count + It 'action preference of Suspend cannot be set as a preference variable' { + $e = { + $GLOBAL:errorActionPreference = "Suspend" Get-Process -Name asdfasdfasdf + } | Should -Throw -ErrorId 'RuntimeException' -PassThru + $e.CategoryInfo.Reason | Should -BeExactly 'ArgumentTransformationMetadataException' - $error.Count | Should -BeExactly $errorCount - } finally { - $GLOBAL:errorActionPreference = $orgin - } + $GLOBAL:errorActionPreference = $orgin } It 'enum disambiguation works' { From f321fb42f0e950e3716b7ac1b4ea6e6752cbb73d Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Mon, 5 Aug 2019 11:22:26 -0300 Subject: [PATCH 29/44] replace tests that are removed elsewhere --- .../Scripting/ActionPreference.Tests.ps1 | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 index a0516585320..134d4d1836d 100644 --- a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 +++ b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 @@ -4,6 +4,7 @@ Describe "Tests for (error, warning, etc) action preference" -Tags "CI" { BeforeAll { $orgin = $GLOBAL:errorActionPreference } + AfterAll { if ($GLOBAL:errorActionPreference -ne $orgin) { @@ -17,15 +18,18 @@ Describe "Tests for (error, warning, etc) action preference" -Tags "CI" { get-childitem nosuchfile.nosuchextension -ErrorAction stop -ErrorVariable err } catch {} + It '$err.Count' { $err.Count | Should -Be 1 } It '$err[0] should not be $null' { $err[0] | Should -Not -BeNullOrEmpty } It '$err[0].GetType().Name' { $err[0] | Should -BeOfType "System.Management.Automation.ActionPreferenceStopException" } It '$err[0].ErrorRecord' { $err[0].ErrorRecord | Should -Not -BeNullOrEmpty } It '$err[0].ErrorRecord.Exception.GetType().Name' { $err[0].ErrorRecord.Exception | Should -BeOfType "System.Management.Automation.ItemNotFoundException" } } + It 'ActionPreference Ignore Works' { $errorCount = $error.Count Get-Process -Name asdfasdfsadfsadf -ErrorAction Ignore + $error.Count | Should -BeExactly $errorCount } @@ -56,7 +60,42 @@ Describe "Tests for (error, warning, etc) action preference" -Tags "CI" { $error.Count | Should -BeExactly $errorCount } + It 'ErrorAction = Suspend works on Workflow' -Skip:$IsCoreCLR { + . .\TestsOnWinFullOnly.ps1 + Run-TestOnWinFull "ActionPreference:ErrorAction=SuspendOnWorkflow" + } + + It 'ErrorAction = Suspend does not work on functions' { + function MyHelperFunction { + [CmdletBinding()] + param() + "Hello" + } + + { MyHelperFunction -ErrorAction Suspend } | Should -Throw -ErrorId "ParameterBindingFailed,MyHelperFunction" + } + + It 'ErrorAction = Suspend does not work on cmdlets' { + { Get-Process -ErrorAction Suspend } | Should -Throw -ErrorId "ParameterBindingFailed,Microsoft.PowerShell.Commands.GetProcessCommand" + } + + It 'WarningAction = Suspend does not work' { + { Get-Process -WarningAction Suspend } | Should -Throw -ErrorId "ParameterBindingFailed,Microsoft.PowerShell.Commands.GetProcessCommand" + } + #issue 2076 + It 'ErrorAction and WarningAction are the only action preferences do not support suspend' -Pending{ + $params = [System.Management.Automation.Internal.CommonParameters].GetProperties().Name | Select-String Action + + $suspendErrors = $null + $num=0 + + $params | ForEach-Object { + $input=@{'InputObject' = 'Test';$_='Suspend'} + { Write-Output @input } | Should -Throw -ErrorId "ParameterBindingFailed,Microsoft.PowerShell.Commands.WriteOutputCommand" + } + } + It ' does not take precedence over $ErrorActionPreference' -TestCases @( @{switch="Verbose"}, @{switch="Debug"} From e6f8f9b127004eb701c8d99590dfb4cb32a80cce Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Mon, 5 Aug 2019 13:37:22 -0300 Subject: [PATCH 30/44] fix Pester test failure --- src/System.Management.Automation/engine/ExecutionContext.cs | 4 +--- .../engine/runtime/Operations/MiscOps.cs | 5 ++--- .../powershell/Language/Scripting/ActionPreference.Tests.ps1 | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/System.Management.Automation/engine/ExecutionContext.cs b/src/System.Management.Automation/engine/ExecutionContext.cs index 4281b848da2..4f87850900a 100644 --- a/src/System.Management.Automation/engine/ExecutionContext.cs +++ b/src/System.Management.Automation/engine/ExecutionContext.cs @@ -570,9 +570,7 @@ internal void SetVariable(VariablePath path, object newValue) internal T GetEnumPreference(VariablePath preferenceVariablePath, T defaultPref, out bool defaultUsed) { - CmdletProviderContext context = null; - SessionStateScope scope = null; - object val = EngineSessionState.GetVariableValue(preferenceVariablePath, out context, out scope); + object val = EngineSessionState.GetVariableValue(preferenceVariablePath, _, _); if (val is T) { // We don't want to support "Ignore" as action preferences, as it leads to bad diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index 22a90096ea3..cc6960a23a4 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -1789,11 +1789,10 @@ private static ActionPreference ProcessTraps(FunctionContext funcContext, /// internal static ActionPreference GetErrorActionPreference(ExecutionContext context) { - bool defaultUsed; - return context.GetEnumPreference( + return context.GetEnumPreference( SpecialVariables.ErrorActionPreferenceVarPath, ActionPreference.Continue, - out defaultUsed); + _); } /// diff --git a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 index 134d4d1836d..68277b6ff06 100644 --- a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 +++ b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 @@ -37,7 +37,7 @@ Describe "Tests for (error, warning, etc) action preference" -Tags "CI" { $e = { $GLOBAL:errorActionPreference = "Ignore" Get-Process -Name asdfasdfasdf - } | Should -Throw -ErrorId 'System.NotSupportedException,Microsoft.PowerShell.Commands.GetProcessCommand' -PassThru + } | Should -Throw -ErrorId 'System.NotSupportedException' -PassThru $e.CategoryInfo.Reason | Should -BeExactly 'NotSupportedException' $GLOBAL:errorActionPreference = $orgin From f3fdb359efc220e64f7859dd357d058bc3997f58 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Mon, 5 Aug 2019 15:41:04 -0300 Subject: [PATCH 31/44] replace mistakenly removed keywords --- src/System.Management.Automation/engine/ExecutionContext.cs | 2 +- .../engine/runtime/Operations/MiscOps.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/engine/ExecutionContext.cs b/src/System.Management.Automation/engine/ExecutionContext.cs index 4f87850900a..45cb3bd7fa5 100644 --- a/src/System.Management.Automation/engine/ExecutionContext.cs +++ b/src/System.Management.Automation/engine/ExecutionContext.cs @@ -570,7 +570,7 @@ internal void SetVariable(VariablePath path, object newValue) internal T GetEnumPreference(VariablePath preferenceVariablePath, T defaultPref, out bool defaultUsed) { - object val = EngineSessionState.GetVariableValue(preferenceVariablePath, _, _); + object val = EngineSessionState.GetVariableValue(preferenceVariablePath, out _, out _); if (val is T) { // We don't want to support "Ignore" as action preferences, as it leads to bad diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index cc6960a23a4..ef667b77377 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -1792,7 +1792,7 @@ internal static ActionPreference GetErrorActionPreference(ExecutionContext conte return context.GetEnumPreference( SpecialVariables.ErrorActionPreferenceVarPath, ActionPreference.Continue, - _); + out _); } /// From 0efbd38a6332219fea80b1b2c030d332a34a7552 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Mon, 5 Aug 2019 16:59:16 -0300 Subject: [PATCH 32/44] add automated tests for local debugger --- .../Scripting/ActionPreference.Tests.ps1 | 220 ++++++++++++++++++ .../Wait-Debugger.Tests.ps1 | 37 +++ 2 files changed, 257 insertions(+) create mode 100644 test/powershell/Modules/Microsoft.PowerShell.Utility/Wait-Debugger.Tests.ps1 diff --git a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 index 68277b6ff06..1b757ddd184 100644 --- a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 +++ b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 @@ -1,5 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. + Describe "Tests for (error, warning, etc) action preference" -Tags "CI" { BeforeAll { $orgin = $GLOBAL:errorActionPreference @@ -115,3 +116,222 @@ Describe "Tests for (error, warning, etc) action preference" -Tags "CI" { Remove-Item "$testdrive\test.txt" -Force } } + +Describe 'ActionPreference.Break tests' -tag 'CI' { + + BeforeAll { + Register-DebuggerHandler + } + + AfterAll { + Unregister-DebuggerHandler + } + + Context '-ErrorAction Break should break on a non-terminating error' { + BeforeAll { + $testScript = { + function Test-Break { + [CmdletBinding()] + param() + try { + # Generate a non-terminating error + Get-Process -Id ([int]::MaxValue) + # Do something afterwards + 'This should still run' + } catch { + 'Do nothing' + } finally { + 'This finally runs' + } + } + Test-Break -ErrorAction Break + } + + $results = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 's', 's') + } + + It 'Should show 3 debugger commands were invoked' { + # There is always an implicit 'c' command that keeps the debugger automation moving + $results.Count | Should -Be 3 + } + + It 'The breakpoint should be the statement that generated the non-terminating error' { + $results[0] | ShouldHaveExtent -Line 7 -FromColumn 25 -ToColumn 58 + } + + It 'The second statement should be the statement after that which generated the non-terminating error' { + $results[1] | ShouldHaveExtent -Line 9 -FromColumn 25 -ToColumn 48 + } + + It 'The third statement should be the statement in the finally block' { + $results[2] | ShouldHaveExtent -Line 13 -FromColumn 25 -ToColumn 44 + } + } + + Context '-ErrorAction Break should break on a terminating error' { + BeforeAll { + $testScript = { + function Test-Break { + [CmdletBinding()] + param() + try { + # Generate a terminating error + Get-Process -TheAnswer 42 + # Do something afterwards + 'This should not run' + } catch { + 'Do nothing' + } finally { + 'This finally runs' + } + } + Test-Break -ErrorAction Break + } + + $results = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 's', 's') + } + + It 'Should show 3 debugger commands were invoked' { + # There is always an implicit 'c' command that keeps the debugger automation moving + $results.Count | Should -Be 3 + } + + It 'The breakpoint should be the statement that generated the terminating error' { + $results[0] | ShouldHaveExtent -Line 7 -FromColumn 25 -ToColumn 50 + } + + It 'The second statement should be the statement in the catch block where the terminating error is caught' { + $results[1] | ShouldHaveExtent -Line 11 -FromColumn 25 -ToColumn 37 + } + + It 'The third statement should be the statement in the finally block' { + $results[2] | ShouldHaveExtent -Line 13 -FromColumn 25 -ToColumn 44 + } + } + + Context '-ErrorAction Break should not break on a naked rethrow' { + BeforeAll { + $testScript = { + function Test-Break { + [CmdletBinding()] + param() + try { + try { + # Generate a terminating error + Get-Process -TheAnswer 42 + } catch { + throw + } + } catch { + # Swallow the exception here + } + } + Test-Break -ErrorAction Break + } + + $results = @(Test-Debugger -ScriptBlock $testScript) + } + + It 'Should show 1 debugger command was invoked' { + # ErrorAction break should only trigger on the initial terminating error + $results.Count | Should -Be 1 + } + + It 'The breakpoint should be the statement that generated the terminating error' { + $results[0] | ShouldHaveExtent -Line 8 -FromColumn 29 -ToColumn 54 + } + } + + Context '-ErrorAction Break should break when throwing a specific error or object' { + BeforeAll { + $testScript = { + function Test-Break { + [CmdletBinding()] + param() + try { + try { + # Generate a terminating error + Get-Process -TheAnswer 42 + } catch { + throw $_ + } + } catch { + # Swallow the exception here + } + } + Test-Break -ErrorAction Break + } + + $results = @(Test-Debugger -ScriptBlock $testScript) + } + + It 'Should show 2 debugger commands were invoked' { + # ErrorAction break should trigger on the initial terminating error and the throw + # since it throws a "new" error (throwing anything is considered a new terminating + # error) + $results.Count | Should -Be 2 + } + + It 'The first breakpoint should be the statement that generated the terminating error' { + $results[0] | ShouldHaveExtent -Line 8 -FromColumn 29 -ToColumn 54 + } + + It 'The second breakpoint should be the statement that threw $_' { + $results[1] | ShouldHaveExtent -Line 10 -FromColumn 29 -ToColumn 37 + } + } + + Context 'Other message types should break on their corresponding messages when requested' { + BeforeAll { + $testScript = { + function Test-Break { + [CmdletBinding()] + param() + Write-Warning -Message 'This is a warning message' + Write-Verbose -Message 'This is a verbose message' + Write-Debug -Message 'This is a debug message' + Write-Information -MessageData 'This is an information message' + Write-Progress -Activity 'This shows progress' + } + Test-Break -WarningAction Break -InformationAction Break *>$null + $WarningPreference = $VerbosePreference = $DebugPreference = $InformationPreference = $ProgressPreference = [actionpreference]::Break + Test-Break *>$null + } + + $results = @(Test-Debugger -ScriptBlock $testScript) + } + + It 'Should show 7 debugger commands were invoked' { + # When no debugger commands are provided, 'c' is invoked every time a breakpoint is hit + $results.Count | Should -Be 7 + } + + It 'Write-Warning should trigger a breakpoint from -WarningAction Break' { + $results[0] | ShouldHaveExtent -Line 5 -FromColumn 21 -ToColumn 71 + } + + It 'Write-Information should trigger a breakpoint from -InformationAction Break' { + $results[1] | ShouldHaveExtent -Line 8 -FromColumn 21 -ToColumn 84 + } + + It 'Write-Warning should trigger a breakpoint from $WarningPreference = [actionpreference]::Break' { + $results[2] | ShouldHaveExtent -Line 5 -FromColumn 21 -ToColumn 71 + } + + It 'Write-Verbose should trigger a breakpoint from $VerbosePreference = [actionpreference]::Break' { + $results[3] | ShouldHaveExtent -Line 6 -FromColumn 21 -ToColumn 71 + } + + It 'Write-Debug should trigger a breakpoint from $DebugPreference = [actionpreference]::Break' { + $results[4] | ShouldHaveExtent -Line 7 -FromColumn 21 -ToColumn 67 + } + + It 'Write-Information should trigger a breakpoint from $InformationPreference = [actionpreference]::Break' { + $results[5] | ShouldHaveExtent -Line 8 -FromColumn 21 -ToColumn 84 + } + + It 'Write-Progress should trigger a breakpoint from $ProgressPreference = [actionpreference]::Break' { + $results[6] | ShouldHaveExtent -Line 9 -FromColumn 21 -ToColumn 67 + } + } +} diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Wait-Debugger.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Wait-Debugger.Tests.ps1 new file mode 100644 index 00000000000..180c066016f --- /dev/null +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Wait-Debugger.Tests.ps1 @@ -0,0 +1,37 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +Describe 'Tests for Wait-Debugger' -Tags "CI" { + BeforeAll { + Register-DebuggerHandler + } + + AfterAll { + Unregister-DebuggerHandler + } + + Context 'Wait-Debugger should break on the statement containing the Wait-Debugger command' { + BeforeAll { + $testScript = { + function Test-Break { + [CmdletBinding()] + param() + Wait-Debugger + 'The debugger should break on the previous line, not on this line.' + } + Test-Break + } + + $results = @(Test-Debugger -ScriptBlock $testScript) + } + + It 'Should show 1 debugger command was invoked' { + # There is always an implicit 'c' command that keeps the debugger automation moving + $results.Count | Should -Be 1 + } + + It 'The breakpoint should be the statement containing Wait-Debugger' { + $results[0] | ShouldHaveExtent -Line 5 -FromColumn 21 -ToColumn 34 + } + } +} From ac2fe737844dfec35ec73a864927fc79b2d00ff0 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Mon, 5 Aug 2019 17:22:36 -0300 Subject: [PATCH 33/44] add job debugging test --- .../Scripting/ActionPreference.Tests.ps1 | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 index 1b757ddd184..0c3cf84ca50 100644 --- a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 +++ b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 @@ -334,4 +334,22 @@ Describe 'ActionPreference.Break tests' -tag 'CI' { $results[6] | ShouldHaveExtent -Line 9 -FromColumn 21 -ToColumn 67 } } + + Context 'ActionPreference.Break in jobs' { + + BeforeAll { + $job = Start-Job { + $ErrorActionPreference = [actionpreference]::Break + Get-Process -TheAnswer 42 + } + } + + AfterAll { + Remove-Job -Job $job -Force + } + + It 'ActionPreference.Break should break in a running job' { + Wait-UntilTrue -sb { $job.State -eq 'AtBreakpoint' } -TimeoutInMilliseconds (10 * 1000) -IntervalInMilliseconds 100 | Should -BeTrue + } + } } From bd631b5b6cbd658a74cee8641f0d23f5dd50e1c3 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Mon, 5 Aug 2019 18:09:33 -0300 Subject: [PATCH 34/44] minor change to Pester test --- .../Language/Scripting/ActionPreference.Tests.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 index 0c3cf84ca50..45be7696d5e 100644 --- a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 +++ b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 @@ -117,7 +117,7 @@ Describe "Tests for (error, warning, etc) action preference" -Tags "CI" { } } -Describe 'ActionPreference.Break tests' -tag 'CI' { +Describe 'ActionPreference.Break tests' -tag 'CI','KirkTest' { BeforeAll { Register-DebuggerHandler @@ -135,7 +135,7 @@ Describe 'ActionPreference.Break tests' -tag 'CI' { param() try { # Generate a non-terminating error - Get-Process -Id ([int]::MaxValue) + Write-Error 'This is a non-terminating error.' # Do something afterwards 'This should still run' } catch { @@ -156,7 +156,7 @@ Describe 'ActionPreference.Break tests' -tag 'CI' { } It 'The breakpoint should be the statement that generated the non-terminating error' { - $results[0] | ShouldHaveExtent -Line 7 -FromColumn 25 -ToColumn 58 + $results[0] | ShouldHaveExtent -Line 7 -FromColumn 25 -ToColumn 71 } It 'The second statement should be the statement after that which generated the non-terminating error' { From f2bd96ea9db0ae40dc07ee2513769f43ccc8d363 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Mon, 5 Aug 2019 21:11:24 -0300 Subject: [PATCH 35/44] remove temporary Pester tag --- test/powershell/Language/Scripting/ActionPreference.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 index 45be7696d5e..d0edba08aae 100644 --- a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 +++ b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 @@ -117,7 +117,7 @@ Describe "Tests for (error, warning, etc) action preference" -Tags "CI" { } } -Describe 'ActionPreference.Break tests' -tag 'CI','KirkTest' { +Describe 'ActionPreference.Break tests' -tag 'CI' { BeforeAll { Register-DebuggerHandler From 2e0e87b9285e04f7a498440fb70c78b8ca39e2c6 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Tue, 6 Aug 2019 13:57:59 -0300 Subject: [PATCH 36/44] change stepInto to stepOver in tests --- test/powershell/Language/Scripting/ActionPreference.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 index d0edba08aae..4d8bcab76bc 100644 --- a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 +++ b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 @@ -147,7 +147,7 @@ Describe 'ActionPreference.Break tests' -tag 'CI' { Test-Break -ErrorAction Break } - $results = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 's', 's') + $results = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 'v', 'v') } It 'Should show 3 debugger commands were invoked' { @@ -188,7 +188,7 @@ Describe 'ActionPreference.Break tests' -tag 'CI' { Test-Break -ErrorAction Break } - $results = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 's', 's') + $results = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 'v', 'v') } It 'Should show 3 debugger commands were invoked' { From 56c135cd0a5766eacc8c3aabf2579be08f090977 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Tue, 6 Aug 2019 17:22:26 -0300 Subject: [PATCH 37/44] remove $Host.Runspace from Pester test --- .../Api/TaskBasedAsyncPowerShellAPI.Tests.ps1 | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/test/powershell/engine/Api/TaskBasedAsyncPowerShellAPI.Tests.ps1 b/test/powershell/engine/Api/TaskBasedAsyncPowerShellAPI.Tests.ps1 index be08c60fbb2..c22d994a2b9 100644 --- a/test/powershell/engine/Api/TaskBasedAsyncPowerShellAPI.Tests.ps1 +++ b/test/powershell/engine/Api/TaskBasedAsyncPowerShellAPI.Tests.ps1 @@ -101,14 +101,27 @@ try { } It 'cannot invoke a single script asynchronously in a runspace that is busy' { - $ps = [powershell]::Create($Host.Runspace) + $rs = [runspacefactory]::CreateRunspace() try { - # This test is designed to fail. You cannot invoke PowerShell asynchronously - # in a runspace that is busy, because pipelines cannot be run concurrently. - $err = { InvokeAsyncHelper -PowerShell $ps -Wait } | Should -Throw -ErrorId 'AggregateException' -PassThru - GetInnerErrorId -Exception $err.Exception | Should -Be 'InvalidOperation' + $rs.Open(); + $ps1 = [powershell]::Create($rs) + $ps2 = [powershell]::Create($rs) + try { + # Make the runspace busy by running an async command. + $ps1.AddScript('@(1..100).foreach{Start-Sleep -Milliseconds 250}').InvokeAsync() + Wait-UntilTrue { $rs.RunspaceAvailability -eq [System.Management.Automation.Runspaces.RunspaceAvailability]::Busy } | Should -BeTrue + # This test is designed to fail. You cannot invoke PowerShell asynchronously + # in a runspace that is busy, because pipelines cannot be run concurrently. + $err = { InvokeAsyncHelper -PowerShell $ps2 -Wait } | Should -Throw -ErrorId 'AggregateException' -PassThru + GetInnerErrorId -Exception $err.Exception | Should -Be 'InvalidOperation' + } finally { + $ps1.Stop() + $ps1.Dispose() + $ps2.Dispose() + } + } finally { - $ps.Dispose() + $rs.Dispose() } } From df85bd959dd0cccbbaf4139c484dc8e420101d50 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Thu, 8 Aug 2019 20:52:32 -0300 Subject: [PATCH 38/44] replace unused variable with discard --- .../engine/runtime/Operations/MiscOps.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs index ef667b77377..db135416d49 100644 --- a/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs +++ b/src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs @@ -1809,12 +1809,11 @@ internal static ActionPreference GetErrorActionPreference(ExecutionContext conte internal static ActionPreference QueryForAction(RuntimeException rte, string message, ExecutionContext context) { // 906264 "$ErrorActionPreference="Inquire" prevents original non-terminating error from being reported to $error" - bool defaultUsed; ActionPreference preference = context.GetEnumPreference( SpecialVariables.ErrorActionPreferenceVarPath, ActionPreference.Continue, - out defaultUsed); + out _); if (preference != ActionPreference.Inquire || rte.SuppressPromptInInterpreter) return preference; From c35f5b75472adbac859c695482c39104813a0e1c Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Thu, 8 Aug 2019 22:14:10 -0300 Subject: [PATCH 39/44] add #9474 test and update HelpersDebugger module --- .../Scripting/Debugging/Debugging.Tests.ps1 | 52 +++++++++++++++++++ .../HelpersDebugger/HelpersDebugger.psm1 | 15 ++++-- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/test/powershell/Language/Scripting/Debugging/Debugging.Tests.ps1 b/test/powershell/Language/Scripting/Debugging/Debugging.Tests.ps1 index 851e2d5282d..350bbd976d1 100644 --- a/test/powershell/Language/Scripting/Debugging/Debugging.Tests.ps1 +++ b/test/powershell/Language/Scripting/Debugging/Debugging.Tests.ps1 @@ -1,6 +1,58 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +Describe 'Basic debugger tests' -tag 'CI' { + + BeforeAll { + Register-DebuggerHandler + } + + AfterAll { + Unregister-DebuggerHandler + } + + Context 'The value of $? should be preserved when exiting the debugger' { + BeforeAll { + $testScript = { + function Test-DollarQuestionMark { + [CmdletBinding()] + param() + Get-Process -id ([int]::MaxValue) + if (-not $?) { + 'The value of $? was preserved during debugging.' + } else { + 'The value of $? was changed to $true during debugging.' + } + } + $bp = Set-PSBreakpoint -Command Get-Process + $global:DollarQuestionMarkResults = Test-DollarQuestionMark -ErrorAction Break + Remove-PSBreakpoint -Breakpoint $bp + } + + $results = @(Test-Debugger -ScriptBlock $testScript -CommandQueue '$?') + } + + AfterAll { + Remove-Variable -Name DollarQuestionMarkResults -Scope Global -ErrorAction Ignore + } + + It 'Should show 2 debugger commands were invoked' { + # One extra for the implicit 'c' command that keeps the debugger automation moving + $results.Count | Should -Be 2 + } + + It 'Should have $false output from the first $? command' { + $results[0].Output | Should -BeOfType bool + $results[0].Output | Should -Not -BeTrue + } + + It 'Should have string output showing that $? was preserved as $false by the debugger' { + $global:DollarQuestionMarkResults | Should -BeOfType string + $global:DollarQuestionMarkResults | Should -BeExactly 'The value of $? was preserved during debugging.' + } + } +} + Describe "Breakpoints when set should be hit" -tag "CI" { Context "Basic tests" { BeforeAll { diff --git a/test/tools/Modules/HelpersDebugger/HelpersDebugger.psm1 b/test/tools/Modules/HelpersDebugger/HelpersDebugger.psm1 index 79cafc91a92..aaf41b6b103 100644 --- a/test/tools/Modules/HelpersDebugger/HelpersDebugger.psm1 +++ b/test/tools/Modules/HelpersDebugger/HelpersDebugger.psm1 @@ -30,14 +30,19 @@ $debuggerStopHandler = { $stringDbgCommand = $script:dbgCmdQueue.Dequeue() } $dbgCmd = [System.Management.Automation.PSCommand]::new() - $dbgCmd.AddCommand($stringDbgCommand) + $dbgCmd.AddScript($stringDbgCommand) > $null $output = [System.Management.Automation.PSDataCollection[PSObject]]::new() $result = $Host.Runspace.Debugger.ProcessCommand($dbgCmd, $output) + if ($stringDbgCommand -eq '$?' -and $output.Count -eq 1) { + $output[0] = $PSDebugContext.Trigger -isnot [System.Management.Automation.ErrorRecord] + } $script:dbgResults += [pscustomobject]@{ - PSTypeName = 'DebuggerCommandResult' - Command = $stringDbgCommand - Context = $PSDebugContext - Output = $output + PSTypeName = 'DebuggerCommandResult' + Command = $stringDbgCommand + Context = $PSDebugContext + Output = $output + EvaluatedByDebugger = $result.EvaluatedByDebugger + ResumeAction = $result.ResumeAction } } while ($result -eq $null -or $result.ResumeAction -eq $null) $e.ResumeAction = $result.ResumeAction From fc1e8744974d3958323367f93654e792c8c475e9 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Thu, 8 Aug 2019 22:37:18 -0300 Subject: [PATCH 40/44] remove lines that shouldn't have been checked in --- .../Language/Scripting/Debugging/Debugging.Tests.ps1 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/powershell/Language/Scripting/Debugging/Debugging.Tests.ps1 b/test/powershell/Language/Scripting/Debugging/Debugging.Tests.ps1 index 350bbd976d1..2259a587e82 100644 --- a/test/powershell/Language/Scripting/Debugging/Debugging.Tests.ps1 +++ b/test/powershell/Language/Scripting/Debugging/Debugging.Tests.ps1 @@ -24,12 +24,10 @@ Describe 'Basic debugger tests' -tag 'CI' { 'The value of $? was changed to $true during debugging.' } } - $bp = Set-PSBreakpoint -Command Get-Process $global:DollarQuestionMarkResults = Test-DollarQuestionMark -ErrorAction Break - Remove-PSBreakpoint -Breakpoint $bp } - $results = @(Test-Debugger -ScriptBlock $testScript -CommandQueue '$?') + $global:results = @(Test-Debugger -ScriptBlock $testScript -CommandQueue '$?') } AfterAll { From fe935c8b938831b200a5aae6dbf8cb7d1290a979 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Fri, 16 Aug 2019 12:34:57 -0700 Subject: [PATCH 41/44] Add one space for the named parameter uses --- src/System.Management.Automation/engine/debugger/debugger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/debugger/debugger.cs b/src/System.Management.Automation/engine/debugger/debugger.cs index 2910e758f8b..fb6e6a17ef1 100644 --- a/src/System.Management.Automation/engine/debugger/debugger.cs +++ b/src/System.Management.Automation/engine/debugger/debugger.cs @@ -4876,7 +4876,7 @@ internal class DebuggerCommandProcessor public DebuggerCommandProcessor() { _commandTable = new Dictionary(StringComparer.OrdinalIgnoreCase); - _commandTable[StepCommand] = _commandTable[StepShortcut] = new DebuggerCommand(StepCommand, DebuggerResumeAction.StepInto, repeatOnEnter:true, executedByDebugger:false); + _commandTable[StepCommand] = _commandTable[StepShortcut] = new DebuggerCommand(StepCommand, DebuggerResumeAction.StepInto, repeatOnEnter: true, executedByDebugger: false); _commandTable[StepOutCommand] = _commandTable[StepOutShortcut] = new DebuggerCommand(StepOutCommand, DebuggerResumeAction.StepOut, repeatOnEnter: false, executedByDebugger: false); _commandTable[StepOverCommand] = _commandTable[StepOverShortcut] = new DebuggerCommand(StepOverCommand, DebuggerResumeAction.StepOver, repeatOnEnter: true, executedByDebugger: false); _commandTable[ContinueCommand] = _commandTable[ContinueShortcut] = new DebuggerCommand(ContinueCommand, DebuggerResumeAction.Continue, repeatOnEnter: false, executedByDebugger: false); From d1aca869bba8148c87b8dde6cc8bae50d34390ca Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Fri, 16 Aug 2019 18:52:37 -0300 Subject: [PATCH 42/44] changes based on review feedback --- .../engine/debugger/debugger.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/debugger/debugger.cs b/src/System.Management.Automation/engine/debugger/debugger.cs index 2910e758f8b..8c8e1e215f8 100644 --- a/src/System.Management.Automation/engine/debugger/debugger.cs +++ b/src/System.Management.Automation/engine/debugger/debugger.cs @@ -5228,13 +5228,23 @@ public DebuggerCommand(string command, DebuggerResumeAction? action, bool repeat /// public class PSDebugContext { + /// + /// Initializes a new instance of the class. + /// + /// The invocation information for the current command. + /// The breakpoint(s) that caused the script to break in the debugger. + public PSDebugContext(InvocationInfo invocationInfo, List breakpoints) + : this(invocationInfo, breakpoints, triggerObject: null) + { + } + /// /// Initializes a new instance of the class. /// /// The invocation information for the current command. /// The breakpoint(s) that caused the script to break in the debugger. /// The object that caused the script to break in the debugger. - public PSDebugContext(InvocationInfo invocationInfo, List breakpoints, object triggerObject = null) + public PSDebugContext(InvocationInfo invocationInfo, List breakpoints, object triggerObject) { if (breakpoints == null) { From a5ed1911d892a4e1bbc30f2ec2a8e408ad037da6 Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Wed, 21 Aug 2019 22:49:23 -0300 Subject: [PATCH 43/44] removed type accelerator; added enum values --- .../engine/CommandBase.cs | 14 +++++++------- .../engine/parser/TypeResolver.cs | 1 - .../Language/Parser/TypeAccelerator.Tests.ps1 | 8 ++------ 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/System.Management.Automation/engine/CommandBase.cs b/src/System.Management.Automation/engine/CommandBase.cs index 30b5910088a..8848d86f3d9 100644 --- a/src/System.Management.Automation/engine/CommandBase.cs +++ b/src/System.Management.Automation/engine/CommandBase.cs @@ -277,25 +277,25 @@ namespace System.Management.Automation public enum ActionPreference { /// Ignore this event and continue - SilentlyContinue, + SilentlyContinue = 0, /// Stop the command - Stop, + Stop = 1, /// Handle this event as normal and continue - Continue, + Continue = 2, /// Ask whether to stop or continue - Inquire, + Inquire = 3, /// Ignore the event completely (not even logging it to the target stream) - Ignore, + Ignore = 4, /// Suspend the command for further diagnosis. Supported only for workflows. - Suspend, + Suspend = 5, /// Enter the debugger. - Break, + Break = 6, } // enum ActionPreference #endregion ActionPreference diff --git a/src/System.Management.Automation/engine/parser/TypeResolver.cs b/src/System.Management.Automation/engine/parser/TypeResolver.cs index 27dea5ef02a..7205b82b700 100644 --- a/src/System.Management.Automation/engine/parser/TypeResolver.cs +++ b/src/System.Management.Automation/engine/parser/TypeResolver.cs @@ -885,7 +885,6 @@ static TypeAccelerators() builtinTypeAccelerators.Add("psnoteproperty", typeof(PSNoteProperty)); builtinTypeAccelerators.Add("psaliasproperty", typeof(PSAliasProperty)); builtinTypeAccelerators.Add("psvariableproperty", typeof(PSVariableProperty)); - builtinTypeAccelerators.Add("actionpreference", typeof(ActionPreference)); } internal static string FindBuiltinAccelerator(Type type, string expectedKey = null) diff --git a/test/powershell/Language/Parser/TypeAccelerator.Tests.ps1 b/test/powershell/Language/Parser/TypeAccelerator.Tests.ps1 index cbb8cecb7d4..e90e404602e 100644 --- a/test/powershell/Language/Parser/TypeAccelerator.Tests.ps1 +++ b/test/powershell/Language/Parser/TypeAccelerator.Tests.ps1 @@ -402,19 +402,15 @@ Describe "Type accelerators" -Tags "CI" { Accelerator = 'pspropertyexpression' Type = [Microsoft.PowerShell.Commands.PSPropertyExpression] } - @{ - Accelerator = 'actionpreference' - Type = [System.Management.Automation.ActionPreference] - } ) if ( !$IsWindows ) { - $totalAccelerators = 100 + $totalAccelerators = 99 } else { - $totalAccelerators = 105 + $totalAccelerators = 104 $extraFullPSAcceleratorTestCases = @( @{ From 231739f4f1b4d4e9e81d731f02667caf08a7d8ec Mon Sep 17 00:00:00 2001 From: Kirk Munro Date: Wed, 21 Aug 2019 23:42:13 -0300 Subject: [PATCH 44/44] update test to reflect removal of type accelerator --- .../Language/Scripting/ActionPreference.Tests.ps1 | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 index 4d8bcab76bc..3cc2e0337c4 100644 --- a/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 +++ b/test/powershell/Language/Scripting/ActionPreference.Tests.ps1 @@ -294,7 +294,7 @@ Describe 'ActionPreference.Break tests' -tag 'CI' { Write-Progress -Activity 'This shows progress' } Test-Break -WarningAction Break -InformationAction Break *>$null - $WarningPreference = $VerbosePreference = $DebugPreference = $InformationPreference = $ProgressPreference = [actionpreference]::Break + $WarningPreference = $VerbosePreference = $DebugPreference = $InformationPreference = $ProgressPreference = [System.Management.Automation.ActionPreference]::Break Test-Break *>$null } @@ -314,23 +314,23 @@ Describe 'ActionPreference.Break tests' -tag 'CI' { $results[1] | ShouldHaveExtent -Line 8 -FromColumn 21 -ToColumn 84 } - It 'Write-Warning should trigger a breakpoint from $WarningPreference = [actionpreference]::Break' { + It 'Write-Warning should trigger a breakpoint from $WarningPreference = [System.Management.Automation.ActionPreference]::Break' { $results[2] | ShouldHaveExtent -Line 5 -FromColumn 21 -ToColumn 71 } - It 'Write-Verbose should trigger a breakpoint from $VerbosePreference = [actionpreference]::Break' { + It 'Write-Verbose should trigger a breakpoint from $VerbosePreference = [System.Management.Automation.ActionPreference]::Break' { $results[3] | ShouldHaveExtent -Line 6 -FromColumn 21 -ToColumn 71 } - It 'Write-Debug should trigger a breakpoint from $DebugPreference = [actionpreference]::Break' { + It 'Write-Debug should trigger a breakpoint from $DebugPreference = [System.Management.Automation.ActionPreference]::Break' { $results[4] | ShouldHaveExtent -Line 7 -FromColumn 21 -ToColumn 67 } - It 'Write-Information should trigger a breakpoint from $InformationPreference = [actionpreference]::Break' { + It 'Write-Information should trigger a breakpoint from $InformationPreference = [System.Management.Automation.ActionPreference]::Break' { $results[5] | ShouldHaveExtent -Line 8 -FromColumn 21 -ToColumn 84 } - It 'Write-Progress should trigger a breakpoint from $ProgressPreference = [actionpreference]::Break' { + It 'Write-Progress should trigger a breakpoint from $ProgressPreference = [System.Management.Automation.ActionPreference]::Break' { $results[6] | ShouldHaveExtent -Line 9 -FromColumn 21 -ToColumn 67 } } @@ -339,7 +339,7 @@ Describe 'ActionPreference.Break tests' -tag 'CI' { BeforeAll { $job = Start-Job { - $ErrorActionPreference = [actionpreference]::Break + $ErrorActionPreference = [System.Management.Automation.ActionPreference]::Break Get-Process -TheAnswer 42 } }