未验证 提交 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 ...@@ -22,10 +22,12 @@ internal sealed class AsyncIteratorMethodToStateMachineRewriter : AsyncMethodToS
/// <summary> /// <summary>
/// Initially, this is the method's return value label (<see cref="AsyncMethodToStateMachineRewriter._exprReturnLabel"/>). /// 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`. /// Inside a `try` or `catch` with 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 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> /// </summary>
private LabelSymbol _enclosingFinallyOrExitLabel; private LabelSymbol _enclosingFinallyEntryOrFinallyExitOrExitLabel;
private ArrayBuilder<LabelSymbol> _previousDisposalLabels;
/// <summary> /// <summary>
/// We use _exprReturnLabel for normal end of method (ie. no more values) and `yield break;`. /// We use _exprReturnLabel for normal end of method (ie. no more values) and `yield break;`.
...@@ -58,7 +60,7 @@ internal sealed class AsyncIteratorMethodToStateMachineRewriter : AsyncMethodToS ...@@ -58,7 +60,7 @@ internal sealed class AsyncIteratorMethodToStateMachineRewriter : AsyncMethodToS
Debug.Assert(asyncIteratorInfo != null); Debug.Assert(asyncIteratorInfo != null);
_asyncIteratorInfo = asyncIteratorInfo; _asyncIteratorInfo = asyncIteratorInfo;
_enclosingFinallyOrExitLabel = _exprReturnLabel; _enclosingFinallyEntryOrFinallyExitOrExitLabel = _exprReturnLabel;
_exprReturnLabelTrue = F.GenerateLabel("yieldReturn"); _exprReturnLabelTrue = F.GenerateLabel("yieldReturn");
} }
...@@ -134,12 +136,12 @@ protected override BoundStatement GenerateSetExceptionCall(LocalSymbol exception ...@@ -134,12 +136,12 @@ protected override BoundStatement GenerateSetExceptionCall(LocalSymbol exception
private BoundStatement GenerateJumpToCurrentFinallyOrExit() private BoundStatement GenerateJumpToCurrentFinallyOrExit()
{ {
Debug.Assert((object)_enclosingFinallyOrExitLabel != null); Debug.Assert((object)_enclosingFinallyEntryOrFinallyExitOrExitLabel != null);
return F.If( return F.If(
// if (disposeMode) // if (disposeMode)
F.InstanceField(_asyncIteratorInfo.DisposeModeField), F.InstanceField(_asyncIteratorInfo.DisposeModeField),
// goto finallyOrExitLabel; // goto finallyOrExitLabel;
thenClause: F.Goto(_enclosingFinallyOrExitLabel)); thenClause: F.Goto(_enclosingFinallyEntryOrFinallyExitOrExitLabel));
} }
private BoundStatement AppendJumpToCurrentFinallyOrExit(BoundStatement node) private BoundStatement AppendJumpToCurrentFinallyOrExit(BoundStatement node)
...@@ -257,7 +259,7 @@ public override BoundNode VisitYieldBreakStatement(BoundYieldBreakStatement node ...@@ -257,7 +259,7 @@ public override BoundNode VisitYieldBreakStatement(BoundYieldBreakStatement node
// disposeMode = true; // disposeMode = true;
SetDisposeMode(true), SetDisposeMode(true),
// goto _enclosingFinallyOrExitLabel; // goto _enclosingFinallyOrExitLabel;
F.Goto(_enclosingFinallyOrExitLabel)); F.Goto(_enclosingFinallyEntryOrFinallyExitOrExitLabel));
} }
private BoundExpressionStatement SetDisposeMode(bool value) private BoundExpressionStatement SetDisposeMode(bool value)
...@@ -280,30 +282,46 @@ private BoundExpressionStatement SetDisposeMode(bool value) ...@@ -280,30 +282,46 @@ private BoundExpressionStatement SetDisposeMode(bool value)
/// </summary> /// </summary>
public override BoundNode VisitTryStatement(BoundTryStatement node) 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) if (node.FinallyBlockOpt != null)
{ {
var finallyEntry = F.GenerateLabel("finallyEntry"); var finallyEntry = F.GenerateLabel("finallyEntry");
_enclosingFinallyOrExitLabel = finallyEntry; _enclosingFinallyEntryOrFinallyExitOrExitLabel = finallyEntry;
// Add finallyEntry label: // Add finallyEntry label:
// try // try
// { // {
// ... // ...
// finallyEntry: // finallyEntry:
// Add finallyExit label:
// finally
// {
// ...
// finallyExit:
// }
node = node.Update( node = node.Update(
tryBlock: F.Block(node.TryBlock, F.Label(finallyEntry)), 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) else if ((object)node.FinallyLabelOpt != null)
{ {
_enclosingFinallyOrExitLabel = node.FinallyLabelOpt; _enclosingFinallyEntryOrFinallyExitOrExitLabel = node.FinallyLabelOpt;
} }
var result = (BoundStatement)base.VisitTryStatement(node); 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) if (node.FinallyBlockOpt != null)
{ {
...@@ -317,6 +335,11 @@ public override BoundNode VisitTryStatement(BoundTryStatement node) ...@@ -317,6 +335,11 @@ public override BoundNode VisitTryStatement(BoundTryStatement node)
return result; return result;
} }
protected override void CloseTryCatchBlocks()
{
_enclosingFinallyEntryOrFinallyExitOrExitLabel = _previousDisposalLabels.Pop();
}
/// <summary> /// <summary>
/// Some `finally` clauses may have already been rewritten and extracted to a plain block (<see cref="AsyncExceptionHandlerRewriter"/>). /// 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. /// 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 ...@@ -30,6 +30,13 @@ internal abstract class MethodToStateMachineRewriter : MethodToClassRewriter
/// </summary> /// </summary>
protected abstract BoundStatement GenerateReturn(bool finished); 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; protected readonly SyntheticBoundNodeFactory F;
/// <summary> /// <summary>
...@@ -841,6 +848,8 @@ public override BoundNode VisitTryStatement(BoundTryStatement node) ...@@ -841,6 +848,8 @@ public override BoundNode VisitTryStatement(BoundTryStatement node)
_dispatches = oldDispatches; _dispatches = oldDispatches;
ImmutableArray<BoundCatchBlock> catchBlocks = this.VisitList(node.CatchBlocks); ImmutableArray<BoundCatchBlock> catchBlocks = this.VisitList(node.CatchBlocks);
CloseTryCatchBlocks();
BoundBlock finallyBlockOpt = node.FinallyBlockOpt == null ? null : F.Block( BoundBlock finallyBlockOpt = node.FinallyBlockOpt == null ? null : F.Block(
F.HiddenSequencePoint(), F.HiddenSequencePoint(),
F.If( F.If(
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册