Fix a casting error when using $PSNativeCommandUsesErrorActionPreference (#15993)

This commit is contained in:
Dongbo Wang 2021-08-25 17:05:54 -07:00 committed by GitHub
parent 15836f289c
commit 403767d7f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 55 additions and 103 deletions

View file

@ -525,9 +525,7 @@ namespace System.Management.Automation
/// </summary>
internal object GetVariableValue(VariablePath path, object defaultValue)
{
CmdletProviderContext context;
SessionStateScope scope;
return EngineSessionState.GetVariableValue(path, out context, out scope) ?? defaultValue;
return EngineSessionState.GetVariableValue(path, out _, out _) ?? defaultValue;
}
/// <summary>
@ -612,19 +610,15 @@ namespace System.Management.Automation
/// <returns></returns>
internal bool GetBooleanPreference(VariablePath preferenceVariablePath, bool defaultPref, out bool defaultUsed)
{
CmdletProviderContext context = null;
SessionStateScope scope = null;
object val = EngineSessionState.GetVariableValue(preferenceVariablePath, out context, out scope);
if (val == null)
object val = EngineSessionState.GetVariableValue(preferenceVariablePath, out _, out _);
if (val is null)
{
defaultUsed = true;
return defaultPref;
}
bool converted = defaultPref;
defaultUsed = !LanguagePrimitives.TryConvertTo<bool>
(val, out converted);
return (defaultUsed) ? defaultPref : converted;
defaultUsed = !LanguagePrimitives.TryConvertTo(val, out bool converted);
return defaultUsed ? defaultPref : converted;
}
#endregion GetSetVariable methods
@ -1568,23 +1562,6 @@ namespace System.Management.Automation
private void InitializeCommon(AutomationEngine engine, PSHost hostInterface)
{
Engine = engine;
#if !CORECLR// System.AppDomain is not in CoreCLR
// Set the assembly resolve handler if it isn't already set...
if (!_assemblyEventHandlerSet)
{
// we only want to set the event handler once for the entire app domain...
lock (lockObject)
{
// Need to check again inside the lock due to possibility of a race condition...
if (!_assemblyEventHandlerSet)
{
AppDomain currentAppDomain = AppDomain.CurrentDomain;
currentAppDomain.AssemblyResolve += new ResolveEventHandler(PowerShellAssemblyResolveHandler);
_assemblyEventHandlerSet = true;
}
}
}
#endif
Events = new PSLocalEventManager(this);
transactionManager = new PSTransactionManager();
_debugger = new ScriptDebugger(this);
@ -1609,35 +1586,6 @@ namespace System.Management.Automation
}
private static readonly object lockObject = new object();
#if !CORECLR // System.AppDomain is not in CoreCLR
private static bool _assemblyEventHandlerSet = false;
/// <summary>
/// AssemblyResolve event handler that will look in the assembly cache to see
/// if the named assembly has been loaded. This is necessary so that assemblies loaded
/// with LoadFrom, which are in a different loaded context than Load, can still be used to
/// resolve types.
/// </summary>
/// <param name="sender">The event sender.</param>
/// <param name="args">The event args.</param>
/// <returns>The resolve assembly or null if not found.</returns>
private static Assembly PowerShellAssemblyResolveHandler(object sender, ResolveEventArgs args)
{
ExecutionContext ecFromTLS = Runspaces.LocalPipeline.GetExecutionContextFromTLS();
if (ecFromTLS != null)
{
if (ecFromTLS.AssemblyCache != null)
{
Assembly assembly;
ecFromTLS.AssemblyCache.TryGetValue(args.Name, out assembly);
return assembly;
}
}
return null;
}
#endif
}
/// <summary>

View file

