From 0d8eff6446879d14247fc639f3e4725cfd8d7d40 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Mon, 24 Jul 2017 21:52:30 -0700 Subject: [PATCH] Fix array expression to not return null or throw error (#4296) This change fixes 3 issues: - According to [PowerShell Language Specification Version 3.0](https://www.microsoft.com/en-us/download/details.aspx?id=36389), as quoted: "_The result is the (possibly empty) unconstrained 1-dimensional array_", `@(...)` should only return `object[]` array. - `@([object[]]$null).GetType()` fails with error `"You cannot call a method on a null-valued expression."` - `@([System.Collections.Generic.List[object]]$null)` fails with error `"Object reference not set to an instance of an object."` --- .../engine/parser/Compiler.cs | 18 ++--- .../Language/Scripting/Array.Tests.ps1 | 75 +++++++++++++++++++ 2 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 test/powershell/Language/Scripting/Array.Tests.ps1 diff --git a/src/System.Management.Automation/engine/parser/Compiler.cs b/src/System.Management.Automation/engine/parser/Compiler.cs index 88d2bb468d..a0c5540163 100644 --- a/src/System.Management.Automation/engine/parser/Compiler.cs +++ b/src/System.Management.Automation/engine/parser/Compiler.cs @@ -5477,6 +5477,8 @@ namespace System.Management.Automation.Language public object VisitArrayExpression(ArrayExpressionAst arrayExpressionAst) { Expression values = null; + ExpressionAst pureExprAst = null; + var subExpr = arrayExpressionAst.SubExpression; if (subExpr.Traps == null) { @@ -5485,10 +5487,10 @@ namespace System.Management.Automation.Language var pipelineBase = subExpr.Statements[0] as PipelineBaseAst; if (pipelineBase != null) { - var exprAst = pipelineBase.GetPureExpression(); - if (exprAst != null) + pureExprAst = pipelineBase.GetPureExpression(); + if (pureExprAst != null) { - values = Compile(exprAst); + values = Compile(pureExprAst); } } } @@ -5500,16 +5502,12 @@ namespace System.Management.Automation.Language } values = values ?? CaptureAstResults(subExpr, CaptureAstContext.Enumerable); - if (values.Type.IsArray) + if (pureExprAst is ArrayLiteralAst) { - // If the result is already an array, don't wrap the array. + // If the pure expression is ArrayLiteralAst, just return the result. return values; } - if (values.Type == typeof(List)) - { - return Expression.Call(values, CachedReflectionInfo.ObjectList_ToArray); - } - if (values.Type.GetTypeInfo().IsPrimitive || values.Type == typeof(string)) + if (values.Type.IsPrimitive || values.Type == typeof(string)) { // Slight optimization - no need for a dynamic site. We could special case other // types as well, but it's probably not worth it. diff --git a/test/powershell/Language/Scripting/Array.Tests.ps1 b/test/powershell/Language/Scripting/Array.Tests.ps1 new file mode 100644 index 0000000000..c66ce5fc21 --- /dev/null +++ b/test/powershell/Language/Scripting/Array.Tests.ps1 @@ -0,0 +1,75 @@ +Describe "ArrayExpression Tests" -Tags "CI" { + It "@([object[]](1,2,3)) should return a 3-element array of object[]" { + $result = @([object[]](1,2,3)) + $result.GetType().FullName | Should Be "System.Object[]" + $result.Length | Should Be 3 + } + + It "@([int[]](1,2,3)) should return a 3-element array of object[]" { + $result = @([int[]](1,2,3)) + $result.GetType().FullName | Should Be "System.Object[]" + $result.Length | Should Be 3 + } + + It "@([object[]]`$null) should return a 1-element(`$null) array of object[]" { + $result = @([object[]]$null) + $result.GetType().FullName | Should Be "System.Object[]" + $result.Length | Should Be 1 + $result[0] | Should Be $null + } + + It "@([int[]]`$null) should return a 1-element(`$null) array of object[]" { + $result = @([int[]]$null) + $result.GetType().FullName | Should Be "System.Object[]" + $result.Length | Should Be 1 + $result[0] | Should Be $null + } + + It "@([object[]][System.Management.Automation.Internal.AutomationNull]::Value) should return a 1-element(`$null) array of object[]" { + $result = @([object[]][System.Management.Automation.Internal.AutomationNull]::Value) + $result.GetType().FullName | Should Be "System.Object[]" + $result.Length | Should Be 1 + $result[0] | Should Be $null + } + + It "@([int[]][System.Management.Automation.Internal.AutomationNull]::Value) should return a 1-element(`$null) array of object[]" { + $result = @([int[]][System.Management.Automation.Internal.AutomationNull]::Value) + $result.GetType().FullName | Should Be "System.Object[]" + $result.Length | Should Be 1 + $result[0] | Should Be $null + } + + It "@(`$null) should return a 1-element(`$null) array of object[]" { + $result = @($null) + $result.GetType().FullName | Should Be "System.Object[]" + $result.Length | Should Be 1 + $result[0] | Should Be $null + } + + It "@([System.Management.Automation.Internal.AutomationNull]::Value) should return an empty array of object[]" { + $result = @([System.Management.Automation.Internal.AutomationNull]::Value) + $result.GetType().FullName | Should Be "System.Object[]" + $result.Length | Should Be 0 + } + + It "@([object[]]`$a) should return a new array" { + $a = 1,2,3 + $result = @([object[]]$a) + $result.GetType().FullName | Should Be "System.Object[]" + $result.Length | Should Be 3 + } + + It "@([int[]]`$a) should return a new array" { + $a = 1,2,3 + $result = @([int[]]$a) + $result.GetType().FullName | Should Be "System.Object[]" + $result.Length | Should Be 3 + } + + It "@([System.Collections.Generic.List[object]]`$null) should return a 1-element(`$null) array of object[]" { + $result = @([System.Collections.Generic.List[object]]$null) + $result.GetType().FullName | Should Be "System.Object[]" + $result.Length | Should Be 1 + $result[0] | Should Be $null + } +}