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: 13 additions & 14 deletions src/System.Management.Automation/engine/parser/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2278,26 +2278,25 @@ 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
if (context == CaptureAstContext.AssignmentWithoutResultPreservation)
var catchExprs = new List<Expression>
{
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)));
}
Expression.Call(CachedReflectionInfo.PipelineOps_ClearPipe, resultList),
Expression.Rethrow(),
Expression.Constant(null, typeof(object))
};

// PipelineResult might get skipped in some circumstances due to 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));
catches.Add(Expression.Catch(typeof(RuntimeException), Expression.Block(typeof(object), catchExprs)));
break;
case CaptureAstContext.Condition:
result = DynamicExpression.Dynamic(PSPipelineResultToBoolBinder.Get(), typeof(bool), resultList);
Expand Down
22 changes: 22 additions & 0 deletions test/powershell/Language/Scripting/Scripting.Followup.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,26 @@ Describe "Scripting.Followup.Tests" -Tags "CI" {
$result = [ordered]@{ key = 1 }
$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")
}
}