From c0c17ded030b7a2901a89eb72b6a0a1880c14957 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Mon, 30 Mar 2020 09:46:12 -0700 Subject: [PATCH] Do not wrap return result to `PSObject` when converting ScriptBlock to delegate (#10619) This is a breaking change for the delegate types with the object return type: Before this change, the returned object will always be an PSObject instance. After this change, the returned object is the underlying object, which could still be an PSObject if that's what the script actually returns. --- .../engine/lang/scriptblock.cs | 11 ++-- .../engine/Api/LanguagePrimitive.Tests.ps1 | 50 +++++++++++++++++++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/System.Management.Automation/engine/lang/scriptblock.cs b/src/System.Management.Automation/engine/lang/scriptblock.cs index ccd9ac5d91..539be28fe2 100644 --- a/src/System.Management.Automation/engine/lang/scriptblock.cs +++ b/src/System.Management.Automation/engine/lang/scriptblock.cs @@ -647,16 +647,17 @@ namespace System.Management.Automation /// /// This does normal array reduction in the case of a one-element array. /// - internal static object GetRawResult(List result) + internal static object GetRawResult(List result, bool wrapToPSObject) { switch (result.Count) { case 0: return AutomationNull.Value; case 1: - return LanguagePrimitives.AsPSObjectOrNull(result[0]); + return wrapToPSObject ? LanguagePrimitives.AsPSObjectOrNull(result[0]) : result[0]; default: - return LanguagePrimitives.AsPSObjectOrNull(result.ToArray()); + object resultArray = result.ToArray(); + return wrapToPSObject ? LanguagePrimitives.AsPSObjectOrNull(resultArray) : resultArray; } } @@ -807,7 +808,7 @@ namespace System.Management.Automation outputPipe: outputPipe, invocationInfo: null, args: args); - return GetRawResult(rawResult); + return GetRawResult(rawResult, wrapToPSObject: false); } #endregion @@ -934,7 +935,7 @@ namespace System.Management.Automation outputPipe: outputPipe, invocationInfo: null, args: args); - return GetRawResult(result); + return GetRawResult(result, wrapToPSObject: true); } internal void InvokeWithPipe( diff --git a/test/powershell/engine/Api/LanguagePrimitive.Tests.ps1 b/test/powershell/engine/Api/LanguagePrimitive.Tests.ps1 index 155da067e8..e987d3314c 100644 --- a/test/powershell/engine/Api/LanguagePrimitive.Tests.ps1 +++ b/test/powershell/engine/Api/LanguagePrimitive.Tests.ps1 @@ -102,4 +102,54 @@ Describe "Language Primitive Tests" -Tags "CI" { $val | Should -BeTrue $result | Should -BeExactly $compareResult } + + It "Convert ScriptBlock to delegate type" { + $code = @' + using System; + namespace Test.API + { + public enum TestEnum + { + Music, + Video + } + public class LanguagePrimitivesTest + { + Func _handlerReturnObject; + Func _handlerReturnEnum; + public LanguagePrimitivesTest(Func handlerReturnObject, Func handlerReturnEnum) + { + _handlerReturnObject = handlerReturnObject; + _handlerReturnEnum = handlerReturnEnum; + } + + public bool TestHandlerReturnEnum() + { + var value = _handlerReturnEnum("bar"); + return value == TestEnum.Music; + } + + public bool TestHandlerReturnObject() + { + object value = _handlerReturnObject("bar"); + return value is TestEnum; + } + } + } +'@ + + if (-not ("Test.API.TestEnum" -as [type])) + { + Add-Type -TypeDefinition $code + } + + # The script actually returns a enum value, and the converted delegate should return the boxed enum value. + $handlerReturnObject = [System.Func[string, object]] { param([string]$str) [Test.API.TestEnum]::Music } + # The script actually returns a string, and the converted delegate should return the corresponding enum value. + $handlerReturnEnum = [System.Func[string, Test.API.TestEnum]] { param([string]$str) "Music" } + $test = [Test.API.LanguagePrimitivesTest]::new($handlerReturnObject, $handlerReturnEnum) + + $test.TestHandlerReturnEnum() | Should -BeTrue + $test.TestHandlerReturnObject() | Should -BeTrue + } }