From ad231a8a7ff775582875b9362c93096a184eb801 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Tue, 2 Jan 2018 09:25:29 -0800 Subject: [PATCH] Make minor fixes in Compiler to properly handle void type expression (#5764) --- .../engine/parser/Compiler.cs | 17 ++++- .../engine/parser/ast.cs | 2 +- .../Language/Scripting/Array.Tests.ps1 | 67 +++++++++++++++++++ .../Scripting/Scripting.Followup.Tests.ps1 | 30 +++++++++ 4 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 test/powershell/Language/Scripting/Scripting.Followup.Tests.ps1 diff --git a/src/System.Management.Automation/engine/parser/Compiler.cs b/src/System.Management.Automation/engine/parser/Compiler.cs index 65b9443672..27db6741cc 100644 --- a/src/System.Management.Automation/engine/parser/Compiler.cs +++ b/src/System.Management.Automation/engine/parser/Compiler.cs @@ -3039,6 +3039,12 @@ namespace System.Management.Automation.Language input = GetRangeEnumerator(firstCommandExpr.Expression) ?? Compile(firstCommandExpr.Expression); } + + if (input.Type == typeof(void)) + { + input = Expression.Block(input, ExpressionCache.AutomationNullConstant); + } + i = 1; commandsInPipe = pipeElements.Count - 1; } @@ -5525,7 +5531,7 @@ namespace System.Management.Automation.Language if (values.Type == typeof(void)) { // A dynamic site can't take void - but a void value is just an empty array. - return Expression.NewArrayInit(typeof(object)); + return Expression.Block(values, Expression.NewArrayInit(typeof(object))); } return DynamicExpression.Dynamic(PSToObjectArrayBinder.Get(), typeof(object[]), values); @@ -5533,8 +5539,13 @@ namespace System.Management.Automation.Language public object VisitArrayLiteral(ArrayLiteralAst arrayLiteralAst) { - return Expression.NewArrayInit(typeof(object), - arrayLiteralAst.Elements.Select(elem => Compile(elem).Cast(typeof(object)))); + List elementValues = new List(arrayLiteralAst.Elements.Count); + foreach (var element in arrayLiteralAst.Elements) + { + var eValue = Compile(element); + elementValues.Add(eValue.Type != typeof(void) ? eValue.Cast(typeof(object)) : Expression.Block(eValue, ExpressionCache.AutomationNullConstant)); + } + return Expression.NewArrayInit(typeof(object), elementValues); } private IEnumerable BuildHashtable(ReadOnlyCollection keyValuePairs, ParameterExpression temp, bool ordered) diff --git a/src/System.Management.Automation/engine/parser/ast.cs b/src/System.Management.Automation/engine/parser/ast.cs index 63dca76ca8..36f660c0fe 100644 --- a/src/System.Management.Automation/engine/parser/ast.cs +++ b/src/System.Management.Automation/engine/parser/ast.cs @@ -9301,7 +9301,7 @@ namespace System.Management.Automation.Language } /// - /// The non-empty collection of asts of the elements of the array, or null if no elements were specified (e.g. @()). + /// The non-empty collection of asts of the elements of the array. /// public ReadOnlyCollection Elements { get; private set; } diff --git a/test/powershell/Language/Scripting/Array.Tests.ps1 b/test/powershell/Language/Scripting/Array.Tests.ps1 index c66ce5fc21..7006cec930 100644 --- a/test/powershell/Language/Scripting/Array.Tests.ps1 +++ b/test/powershell/Language/Scripting/Array.Tests.ps1 @@ -72,4 +72,71 @@ Describe "ArrayExpression Tests" -Tags "CI" { $result.Length | Should Be 1 $result[0] | Should Be $null } + + It "@([void](New-Item)) should create file" { + try { + $testFile = Join-Path $TestDrive (New-Guid) + $result = @([void](New-Item $testFile -ItemType File)) + ## file should be created + $testFile | Should Exist + ## the array should be empty + $result.Count | Should Be 0 + } finally { + Remove-Item $testFile -Force -ErrorAction SilentlyContinue + } + } +} + +Describe "ArrayLiteral Tests" -Tags "CI" { + It "'[void](New-Item),2,3' should return a 3-element array and first element is AutomationNull" { + try { + $testFile = Join-Path $TestDrive (New-Guid) + $result = [void](New-Item $testFile -ItemType File), 2, 3 + ## file should be created + $testFile | Should Exist + ## the array should contain 3 items + $result.Count | Should Be 3 + + ## the first item should be AutomationNull + $result[0] | ForEach-Object { "YES" } | Should Be $null + $result | Measure-Object | ForEach-Object -MemberName Count | Should Be 2 + } finally{ + Remove-Item $testFile -Force -ErrorAction SilentlyContinue + } + } + + It "'[void]1, [void](New-Item), [void]2' should return a 3-AutomationNull-element array" { + try { + $testFile = Join-Path $TestDrive (New-Guid) + $result = [void]1, [void](New-Item $testFile -ItemType File), [void]2 + ## file should be created + $testFile | Should Exist + ## the array should contain 3 items + $result.Count | Should Be 3 + + ## all items should be AutomationNull + $result | ForEach-Object { "YES" } | Should Be $null + } finally { + Remove-Item $testFile -Force -ErrorAction SilentlyContinue + } + } + + It "'[void]`$arraylist1.Add(1), `$arraylist2.Clear()' should return a 2-AutomationNull-element array" { + $arraylist1 = [System.Collections.ArrayList]::new() + $arraylist2 = [System.Collections.ArrayList]::new() + + $arraylist2.Add(2) > $null + $arraylist2.Count | Should Be 1 + + ## first item is a non-void method call + ## second item is a void method call + $result = [void]$arraylist1.Add(1), $arraylist2.Clear() + $result.Count | Should Be 2 + $result | ForEach-Object { "YES" } | Should Be $null + + $arraylist1.Count | Should Be 1 + $arraylist1[0] | Should Be 1 + + $arraylist2.Count | Should Be 0 + } } diff --git a/test/powershell/Language/Scripting/Scripting.Followup.Tests.ps1 b/test/powershell/Language/Scripting/Scripting.Followup.Tests.ps1 new file mode 100644 index 0000000000..0b85062626 --- /dev/null +++ b/test/powershell/Language/Scripting/Scripting.Followup.Tests.ps1 @@ -0,0 +1,30 @@ +Describe "Scripting.Followup.Tests" -Tags "CI" { + It "'[void](New-Item) | ' should work and behave like passing AutomationNull to the pipe" { + try { + $testFile = Join-Path $TestDrive (New-Guid) + [void](New-Item $testFile -ItemType File) | ForEach-Object { "YES" } | Should Be $null + ## file should be created + $testFile | Should Exist + } finally { + Remove-Item $testFile -Force -ErrorAction SilentlyContinue + } + } + + ## cast non-void method call to [void] + It "'[void]`$arraylist.Add(1) | ' should work and behave like passing AutomationNull to the pipe" { + $arraylist = [System.Collections.ArrayList]::new() + [void]$arraylist.Add(1) | ForEach-Object { "YES" } | Should Be $null + ## $arraylist.Add(1) should be executed + $arraylist.Count | Should Be 1 + $arraylist[0] | Should Be 1 + } + + ## void method call + It "'`$arraylist2.Clear() | ' should work and behave like passing AutomationNull to the pipe" { + $arraylist = [System.Collections.ArrayList]::new() + $arraylist.Add(1) > $null + $arraylist.Clear() | ForEach-Object { "YES" } | Should Be $null + ## $arraylist.Clear() should be executed + $arraylist.Count | Should Be 0 + } +}