From 515a09e0b2c3f886bf05040a8095c482e0b55d04 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Wed, 17 Jan 2024 16:29:38 +1000 Subject: [PATCH 1/3] Fix Get-Error serialization of array values Ensure Get-Error does not hang when attempting to serialize an exception that contains a property whose type is an array of System.Type instances. Also ensures that primitive types like System.Int32, System.Boolean as well as System.String is formatted as a string rather than an object with properties. --- .../PowerShellCore_format_ps1xml.cs | 14 ++- .../Get-Error.Tests.ps1 | 105 ++++++++++++++++++ 2 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 test/powershell/Modules/Microsoft.PowerShell.Core/Get-Error.Tests.ps1 diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 182af55d641..4c20f8c65d2 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -946,10 +946,18 @@ private static IEnumerable ViewsOf_System_Management_Autom $isFirstElement = $true foreach ($value in $prop.Value) { $null = $output.Append($newline) - if (!$isFirstElement) { - $null = $output.Append($newline) + + if ($value -is [Type] -or $value -is [string] -or $value.GetType().IsPrimitive) { + # We want to display the primitive value, also includes strings and + # types values, the latter containing a lot of uneeded properties. + $null = $output.Append(""${prefix}$(' ' * $indent)${value}"") + } + else { + if (!$isFirstElement) { + $null = $output.Append($newline) + } + $null = $output.Append((Show-ErrorRecord $value $newIndent ($depth + 1))) } - $null = $output.Append((Show-ErrorRecord $value $newIndent ($depth + 1))) $isFirstElement = $false } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Core/Get-Error.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Core/Get-Error.Tests.ps1 new file mode 100644 index 00000000000..7afa2d9ab11 --- /dev/null +++ b/test/powershell/Modules/Microsoft.PowerShell.Core/Get-Error.Tests.ps1 @@ -0,0 +1,105 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +Describe "Get-Error" -Tags "CI" { + It "Does not hang when serializing exception with array with type instances" { + $ps = [PowerShell]::Create() + $null = $ps.AddScript(@' + class GetErrorWithTypeArray : Exception { + [type[]]$Values + + GetErrorWithTypeArray ([string]$Message) : base($Message) { + $this.Values = [type[]]@([string], [type]) + } + } + try { throw [GetErrorWithTypeArray]::new("") } catch {} + Get-Error | Out-String +'@) + + $task = $ps.BeginInvoke() + if (-not $task.AsyncWaitHandle.WaitOne(5000)) { + $null = $ps.BeginStop($null, $null) + throw "Timed out waiting for Get-Error to serialize" + } + + $result = $ps.EndInvoke($task) + $result.Count | Should -Be 1 + $result[0] | Should -BeOfType ([string]) + + $formattedError = (@( + $result[0] -split "\r?\n" | ForEach-Object { + $_.TrimEnd() + } + ) -join ([Environment]::NewLine)).Trim() + + $formattedError | Should -Be @' +Exception : + Type : GetErrorWithTypeArray + Values : + string + type + HResult : -2146233088 +CategoryInfo : OperationStopped: (:) [], GetErrorWithTypeArray +InvocationInfo : + ScriptLineNumber : 8 + OffsetInLine : 19 + HistoryId : 1 + Line : try { throw [GetErrorWithTypeArray]::new("") } catch {} + + Statement : throw [GetErrorWithTypeArray]::new("") + PositionMessage : At line:8 char:19 + + try { throw [GetErrorWithTypeArray]::new("") } catch {} + + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CommandOrigin : Internal +ScriptStackTrace : at , : line 8 +'@.Trim() + } + + It "Formats strings and primitive types in an array" { + $ps = [PowerShell]::Create() + $null = $ps.AddScript(@' + class GetErrorPrimitiveArray : Exception { + [object[]]$Values + + GetErrorPrimitiveArray ([string]$Message) : base($Message) { + $this.Values = @(1, "2", 0.5) + } + } + try { throw [GetErrorPrimitiveArray]::new("") } catch {} + Get-Error | Out-String +'@) + + $result = $ps.Invoke() + $result.Count | Should -Be 1 + $result[0] | Should -BeOfType ([string]) + + $formattedError = (@( + $result[0] -split "\r?\n" | ForEach-Object { + $_.TrimEnd() + } + ) -join ([Environment]::NewLine)).Trim() + + $formattedError | Should -Be @' +Exception : + Type : GetErrorPrimitiveArray + Values : + 1 + 2 + 0.5 + HResult : -2146233088 +CategoryInfo : OperationStopped: (:) [], GetErrorPrimitiveArray +InvocationInfo : + ScriptLineNumber : 8 + OffsetInLine : 19 + HistoryId : 1 + Line : try { throw [GetErrorPrimitiveArray]::new("") } catch {} + + Statement : throw [GetErrorPrimitiveArray]::new("") + PositionMessage : At line:8 char:19 + + try { throw [GetErrorPrimitiveArray]::new("") } catch {} + + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CommandOrigin : Internal +ScriptStackTrace : at , : line 8 +'@.Trim() + } +} From 16006bf68fe337ae5bf2ff9964c59adc57bad9ce Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Mon, 22 Jan 2024 10:44:36 -0800 Subject: [PATCH 2/3] fix indentation and render types with square brackets --- .../DefaultFormatters/PowerShellCore_format_ps1xml.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 4c20f8c65d2..53c87f88816 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -946,11 +946,14 @@ private static IEnumerable ViewsOf_System_Management_Autom $isFirstElement = $true foreach ($value in $prop.Value) { $null = $output.Append($newline) + $valueIndent = ' ' * ($newIndent + 2) - if ($value -is [Type] -or $value -is [string] -or $value.GetType().IsPrimitive) { - # We want to display the primitive value, also includes strings and - # types values, the latter containing a lot of uneeded properties. - $null = $output.Append(""${prefix}$(' ' * $indent)${value}"") + if ($value -is [Type]) { + # Just show the typename instead of it as an object + $null = $output.Append(""${prefix}${valueIndent}[${value}]"") + } + elseif ($value -is [string] -or $value.GetType().IsPrimitive) { + $null = $output.Append(""${prefix}${valueIndent}${value}"") } else { if (!$isFirstElement) { From fbd3ed47c1cc5f853240c41f662e0ba532e77d67 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Mon, 22 Jan 2024 14:44:53 -0800 Subject: [PATCH 3/3] make types be in square brackets and fix tests --- .../PowerShellCore_format_ps1xml.cs | 6 ++++- .../Get-Error.Tests.ps1 | 24 ++++++++++--------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs index 53c87f88816..8bc6b1971cf 100644 --- a/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs +++ b/src/System.Management.Automation/FormatAndOutput/DefaultFormatters/PowerShellCore_format_ps1xml.cs @@ -950,7 +950,7 @@ private static IEnumerable ViewsOf_System_Management_Autom if ($value -is [Type]) { # Just show the typename instead of it as an object - $null = $output.Append(""${prefix}${valueIndent}[${value}]"") + $null = $output.Append(""${prefix}${valueIndent}[$($value.ToString())]"") } elseif ($value -is [string] -or $value.GetType().IsPrimitive) { $null = $output.Append(""${prefix}${valueIndent}${value}"") @@ -965,6 +965,10 @@ private static IEnumerable ViewsOf_System_Management_Autom } } } + elseif ($prop.Value -is [Type]) { + # Just show the typename instead of it as an object + $null = $output.Append(""[$($prop.Value.ToString())]"") + } # Anything else, we convert to string. # ToString() can throw so we use LanguagePrimitives.TryConvertTo() to hide a convert error else { diff --git a/test/powershell/Modules/Microsoft.PowerShell.Core/Get-Error.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Core/Get-Error.Tests.ps1 index 7afa2d9ab11..9bec24f302e 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Core/Get-Error.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Core/Get-Error.Tests.ps1 @@ -7,9 +7,11 @@ Describe "Get-Error" -Tags "CI" { $null = $ps.AddScript(@' class GetErrorWithTypeArray : Exception { [type[]]$Values + [type]$Type GetErrorWithTypeArray ([string]$Message) : base($Message) { - $this.Values = [type[]]@([string], [type]) + $this.Values = [type[]]@([string], [int]) + $this.Type = [bool] } } try { throw [GetErrorWithTypeArray]::new("") } catch {} @@ -34,24 +36,24 @@ Describe "Get-Error" -Tags "CI" { $formattedError | Should -Be @' Exception : - Type : GetErrorWithTypeArray Values : - string - type + [System.String] + [System.Int32] + Type : [System.Boolean] HResult : -2146233088 CategoryInfo : OperationStopped: (:) [], GetErrorWithTypeArray InvocationInfo : - ScriptLineNumber : 8 + ScriptLineNumber : 10 OffsetInLine : 19 HistoryId : 1 Line : try { throw [GetErrorWithTypeArray]::new("") } catch {} Statement : throw [GetErrorWithTypeArray]::new("") - PositionMessage : At line:8 char:19 + PositionMessage : At line:10 char:19 + try { throw [GetErrorWithTypeArray]::new("") } catch {} + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CommandOrigin : Internal -ScriptStackTrace : at , : line 8 +ScriptStackTrace : at , : line 10 '@.Trim() } @@ -62,7 +64,7 @@ ScriptStackTrace : at , : line 8 [object[]]$Values GetErrorPrimitiveArray ([string]$Message) : base($Message) { - $this.Values = @(1, "2", 0.5) + $this.Values = @(1, "alpha", 0.5) } } try { throw [GetErrorPrimitiveArray]::new("") } catch {} @@ -83,9 +85,9 @@ ScriptStackTrace : at , : line 8 Exception : Type : GetErrorPrimitiveArray Values : - 1 - 2 - 0.5 + 1 + alpha + 0.5 HResult : -2146233088 CategoryInfo : OperationStopped: (:) [], GetErrorPrimitiveArray InvocationInfo :