Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions src/System.Management.Automation/engine/parser/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2278,25 +2278,26 @@ private Expression CaptureAstResults(
switch (context)
{
case CaptureAstContext.AssignmentWithResultPreservation:
result = Expression.Call(CachedReflectionInfo.PipelineOps_PipelineResult, resultList);

// PipelineResult might get skipped in some circumstances due to an early return or a FlowControlException thrown out, in which case
// we write to the oldPipe. This can happen in cases like:
// $(1;2;return 3)
finallyExprs.Add(Expression.Call(CachedReflectionInfo.PipelineOps_FlushPipe, oldPipe, resultList));
break;
case CaptureAstContext.AssignmentWithoutResultPreservation:
result = Expression.Call(CachedReflectionInfo.PipelineOps_PipelineResult, resultList);

// Clear the temporary pipe in case of exception, if we are not required to preserve the results
var catchExprs = new List<Expression>
if (context == CaptureAstContext.AssignmentWithoutResultPreservation)
{
Expression.Call(CachedReflectionInfo.PipelineOps_ClearPipe, resultList),
Expression.Rethrow(),
Expression.Constant(null, typeof(object))
};
var catchExprs = new List<Expression>
{
Expression.Call(CachedReflectionInfo.PipelineOps_ClearPipe, resultList),
Expression.Rethrow(),
Expression.Constant(null, typeof(object))
};

catches.Add(Expression.Catch(typeof(RuntimeException), Expression.Block(typeof(object), catchExprs)));
}

catches.Add(Expression.Catch(typeof(RuntimeException), Expression.Block(typeof(object), catchExprs)));
// PipelineResult might get skipped in some circumstances due to an early return or a FlowControlException thrown out,
// in which case we write to the oldPipe. This can happen in cases like:
// $(1;2;return 3)
finallyExprs.Add(Expression.Call(CachedReflectionInfo.PipelineOps_FlushPipe, oldPipe, resultList));
break;
case CaptureAstContext.Condition:
result = DynamicExpression.Dynamic(PSPipelineResultToBoolBinder.Get(), typeof(bool), resultList);
Expand Down
45 changes: 23 additions & 22 deletions test/powershell/Language/Scripting/Scripting.Followup.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -81,28 +81,6 @@ Describe "Scripting.Followup.Tests" -Tags "CI" {
$result | Should -BeOfType 'System.Collections.Specialized.OrderedDictionary'
}

It "Don't preserve result when no need to do so in case of flow-control exception" {
function TestFunc1([switch]$p) {
## No need to preserve and flush the results from the IF statement to the outer
## pipeline, because the results are supposed to be assigned to a variable.
if ($p) {
$null = if ($true) { "one"; return "two" }
} else {
$a = foreach ($a in 1) { "one"; return; }
}
}

function TestFunc2 {
## The results from the sub-expression need to be preserved and flushed to the outer pipeline.
$("1";return "2")
}

TestFunc1 | Should -Be $null
TestFunc1 -p | Should -Be $null

TestFunc2 | Should -Be @("1", "2")
}

It "'[NullString]::Value' should be treated as string type when resolving .NET method" {
$testType = 'NullStringTest' -as [type]
if (-not $testType) {
Expand Down Expand Up @@ -146,4 +124,27 @@ public class NullStringTest {
$result = & $powershell -noprofile -c '[System.Text.Encoding]::GetEncoding("IBM437").WebName'
$result | Should -BeExactly "ibm437"
}

It 'Return statement on the right side of an assignment should write the retrun value to outer pipe' {
function TestFunc1 {
## The return value are not assigned to the variable but should be written to the outer pipe.
$Global:mylhsvar = if ($true) { return "one" }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth adding a few more tests here as using return changes the behavior significantly

}

function TestFunc2 {
## The results from the sub-expression need to be preserved and flushed to the outer pipeline.
$("1";return "2")
}

try {
$Global:mylhsvar = $null
TestFunc1 | Should -BeExactly "one"
TestFunc2 | Should -Be @("1", "2")

$Global:mylhsvar | Should -Be $null
}
finally {
Remove-Variable -Name mylhsvar -Scope Global
}
}
}