@ -1308,37 +1308,6 @@ namespace System.Management.Automation.Runspaces
}
}
#region VariableHelper
/// <summary>
/// A helper for adding variables to session state.
/// Experimental features can be handled here.
/// </summary>
/// <param name="variables">The variables to add to session state.</param>
private void AddVariables(IEnumerable<SessionStateVariableEntry> variables)
{
Variables.Add(variables);
// If the PSNativeCommandArgumentPassing feature is enabled, create the variable which controls the behavior
// Since the BuiltInVariables list is static, and this should be done dynamically
// we need to do this here. Also, since the defaults are different based on platform we need a
// bit more logic.
if (ExperimentalFeature.IsEnabled("PSNativeCommandArgumentPassing"))
{
NativeArgumentPassingStyle style = NativeArgumentPassingStyle.Standard;
if (Platform.IsWindows) {
style = NativeArgumentPassingStyle.Windows;
}
Variables.Add(
new SessionStateVariableEntry(
SpecialVariables.NativeArgumentPassing,
style,
RunspaceInit.NativeCommandArgumentPassingDescription,
ScopedItemOptions.None,
new ArgumentTypeConverterAttribute(typeof(NativeArgumentPassingStyle))));
}
}
#endregion
/// <summary>
/// Creates an initial session state from a PSSC configuration file.
/// </summary>
@ -1444,7 +1413,7 @@ namespace System.Management.Automation.Runspaces
}
// Add built-in variables.
iss.AddVariables(BuiltInVariables);
iss.Variables.Add(BuiltInVariables);
// wrap some commands in a proxy function to restrict their parameters
foreach (KeyValuePair<string, CommandMetadata> proxyFunction in CommandMetadata.GetRestrictedCommands(SessionCapabilities.RemoteServer))
@ -1531,7 +1500,7 @@ namespace System.Management.Automation.Runspaces
InitialSessionState ss = new InitialSessionState();
ss.AddVariables(BuiltInVariables);
ss.Variables.Add(BuiltInVariables);
ss.Commands.Add(new SessionStateApplicationEntry("*"));
ss.Commands.Add(new SessionStateScriptEntry("*"));
ss.Commands.Add(BuiltInFunctions);
@ -1598,7 +1567,7 @@ namespace System.Management.Automation.Runspaces
{
InitialSessionState ss = new InitialSessionState();
ss.AddVariables(BuiltInVariables);
ss.Variables.Add(BuiltInVariables);
ss.Commands.Add(new SessionStateApplicationEntry("*"));
ss.Commands.Add(new SessionStateScriptEntry("*"));
ss.Commands.Add(BuiltInFunctions);
@ -1639,7 +1608,7 @@ namespace System.Management.Automation.Runspaces
{
InitialSessionState ss = new InitialSessionState();
ss.AddVariables(this.Variables.Clone());
ss.Variables.Add(this.Variables.Clone());
ss.EnvironmentVariables.Add(this.EnvironmentVariables.Clone());
ss.Commands.Add(this.Commands.Clone());
ss.Assemblies.Add(this.Assemblies.Clone());
@ -4471,7 +4440,6 @@ end {
internal const ActionPreference DefaultInformationPreference = ActionPreference.SilentlyContinue;
internal const ErrorView DefaultErrorView = ErrorView.ConciseView;
internal const bool DefaultPSNativeCommandUseErrorActionPreference = false;
internal const bool DefaultWhatIfPreference = false;
internal const ConfirmImpact DefaultConfirmPreference = ConfirmImpact.High;
@ -4628,12 +4596,23 @@ end {
builtinVariables.Add(
new SessionStateVariableEntry(
SpecialVariables.PSNativeCommandUseErrorActionPreference,
DefaultPSNativeCommandUseErrorActionPreference,
value: false,
RunspaceInit.PSNativeCommandUseErrorActionPreferenceDescription,
ScopedItemOptions.None,
new ArgumentTypeConverterAttribute(typeof(bool))));
}
if (ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandArgumentPassingFeatureName))
{
builtinVariables.Add(
new SessionStateVariableEntry(
SpecialVariables.NativeArgumentPassing,
Platform.IsWindows ? NativeArgumentPassingStyle.Windows : NativeArgumentPassingStyle.Standard,
RunspaceInit.NativeCommandArgumentPassingDescription,
ScopedItemOptions.None,
new ArgumentTypeConverterAttribute(typeof(NativeArgumentPassingStyle))));
}
BuiltInVariables = builtinVariables.ToArray();
}

View file

@ -3250,8 +3250,7 @@ namespace System.Management.Automation
{
if (!IsWhatIfFlagSet && !_isWhatIfPreferenceCached)
{
bool defaultUsed = false;
_whatIfFlag = Context.GetBooleanPreference(SpecialVariables.WhatIfPreferenceVarPath, _whatIfFlag, out defaultUsed);
_whatIfFlag = Context.GetBooleanPreference(SpecialVariables.WhatIfPreferenceVarPath, _whatIfFlag, out _);
_isWhatIfPreferenceCached = true;
}

View file

@ -20,8 +20,6 @@ namespace System.Management.Automation
/// </summary>
internal class NativeCommandParameterBinder : ParameterBinderBase
{
private readonly VariablePath s_nativeArgumentPassingVarPath = new VariablePath(SpecialVariables.NativeArgumentPassing);
#region ctor
/// <summary>
@ -193,13 +191,13 @@ namespace System.Management.Automation
{
get
{
if (ExperimentalFeature.IsEnabled("PSNativeCommandArgumentPassing"))
if (ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandArgumentPassingFeatureName))
{
try
{
// This will default to the new behavior if it is set to anything other than Legacy
var preference = LanguagePrimitives.ConvertTo<NativeArgumentPassingStyle>(
Context.GetVariableValue(s_nativeArgumentPassingVarPath, NativeArgumentPassingStyle.Standard));
Context.GetVariableValue(SpecialVariables.NativeArgumentPassingVarPath, NativeArgumentPassingStyle.Standard));
return preference;
}
catch

View file

@ -865,7 +865,7 @@ namespace System.Management.Automation
this.commandRuntime.PipelineProcessor.ExecutionFailed = true;
if (!ExperimentalFeature.IsEnabled(ExperimentalFeature.PSNativeCommandErrorActionPreferenceFeatureName)
|| !(bool)Command.Context.GetVariableValue(SpecialVariables.PSNativeCommandUseErrorActionPreferenceVarPath, defaultValue: false))
|| !Command.Context.GetBooleanPreference(SpecialVariables.PSNativeCommandUseErrorActionPreferenceVarPath, defaultPref: false, out _))
{
return;
}

View file

@ -204,6 +204,7 @@ namespace System.Management.Automation
internal static readonly VariablePath PSModuleAutoLoadingPreferenceVarPath = new VariablePath("global:" + PSModuleAutoLoading);
#region Platform Variables
internal const string IsLinux = "IsLinux";
internal static readonly VariablePath IsLinuxPath = new VariablePath("IsLinux");
@ -221,6 +222,7 @@ namespace System.Management.Automation
internal static readonly VariablePath IsCoreCLRPath = new VariablePath("IsCoreCLR");
#endregion
#region Preference Variables
internal const string DebugPreference = "DebugPreference";
@ -255,13 +257,13 @@ namespace System.Management.Automation
internal static readonly VariablePath InformationPreferenceVarPath = new VariablePath(InformationPreference);
#endregion Preference Variables
internal const string PSNativeCommandUseErrorActionPreference = nameof(PSNativeCommandUseErrorActionPreference);
internal static readonly VariablePath PSNativeCommandUseErrorActionPreferenceVarPath =
new(PSNativeCommandUseErrorActionPreference);
#endregion Preference Variables
// Native command argument passing style
internal const string NativeArgumentPassing = "PSNativeCommandArgumentPassing";

View file

@ -56,6 +56,19 @@ Describe 'Native command error handling tests' -Tags 'CI' {
$stderr[1].Exception.Message | Should -BeExactly "Program `"$exeName`" ended with non-zero exit code: 1."
}
It "Non-boolean value should not cause type casting error when the native command exited with non-zero code" {
$ErrorActionPreference = 'Continue'
$PSNativeCommandUseErrorActionPreference = 'Yeah'
$PSNativeCommandUseErrorActionPreference | Should -BeExactly 'Yeah'
$stderr = testexe -returncode 1 2>&1
$error[0].FullyQualifiedErrorId | Should -BeExactly 'ProgramExitedWithNonZeroCode'
$error[0].TargetObject | Should -BeExactly $exeName
$stderr[1].Exception.Message | Should -BeExactly "Program `"$exeName`" ended with non-zero exit code: 1."
}
It 'Non-zero exit code generates a non-teminating error for $ErrorActionPreference = ''SilentlyContinue''' {
$ErrorActionPreference = 'SilentlyContinue'
@ -174,5 +187,18 @@ Describe 'Native command error handling tests' -Tags 'CI' {
$LASTEXITCODE | Should -Be 1
$Error.Count | Should -Be 0
}
It "Non-boolean value should not cause type casting error when the native command exited with non-zero code" {
$ErrorActionPreference = 'Continue'
$PSNativeCommandUseErrorActionPreference = 0
$PSNativeCommandUseErrorActionPreference | Should -Be 0
$PSNativeCommandUseErrorActionPreference | Should -BeOfType 'System.Int32'
testexe -returncode 1 > $null
$LASTEXITCODE | Should -Be 1
$Error.Count | Should -Be 0
}
}
}