From 2af9618cbce79889ec9bc5db9a85218124a0a08a Mon Sep 17 00:00:00 2001 From: Dave Wyatt Date: Tue, 23 Aug 2016 15:52:47 -0400 Subject: [PATCH] Fixes #2035 --- .../commands/utility/Write-Object.cs | 6 +- .../engine/CmdletParameterBinderController.cs | 32 +++---- .../ParameterBinding.Tests.ps1 | 96 ++++++++++++++++++- 3 files changed, 112 insertions(+), 22 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Write-Object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Write-Object.cs index b0c9fd70d0f..3400fdd9cd9 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Write-Object.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Write-Object.cs @@ -55,10 +55,8 @@ protected override void ProcessRecord() { enumerate = false; } - foreach (PSObject inputObject in _inputObjects) // compensate for ValueFromRemainingArguments - { - WriteObject(inputObject, enumerate); - } + + WriteObject(_inputObjects, enumerate); }//processrecord }//WriteOutputCommand #endregion diff --git a/src/System.Management.Automation/engine/CmdletParameterBinderController.cs b/src/System.Management.Automation/engine/CmdletParameterBinderController.cs index a50b75bcefd..92977652e36 100644 --- a/src/System.Management.Automation/engine/CmdletParameterBinderController.cs +++ b/src/System.Management.Automation/engine/CmdletParameterBinderController.cs @@ -1684,33 +1684,31 @@ private void HandleRemainingArguments() var cpi = CommandParameterInternal.CreateParameterWithArgument( PositionUtilities.EmptyExtent, varargsParameter.Parameter.Name, "-" + varargsParameter.Parameter.Name + ":", argumentExtent, valueFromRemainingArguments, false); + + // To make all of the following work similarly (the first is handled elsewhere, but second and third are + // handled here): + // Set-ClusterOwnerNode -Owners foo,bar + // Set-ClusterOwnerNode foo bar + // Set-ClusterOwnerNode foo,bar + // we unwrap our List, but only if there is a single argument of type object[]. + if (valueFromRemainingArguments.Count == 1 && valueFromRemainingArguments[0] is object[]) + { + cpi.SetArgumentValue(UnboundArguments[0].ArgumentExtent, valueFromRemainingArguments[0]); + } + try { BindParameter(cpi, varargsParameter, ParameterBindingFlags.ShouldCoerceType); } catch (ParameterBindingException pbex) { - // To make all of the following work similarly (the first is handled elsewhere, but second and third are - // handled here): - // Set-ClusterOwnerNode -Owners foo,bar - // Set-ClusterOwnerNode foo bar - // Set-ClusterOwnerNode foo,bar - // we make one additional attempt at converting, but only if there is a single argument of type object[]. - if (valueFromRemainingArguments.Count == 1 && valueFromRemainingArguments[0] is object[]) + if (!DefaultParameterBindingInUse) { - cpi.SetArgumentValue(UnboundArguments[0].ArgumentExtent, valueFromRemainingArguments[0]); - BindParameter(cpi, varargsParameter, ParameterBindingFlags.ShouldCoerceType); + throw; } else { - if (!DefaultParameterBindingInUse) - { - throw; - } - else - { - ThrowElaboratedBindingException(pbex); - } + ThrowElaboratedBindingException(pbex); } } UnboundArguments.Clear(); diff --git a/test/powershell/engine/ParameterBinding/ParameterBinding.Tests.ps1 b/test/powershell/engine/ParameterBinding/ParameterBinding.Tests.ps1 index dcd924f8a69..591b01f2543 100644 --- a/test/powershell/engine/ParameterBinding/ParameterBinding.Tests.ps1 +++ b/test/powershell/engine/ParameterBinding/ParameterBinding.Tests.ps1 @@ -228,7 +228,7 @@ [CmdletBinding()] param ( [array]$Parameter1, - [int[]]$Parameter2 + [int[]]$Parameter2 ) Process { @@ -329,4 +329,98 @@ $result | Should Be $expected } } + + Context "ValueFromRemainingArguments" { + BeforeAll { + function Test-BindingFunction { + param ( + [Parameter(ValueFromRemainingArguments)] + [object[]] $Parameter + ) + + return [pscustomobject] @{ + ArgumentCount = $Parameter.Count + Value = $Parameter + } + } + + # Deliberately not using TestDrive:\ here because Pester will fail to clean it up due to the + # assembly being loaded in our process. + + if ($IsWindows) + { + $tempDir = $env:temp + } + else + { + $tempDir = '/tmp' + } + + $dllPath = Join-Path $tempDir TestBindingCmdlet.dll + + Add-Type -OutputAssembly $dllPath -TypeDefinition ' + using System; + using System.Management.Automation; + + [Cmdlet("Test", "BindingCmdlet")] + public class TestBindingCommand : PSCmdlet + { + [Parameter(Position = 0, ValueFromRemainingArguments = true)] + public string[] Parameter { get; set; } + + protected override void ProcessRecord() + { + PSObject obj = new PSObject(); + + obj.Properties.Add(new PSNoteProperty("ArgumentCount", Parameter.Length)); + obj.Properties.Add(new PSNoteProperty("Value", Parameter)); + + WriteObject(obj); + } + } + ' + + Import-Module $dllPath + } + + AfterAll { + Get-Module TestBindingCmdlet | Remove-Module -Force + } + + It "Binds properly when passing an explicit array to an advanced function" { + $result = Test-BindingFunction 1,2,3 + + $result.ArgumentCount | Should Be 3 + $result.Value[0] | Should Be 1 + $result.Value[1] | Should Be 2 + $result.Value[2] | Should Be 3 + } + + It "Binds properly when passing multiple arguments to an advanced function" { + $result = Test-BindingFunction 1 2 3 + + $result.ArgumentCount | Should Be 3 + $result.Value[0] | Should Be 1 + $result.Value[1] | Should Be 2 + $result.Value[2] | Should Be 3 + } + + It "Binds properly when passing an explicit array to a cmdlet" { + $result = Test-BindingCmdlet 1,2,3 + + $result.ArgumentCount | Should Be 3 + $result.Value[0] | Should Be 1 + $result.Value[1] | Should Be 2 + $result.Value[2] | Should Be 3 + } + + It "Binds properly when passing multiple arguments to a cmdlet" { + $result = Test-BindingCmdlet 1 2 3 + + $result.ArgumentCount | Should Be 3 + $result.Value[0] | Should Be 1 + $result.Value[1] | Should Be 2 + $result.Value[2] | Should Be 3 + } + } }