未验证 提交 b1961c73 编写于 作者: J Julien Couvreur 提交者: GitHub

Avoid leave.s opcode jumping out of a finally block (#39300)

上级 064817dc
......@@ -22,10 +22,12 @@ internal sealed class AsyncIteratorMethodToStateMachineRewriter : AsyncMethodToS
/// <summary>
/// Initially, this is the method's return value label (<see cref="AsyncMethodToStateMachineRewriter._exprReturnLabel"/>).
/// When we enter a `try` that has a `finally`, we'll use the label directly preceding the `finally`.
/// When we enter a `try` that has an extracted `finally`, we will use the label preceding the extracted `finally`.
/// Inside a `try` or `catch` with a `finally`, we'll use the label directly preceding the `finally`.
/// Inside a `try` or `catch` with an extracted `finally`, we will use the label preceding the extracted `finally`.
/// Inside a `finally`, we will use the label terminating the `finally` (to avoid restrictions with leave opcode).
/// </summary>
private LabelSymbol _enclosingFinallyOrExitLabel;
private LabelSymbol _enclosingFinallyEntryOrFinallyExitOrExitLabel;
private ArrayBuilder<LabelSymbol> _previousDisposalLabels;
/// <summary>
/// We use _exprReturnLabel for normal end of method (ie. no more values) and `yield break;`.
......@@ -58,7 +60,7 @@ internal sealed class AsyncIteratorMethodToStateMachineRewriter : AsyncMethodToS
Debug.Assert(asyncIteratorInfo != null);
_asyncIteratorInfo = asyncIteratorInfo;
_enclosingFinallyOrExitLabel = _exprReturnLabel;
_enclosingFinallyEntryOrFinallyExitOrExitLabel = _exprReturnLabel;
_exprReturnLabelTrue = F.GenerateLabel("yieldReturn");
}
......@@ -134,12 +136,12 @@ protected override BoundStatement GenerateSetExceptionCall(LocalSymbol exception
private BoundStatement GenerateJumpToCurrentFinallyOrExit()
{
Debug.Assert((object)_enclosingFinallyOrExitLabel != null);
Debug.Assert((object)_enclosingFinallyEntryOrFinallyExitOrExitLabel != null);
return F.If(
// if (disposeMode)
F.InstanceField(_asyncIteratorInfo.DisposeModeField),
// goto finallyOrExitLabel;
thenClause: F.Goto(_enclosingFinallyOrExitLabel));
thenClause: F.Goto(_enclosingFinallyEntryOrFinallyExitOrExitLabel));
}
private BoundStatement AppendJumpToCurrentFinallyOrExit(BoundStatement node)
......@@ -257,7 +259,7 @@ public override BoundNode VisitYieldBreakStatement(BoundYieldBreakStatement node
// disposeMode = true;
SetDisposeMode(true),
// goto _enclosingFinallyOrExitLabel;
F.Goto(_enclosingFinallyOrExitLabel));
F.Goto(_enclosingFinallyEntryOrFinallyExitOrExitLabel));
}
private BoundExpressionStatement SetDisposeMode(bool value)
......@@ -280,30 +282,46 @@ private BoundExpressionStatement SetDisposeMode(bool value)
/// </summary>
public override BoundNode VisitTryStatement(BoundTryStatement node)
{
LabelSymbol parentFinallyOrExitLabel = _enclosingFinallyOrExitLabel;
_previousDisposalLabels ??= new ArrayBuilder<LabelSymbol>();
_previousDisposalLabels.Push(_enclosingFinallyEntryOrFinallyExitOrExitLabel);
var finallyExit = F.GenerateLabel("finallyExit");
_previousDisposalLabels.Push(finallyExit);
if (node.FinallyBlockOpt != null)
{
var finallyEntry = F.GenerateLabel("finallyEntry");
_enclosingFinallyOrExitLabel = finallyEntry;
_enclosingFinallyEntryOrFinallyExitOrExitLabel = finallyEntry;
// Add finallyEntry label:
// try
// {
// ...
// finallyEntry:
// Add finallyExit label:
// finally
// {
// ...
// finallyExit:
// }
node = node.Update(
tryBlock: F.Block(node.TryBlock, F.Label(finallyEntry)),
node.CatchBlocks, node.FinallyBlockOpt, node.FinallyLabelOpt, node.PreferFaultHandler);
node.CatchBlocks, F.Block(node.FinallyBlockOpt, F.Label(finallyExit)), node.FinallyLabelOpt, node.PreferFaultHandler);
}
else if ((object)node.FinallyLabelOpt != null)
{
_enclosingFinallyOrExitLabel = node.FinallyLabelOpt;
_enclosingFinallyEntryOrFinallyExitOrExitLabel = node.FinallyLabelOpt;
}
var result = (BoundStatement)base.VisitTryStatement(node);
_enclosingFinallyOrExitLabel = parentFinallyOrExitLabel;
// While inside the try and catch blocks, we'll use the current finallyEntry label for disposal
// As soon as we close the try and catch blocks (ie. possibly enter the finally block), we'll use the finallyExit label for disposal (restored/popped from the stack by CloseTryCatchBlocks)
Debug.Assert(_enclosingFinallyEntryOrFinallyExitOrExitLabel == finallyExit);
// When exiting the try statement, we restore the previous disposal label.
_enclosingFinallyEntryOrFinallyExitOrExitLabel = _previousDisposalLabels.Pop();
if (node.FinallyBlockOpt != null)
{
......@@ -317,6 +335,11 @@ public override BoundNode VisitTryStatement(BoundTryStatement node)
return result;
}
protected override void CloseTryCatchBlocks()
{
_enclosingFinallyEntryOrFinallyExitOrExitLabel = _previousDisposalLabels.Pop();
}
/// <summary>
/// Some `finally` clauses may have already been rewritten and extracted to a plain block (<see cref="AsyncExceptionHandlerRewriter"/>).
/// The extracted block will have been wrapped as a <see cref="BoundExtractedFinallyBlock"/> so that we can process it as a `finally` block here.
......
......@@ -30,6 +30,13 @@ internal abstract class MethodToStateMachineRewriter : MethodToClassRewriter
/// </summary>
protected abstract BoundStatement GenerateReturn(bool finished);
/// <summary>
/// Signal the transition from `try`/`catch` blocks to the `finally` block.
/// </summary>
protected virtual void CloseTryCatchBlocks()
{
}
protected readonly SyntheticBoundNodeFactory F;
/// <summary>
......@@ -841,6 +848,8 @@ public override BoundNode VisitTryStatement(BoundTryStatement node)
_dispatches = oldDispatches;
ImmutableArray<BoundCatchBlock> catchBlocks = this.VisitList(node.CatchBlocks);
CloseTryCatchBlocks();
BoundBlock finallyBlockOpt = node.FinallyBlockOpt == null ? null : F.Block(
F.HiddenSequencePoint(),
F.If(
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册