635 lines
26 KiB
C#
635 lines
26 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Linq;
|
|
using Microsoft.CodeAnalysis.CSharp.Symbols;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
using Microsoft.CodeAnalysis.PooledObjects;
|
|
using Roslyn.Utilities;
|
|
|
|
namespace Microsoft.CodeAnalysis.CSharp;
|
|
|
|
internal sealed class AsyncExceptionHandlerRewriter : BoundTreeRewriterWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
|
|
{
|
|
private sealed class AwaitInFinallyAnalysis : LabelCollector
|
|
{
|
|
private Dictionary<BoundTryStatement, HashSet<LabelSymbol>> _labelsInInterestingTry;
|
|
|
|
private HashSet<BoundCatchBlock> _awaitContainingCatches;
|
|
|
|
private bool _seenAwait;
|
|
|
|
public AwaitInFinallyAnalysis(BoundStatement body)
|
|
{
|
|
_seenAwait = false;
|
|
Visit(body);
|
|
}
|
|
|
|
public bool FinallyContainsAwaits(BoundTryStatement statement)
|
|
{
|
|
if (_labelsInInterestingTry != null)
|
|
{
|
|
return _labelsInInterestingTry.ContainsKey(statement);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
internal bool CatchContainsAwait(BoundCatchBlock node)
|
|
{
|
|
if (_awaitContainingCatches != null)
|
|
{
|
|
return _awaitContainingCatches.Contains(node);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool ContainsAwaitInHandlers()
|
|
{
|
|
if (_labelsInInterestingTry == null)
|
|
{
|
|
return _awaitContainingCatches != null;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
internal HashSet<LabelSymbol> Labels(BoundTryStatement statement)
|
|
{
|
|
return _labelsInInterestingTry[statement];
|
|
}
|
|
|
|
public override BoundNode VisitTryStatement(BoundTryStatement node)
|
|
{
|
|
HashSet<LabelSymbol> hashSet = currentLabels;
|
|
currentLabels = null;
|
|
Visit(node.TryBlock);
|
|
VisitList(node.CatchBlocks);
|
|
bool seenAwait = _seenAwait;
|
|
_seenAwait = false;
|
|
Visit(node.FinallyBlockOpt);
|
|
if (_seenAwait)
|
|
{
|
|
Dictionary<BoundTryStatement, HashSet<LabelSymbol>> dictionary = _labelsInInterestingTry;
|
|
if (dictionary == null)
|
|
{
|
|
dictionary = (_labelsInInterestingTry = new Dictionary<BoundTryStatement, HashSet<LabelSymbol>>());
|
|
}
|
|
dictionary.Add(node, currentLabels);
|
|
currentLabels = hashSet;
|
|
}
|
|
else if (currentLabels == null)
|
|
{
|
|
currentLabels = hashSet;
|
|
}
|
|
else if (hashSet != null)
|
|
{
|
|
currentLabels.UnionWith(hashSet);
|
|
}
|
|
_seenAwait |= seenAwait;
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode VisitCatchBlock(BoundCatchBlock node)
|
|
{
|
|
bool seenAwait = _seenAwait;
|
|
_seenAwait = false;
|
|
BoundNode? result = base.VisitCatchBlock(node);
|
|
if (_seenAwait)
|
|
{
|
|
HashSet<BoundCatchBlock> awaitContainingCatches = _awaitContainingCatches;
|
|
if (awaitContainingCatches == null)
|
|
{
|
|
awaitContainingCatches = (_awaitContainingCatches = new HashSet<BoundCatchBlock>());
|
|
}
|
|
_awaitContainingCatches.Add(node);
|
|
}
|
|
_seenAwait |= seenAwait;
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node)
|
|
{
|
|
_seenAwait = true;
|
|
return base.VisitAwaitExpression(node);
|
|
}
|
|
|
|
public override BoundNode VisitLambda(BoundLambda node)
|
|
{
|
|
HashSet<LabelSymbol> hashSet = currentLabels;
|
|
bool seenAwait = _seenAwait;
|
|
currentLabels = null;
|
|
_seenAwait = false;
|
|
base.VisitLambda(node);
|
|
currentLabels = hashSet;
|
|
_seenAwait = seenAwait;
|
|
return null;
|
|
}
|
|
|
|
public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement node)
|
|
{
|
|
HashSet<LabelSymbol> hashSet = currentLabels;
|
|
bool seenAwait = _seenAwait;
|
|
currentLabels = null;
|
|
_seenAwait = false;
|
|
base.VisitLocalFunctionStatement(node);
|
|
currentLabels = hashSet;
|
|
_seenAwait = seenAwait;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private sealed class AwaitFinallyFrame
|
|
{
|
|
public readonly AwaitFinallyFrame ParentOpt;
|
|
|
|
public readonly HashSet<LabelSymbol> LabelsOpt;
|
|
|
|
private readonly SyntaxNode _syntaxOpt;
|
|
|
|
public Dictionary<LabelSymbol, LabelSymbol> proxyLabels;
|
|
|
|
public List<LabelSymbol> proxiedLabels;
|
|
|
|
public GeneratedLabelSymbol returnProxyLabel;
|
|
|
|
public SynthesizedLocal returnValue;
|
|
|
|
public AwaitFinallyFrame()
|
|
{
|
|
}
|
|
|
|
public AwaitFinallyFrame(AwaitFinallyFrame parent, HashSet<LabelSymbol> labelsOpt, SyntaxNode syntax)
|
|
{
|
|
ParentOpt = parent;
|
|
LabelsOpt = labelsOpt;
|
|
_syntaxOpt = syntax;
|
|
}
|
|
|
|
public bool IsRoot()
|
|
{
|
|
return ParentOpt == null;
|
|
}
|
|
|
|
public LabelSymbol ProxyLabelIfNeeded(LabelSymbol label)
|
|
{
|
|
if (IsRoot() || (LabelsOpt != null && LabelsOpt.Contains(label)))
|
|
{
|
|
return label;
|
|
}
|
|
Dictionary<LabelSymbol, LabelSymbol> dictionary = proxyLabels;
|
|
List<LabelSymbol> list = proxiedLabels;
|
|
if (dictionary == null)
|
|
{
|
|
dictionary = (proxyLabels = new Dictionary<LabelSymbol, LabelSymbol>());
|
|
list = (proxiedLabels = new List<LabelSymbol>());
|
|
}
|
|
if (!dictionary.TryGetValue(label, out var value))
|
|
{
|
|
value = new GeneratedLabelSymbol("proxy" + label.Name);
|
|
dictionary.Add(label, value);
|
|
list.Add(label);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
public LabelSymbol ProxyReturnIfNeeded(MethodSymbol containingMethod, BoundExpression valueOpt, out SynthesizedLocal returnValue)
|
|
{
|
|
returnValue = null;
|
|
if (IsRoot())
|
|
{
|
|
return null;
|
|
}
|
|
GeneratedLabelSymbol generatedLabelSymbol = returnProxyLabel;
|
|
if (generatedLabelSymbol == null)
|
|
{
|
|
generatedLabelSymbol = (returnProxyLabel = new GeneratedLabelSymbol("returnProxy"));
|
|
}
|
|
if (valueOpt != null)
|
|
{
|
|
returnValue = this.returnValue;
|
|
if (returnValue == null)
|
|
{
|
|
this.returnValue = (returnValue = new SynthesizedLocal(containingMethod, TypeWithAnnotations.Create(valueOpt.Type), (SynthesizedLocalKind)20, _syntaxOpt, isPinned: false, isKnownToReferToTempIfReferenceType: false, (RefKind)0));
|
|
}
|
|
}
|
|
return generatedLabelSymbol;
|
|
}
|
|
}
|
|
|
|
private sealed class AwaitCatchFrame
|
|
{
|
|
public readonly SynthesizedLocal pendingCaughtException;
|
|
|
|
public readonly SynthesizedLocal pendingCatch;
|
|
|
|
public readonly List<BoundBlock> handlers;
|
|
|
|
private readonly AwaitCatchFrame _parentOpt;
|
|
|
|
private readonly Dictionary<LocalSymbol, LocalSymbol> _hoistedLocals;
|
|
|
|
private readonly List<LocalSymbol> _orderedHoistedLocals;
|
|
|
|
public AwaitCatchFrame(SyntheticBoundNodeFactory F, TryStatementSyntax tryStatementSyntax, AwaitCatchFrame parentOpt)
|
|
{
|
|
pendingCaughtException = new SynthesizedLocal(F.CurrentFunction, TypeWithAnnotations.Create(F.SpecialType((SpecialType)1)), (SynthesizedLocalKind)25, (SyntaxNode)(object)tryStatementSyntax, isPinned: false, isKnownToReferToTempIfReferenceType: false, (RefKind)0);
|
|
pendingCatch = new SynthesizedLocal(F.CurrentFunction, TypeWithAnnotations.Create(F.SpecialType((SpecialType)13)), (SynthesizedLocalKind)24, (SyntaxNode)(object)tryStatementSyntax, isPinned: false, isKnownToReferToTempIfReferenceType: false, (RefKind)0);
|
|
handlers = new List<BoundBlock>();
|
|
_parentOpt = parentOpt;
|
|
_hoistedLocals = new Dictionary<LocalSymbol, LocalSymbol>();
|
|
_orderedHoistedLocals = new List<LocalSymbol>();
|
|
}
|
|
|
|
public void HoistLocal(LocalSymbol local, SyntheticBoundNodeFactory F)
|
|
{
|
|
if (!_hoistedLocals.Keys.Any((LocalSymbol l) => l.Name == local.Name && TypeSymbol.Equals(l.Type, local.Type, (TypeCompareKind)0)))
|
|
{
|
|
_hoistedLocals.Add(local, local);
|
|
_orderedHoistedLocals.Add(local);
|
|
}
|
|
else
|
|
{
|
|
LocalSymbol localSymbol = F.SynthesizedLocal(local.Type, pendingCatch.SyntaxOpt, isPinned: false, isKnownToReferToTempIfReferenceType: false, (RefKind)0, (SynthesizedLocalKind)26);
|
|
_hoistedLocals.Add(local, localSymbol);
|
|
_orderedHoistedLocals.Add(localSymbol);
|
|
}
|
|
}
|
|
|
|
public IEnumerable<LocalSymbol> GetHoistedLocals()
|
|
{
|
|
return _orderedHoistedLocals;
|
|
}
|
|
|
|
public bool TryGetHoistedLocal(LocalSymbol originalLocal, out LocalSymbol hoistedLocal)
|
|
{
|
|
if (!_hoistedLocals.TryGetValue(originalLocal, out hoistedLocal))
|
|
{
|
|
return _parentOpt?.TryGetHoistedLocal(originalLocal, out hoistedLocal) ?? false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private readonly SyntheticBoundNodeFactory _F;
|
|
|
|
private readonly AwaitInFinallyAnalysis _analysis;
|
|
|
|
private AwaitCatchFrame _currentAwaitCatchFrame;
|
|
|
|
private AwaitFinallyFrame _currentAwaitFinallyFrame = new AwaitFinallyFrame();
|
|
|
|
private AsyncExceptionHandlerRewriter(MethodSymbol containingMethod, NamedTypeSymbol containingType, SyntheticBoundNodeFactory factory, AwaitInFinallyAnalysis analysis)
|
|
{
|
|
_F = factory;
|
|
_F.CurrentFunction = containingMethod;
|
|
_analysis = analysis;
|
|
}
|
|
|
|
public static BoundStatement Rewrite(MethodSymbol containingSymbol, NamedTypeSymbol containingType, BoundStatement statement, TypeCompilationState compilationState, BindingDiagnosticBag diagnostics)
|
|
{
|
|
AwaitInFinallyAnalysis awaitInFinallyAnalysis = new AwaitInFinallyAnalysis(statement);
|
|
if (!awaitInFinallyAnalysis.ContainsAwaitInHandlers())
|
|
{
|
|
return statement;
|
|
}
|
|
SyntheticBoundNodeFactory factory = new SyntheticBoundNodeFactory(containingSymbol, statement.Syntax, compilationState, diagnostics);
|
|
return (BoundStatement)new AsyncExceptionHandlerRewriter(containingSymbol, containingType, factory, awaitInFinallyAnalysis).Visit(statement);
|
|
}
|
|
|
|
public override BoundNode VisitTryStatement(BoundTryStatement node)
|
|
{
|
|
SyntaxNode syntax = node.Syntax;
|
|
BoundStatement boundStatement;
|
|
BoundBlock boundBlock;
|
|
if (!_analysis.FinallyContainsAwaits(node))
|
|
{
|
|
boundStatement = RewriteFinalizedRegion(node);
|
|
boundBlock = (BoundBlock)Visit(node.FinallyBlockOpt);
|
|
if (boundBlock == null)
|
|
{
|
|
return boundStatement;
|
|
}
|
|
if (boundStatement is BoundTryStatement boundTryStatement)
|
|
{
|
|
return boundTryStatement.Update(boundTryStatement.TryBlock, boundTryStatement.CatchBlocks, boundBlock, boundTryStatement.FinallyLabelOpt, boundTryStatement.PreferFaultHandler);
|
|
}
|
|
return _F.Try((BoundBlock)boundStatement, ImmutableArray<BoundCatchBlock>.Empty, boundBlock);
|
|
}
|
|
AwaitFinallyFrame awaitFinallyFrame = PushFrame(node);
|
|
boundStatement = RewriteFinalizedRegion(node);
|
|
boundBlock = (BoundBlock)VisitBlock(node.FinallyBlockOpt);
|
|
PopFrame();
|
|
NamedTypeSymbol typeSymbol = _F.SpecialType((SpecialType)1);
|
|
SynthesizedLocal synthesizedLocal = new SynthesizedLocal(_F.CurrentFunction, TypeWithAnnotations.Create(typeSymbol), (SynthesizedLocalKind)22, syntax, isPinned: false, isKnownToReferToTempIfReferenceType: false, (RefKind)0);
|
|
GeneratedLabelSymbol generatedLabelSymbol = _F.GenerateLabel("finallyLabel");
|
|
SynthesizedLocal synthesizedLocal2 = new SynthesizedLocal(_F.CurrentFunction, TypeWithAnnotations.Create(_F.SpecialType((SpecialType)13)), (SynthesizedLocalKind)23, syntax, isPinned: false, isKnownToReferToTempIfReferenceType: false, (RefKind)0);
|
|
BoundCatchBlock item = _F.Catch(_F.Local(synthesizedLocal), _F.Block());
|
|
BoundStatement boundStatement2 = _F.Try(_F.Block(boundStatement, _F.HiddenSequencePoint(), _F.Goto(generatedLabelSymbol), PendBranches(awaitFinallyFrame, synthesizedLocal2, generatedLabelSymbol)), ImmutableArray.Create(item), null, generatedLabelSymbol);
|
|
BoundBlock boundBlock2 = _F.Block(_F.HiddenSequencePoint(), _F.Label(generatedLabelSymbol), boundBlock, _F.HiddenSequencePoint(), UnpendException(synthesizedLocal), UnpendBranches(awaitFinallyFrame, synthesizedLocal2));
|
|
BoundStatement boundStatement3 = boundBlock2;
|
|
if (_F.CurrentFunction.IsAsync && _F.CurrentFunction.IsIterator)
|
|
{
|
|
boundStatement3 = _F.ExtractedFinallyBlock(boundBlock2);
|
|
}
|
|
ArrayBuilder<LocalSymbol> instance = ArrayBuilder<LocalSymbol>.GetInstance();
|
|
ArrayBuilder<BoundStatement> instance2 = ArrayBuilder<BoundStatement>.GetInstance();
|
|
instance2.Add(_F.HiddenSequencePoint());
|
|
instance.Add((LocalSymbol)synthesizedLocal);
|
|
instance2.Add((BoundStatement)_F.Assignment(_F.Local(synthesizedLocal), _F.Default(synthesizedLocal.Type)));
|
|
instance.Add((LocalSymbol)synthesizedLocal2);
|
|
instance2.Add((BoundStatement)_F.Assignment(_F.Local(synthesizedLocal2), _F.Default(synthesizedLocal2.Type)));
|
|
LocalSymbol returnValue = awaitFinallyFrame.returnValue;
|
|
if (returnValue != null)
|
|
{
|
|
instance.Add(returnValue);
|
|
}
|
|
instance2.Add(boundStatement2);
|
|
instance2.Add(boundStatement3);
|
|
return _F.Block(instance.ToImmutableAndFree(), instance2.ToImmutableAndFree());
|
|
}
|
|
|
|
private BoundBlock PendBranches(AwaitFinallyFrame frame, LocalSymbol pendingBranchVar, LabelSymbol finallyLabel)
|
|
{
|
|
ArrayBuilder<BoundStatement> instance = ArrayBuilder<BoundStatement>.GetInstance();
|
|
List<LabelSymbol> proxiedLabels = frame.proxiedLabels;
|
|
Dictionary<LabelSymbol, LabelSymbol> proxyLabels = frame.proxyLabels;
|
|
int i = 1;
|
|
if (proxiedLabels != null)
|
|
{
|
|
for (int count = proxiedLabels.Count; i <= count; i++)
|
|
{
|
|
LabelSymbol key = proxiedLabels[i - 1];
|
|
LabelSymbol proxy = proxyLabels[key];
|
|
PendBranch(instance, proxy, i, pendingBranchVar, finallyLabel);
|
|
}
|
|
}
|
|
GeneratedLabelSymbol returnProxyLabel = frame.returnProxyLabel;
|
|
if (returnProxyLabel != null)
|
|
{
|
|
PendBranch(instance, returnProxyLabel, i, pendingBranchVar, finallyLabel);
|
|
}
|
|
return _F.Block(instance.ToImmutableAndFree());
|
|
}
|
|
|
|
private void PendBranch(ArrayBuilder<BoundStatement> bodyStatements, LabelSymbol proxy, int i, LocalSymbol pendingBranchVar, LabelSymbol finallyLabel)
|
|
{
|
|
bodyStatements.Add((BoundStatement)_F.Label(proxy));
|
|
bodyStatements.Add((BoundStatement)_F.Assignment(_F.Local(pendingBranchVar), _F.Literal(i)));
|
|
bodyStatements.Add((BoundStatement)_F.Goto(finallyLabel));
|
|
}
|
|
|
|
private BoundStatement UnpendBranches(AwaitFinallyFrame frame, SynthesizedLocal pendingBranchVar)
|
|
{
|
|
AwaitFinallyFrame parentOpt = frame.ParentOpt;
|
|
List<LabelSymbol> proxiedLabels = frame.proxiedLabels;
|
|
int i = 1;
|
|
ArrayBuilder<SyntheticBoundNodeFactory.SyntheticSwitchSection> instance = ArrayBuilder<SyntheticBoundNodeFactory.SyntheticSwitchSection>.GetInstance();
|
|
if (proxiedLabels != null)
|
|
{
|
|
for (int count = proxiedLabels.Count; i <= count; i++)
|
|
{
|
|
LabelSymbol label = proxiedLabels[i - 1];
|
|
LabelSymbol label2 = parentOpt.ProxyLabelIfNeeded(label);
|
|
SyntheticBoundNodeFactory.SyntheticSwitchSection syntheticSwitchSection = _F.SwitchSection(i, _F.Goto(label2));
|
|
instance.Add(syntheticSwitchSection);
|
|
}
|
|
}
|
|
if (frame.returnProxyLabel != null)
|
|
{
|
|
BoundLocal boundLocal = null;
|
|
if (frame.returnValue != null)
|
|
{
|
|
boundLocal = _F.Local(frame.returnValue);
|
|
}
|
|
SynthesizedLocal returnValue;
|
|
LabelSymbol labelSymbol = parentOpt.ProxyReturnIfNeeded(_F.CurrentFunction, boundLocal, out returnValue);
|
|
BoundStatement boundStatement = ((labelSymbol == null) ? new BoundReturnStatement(_F.Syntax, (RefKind)0, boundLocal, @checked: false) : ((boundLocal != null) ? ((BoundStatement)_F.Block(_F.Assignment(_F.Local(returnValue), boundLocal), _F.Goto(labelSymbol))) : ((BoundStatement)_F.Goto(labelSymbol))));
|
|
SyntheticBoundNodeFactory.SyntheticSwitchSection syntheticSwitchSection2 = _F.SwitchSection(i, boundStatement);
|
|
instance.Add(syntheticSwitchSection2);
|
|
}
|
|
return _F.Switch(_F.Local(pendingBranchVar), instance.ToImmutableAndFree());
|
|
}
|
|
|
|
public override BoundNode VisitGotoStatement(BoundGotoStatement node)
|
|
{
|
|
BoundExpression caseExpressionOpt = (BoundExpression)Visit(node.CaseExpressionOpt);
|
|
BoundLabel labelExpressionOpt = (BoundLabel)Visit(node.LabelExpressionOpt);
|
|
LabelSymbol label = _currentAwaitFinallyFrame.ProxyLabelIfNeeded(node.Label);
|
|
return node.Update(label, caseExpressionOpt, labelExpressionOpt);
|
|
}
|
|
|
|
public override BoundNode VisitConditionalGoto(BoundConditionalGoto node)
|
|
{
|
|
return base.VisitConditionalGoto(node);
|
|
}
|
|
|
|
public override BoundNode VisitReturnStatement(BoundReturnStatement node)
|
|
{
|
|
SynthesizedLocal returnValue;
|
|
LabelSymbol labelSymbol = _currentAwaitFinallyFrame.ProxyReturnIfNeeded(_F.CurrentFunction, node.ExpressionOpt, out returnValue);
|
|
if (labelSymbol == null)
|
|
{
|
|
return base.VisitReturnStatement(node);
|
|
}
|
|
BoundExpression boundExpression = (BoundExpression)Visit(node.ExpressionOpt);
|
|
if (boundExpression != null)
|
|
{
|
|
return _F.Block(_F.Assignment(_F.Local(returnValue), boundExpression), _F.Goto(labelSymbol));
|
|
}
|
|
return _F.Goto(labelSymbol);
|
|
}
|
|
|
|
private BoundStatement UnpendException(LocalSymbol pendingExceptionLocal)
|
|
{
|
|
LocalSymbol localSymbol = _F.SynthesizedLocal(_F.SpecialType((SpecialType)1), null, isPinned: false, isKnownToReferToTempIfReferenceType: false, (RefKind)0, (SynthesizedLocalKind)(-2));
|
|
BoundExpressionStatement boundExpressionStatement = _F.Assignment(_F.Local(localSymbol), _F.Local(pendingExceptionLocal));
|
|
BoundStatement thenClause = Rethrow(localSymbol);
|
|
return _F.Block(ImmutableArray.Create(localSymbol), boundExpressionStatement, _F.If(_F.ObjectNotEqual(_F.Local(localSymbol), _F.Null(localSymbol.Type)), thenClause));
|
|
}
|
|
|
|
private BoundStatement Rethrow(LocalSymbol obj)
|
|
{
|
|
BoundStatement boundStatement = _F.Throw(_F.Local(obj));
|
|
MethodSymbol methodSymbol = _F.WellKnownMethod((WellKnownMember)132, isOptional: true);
|
|
MethodSymbol methodSymbol2 = _F.WellKnownMethod((WellKnownMember)133, isOptional: true);
|
|
if (methodSymbol != null && methodSymbol2 != null)
|
|
{
|
|
LocalSymbol localSymbol = _F.SynthesizedLocal(_F.WellKnownType((WellKnownType)52), null, isPinned: false, isKnownToReferToTempIfReferenceType: false, (RefKind)0, (SynthesizedLocalKind)(-2));
|
|
BoundExpressionStatement boundExpressionStatement = _F.Assignment(_F.Local(localSymbol), _F.As(_F.Local(obj), localSymbol.Type));
|
|
boundStatement = _F.Block(ImmutableArray.Create(localSymbol), boundExpressionStatement, _F.If(_F.ObjectEqual(_F.Local(localSymbol), _F.Null(localSymbol.Type)), boundStatement), _F.ExpressionStatement(_F.Call(_F.StaticCall(methodSymbol.ContainingType, methodSymbol, _F.Local(localSymbol)), methodSymbol2)));
|
|
}
|
|
return boundStatement;
|
|
}
|
|
|
|
private BoundStatement RewriteFinalizedRegion(BoundTryStatement node)
|
|
{
|
|
BoundBlock boundBlock = (BoundBlock)VisitBlock(node.TryBlock);
|
|
if (node.CatchBlocks.IsDefaultOrEmpty)
|
|
{
|
|
return boundBlock;
|
|
}
|
|
AwaitCatchFrame currentAwaitCatchFrame = _currentAwaitCatchFrame;
|
|
_currentAwaitCatchFrame = null;
|
|
ImmutableArray<BoundCatchBlock> catchBlocks = ImmutableArrayExtensions.SelectAsArray<BoundCatchBlock, (AsyncExceptionHandlerRewriter, AwaitCatchFrame), BoundCatchBlock>(node.CatchBlocks, (Func<BoundCatchBlock, (AsyncExceptionHandlerRewriter, AwaitCatchFrame), BoundCatchBlock>)delegate(BoundCatchBlock catchBlock, (AsyncExceptionHandlerRewriter, AwaitCatchFrame origAwaitCatchFrame) arg)
|
|
{
|
|
var (asyncExceptionHandlerRewriter, parentAwaitCatchFrame) = arg;
|
|
return (BoundCatchBlock)asyncExceptionHandlerRewriter.VisitCatchBlock(catchBlock, parentAwaitCatchFrame);
|
|
}, (this, currentAwaitCatchFrame));
|
|
BoundStatement boundStatement = _F.Try(boundBlock, catchBlocks);
|
|
AwaitCatchFrame currentAwaitCatchFrame2 = _currentAwaitCatchFrame;
|
|
if (currentAwaitCatchFrame2 != null)
|
|
{
|
|
GeneratedLabelSymbol label = _F.GenerateLabel("handled");
|
|
List<BoundBlock> handlers = currentAwaitCatchFrame2.handlers;
|
|
ArrayBuilder<SyntheticBoundNodeFactory.SyntheticSwitchSection> instance = ArrayBuilder<SyntheticBoundNodeFactory.SyntheticSwitchSection>.GetInstance(handlers.Count);
|
|
int num = 0;
|
|
for (int count = handlers.Count; num < count; num++)
|
|
{
|
|
instance.Add(_F.SwitchSection(num + 1, _F.Block(handlers[num], _F.Goto(label))));
|
|
}
|
|
boundStatement = _F.Block(ImmutableArray.Create((LocalSymbol)currentAwaitCatchFrame2.pendingCaughtException, (LocalSymbol)currentAwaitCatchFrame2.pendingCatch).AddRange(currentAwaitCatchFrame2.GetHoistedLocals()), _F.HiddenSequencePoint(), _F.Assignment(_F.Local(currentAwaitCatchFrame2.pendingCatch), _F.Default(currentAwaitCatchFrame2.pendingCatch.Type)), boundStatement, _F.HiddenSequencePoint(), _F.Switch(_F.Local(currentAwaitCatchFrame2.pendingCatch), instance.ToImmutableAndFree()), _F.HiddenSequencePoint(), _F.Label(label));
|
|
}
|
|
_currentAwaitCatchFrame = currentAwaitCatchFrame;
|
|
return boundStatement;
|
|
}
|
|
|
|
public override BoundNode VisitCatchBlock(BoundCatchBlock node)
|
|
{
|
|
throw ExceptionUtilities.Unreachable("/_/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncExceptionHandlerRewriter.cs", 511);
|
|
}
|
|
|
|
private BoundNode VisitCatchBlock(BoundCatchBlock node, AwaitCatchFrame parentAwaitCatchFrame)
|
|
{
|
|
if (!_analysis.CatchContainsAwait(node))
|
|
{
|
|
AwaitCatchFrame currentAwaitCatchFrame = _currentAwaitCatchFrame;
|
|
_currentAwaitCatchFrame = null;
|
|
BoundNode? result = base.VisitCatchBlock(node);
|
|
_currentAwaitCatchFrame = currentAwaitCatchFrame;
|
|
return result;
|
|
}
|
|
AwaitCatchFrame awaitCatchFrame = _currentAwaitCatchFrame;
|
|
if (awaitCatchFrame == null)
|
|
{
|
|
TryStatementSyntax tryStatementSyntax = (TryStatementSyntax)(object)node.Syntax.Parent;
|
|
awaitCatchFrame = (_currentAwaitCatchFrame = new AwaitCatchFrame(_F, tryStatementSyntax, parentAwaitCatchFrame));
|
|
}
|
|
TypeSymbol typeSymbol = node.ExceptionTypeOpt ?? _F.SpecialType((SpecialType)1);
|
|
LocalSymbol localSymbol = _F.SynthesizedLocal(typeSymbol, null, isPinned: false, isKnownToReferToTempIfReferenceType: false, (RefKind)0, (SynthesizedLocalKind)(-2));
|
|
BoundExpression expr = _F.AssignmentExpression(_F.Local(awaitCatchFrame.pendingCaughtException), _F.Convert(awaitCatchFrame.pendingCaughtException.Type, _F.Local(localSymbol)));
|
|
BoundExpressionStatement boundExpressionStatement = _F.Assignment(_F.Local(awaitCatchFrame.pendingCatch), _F.Literal(awaitCatchFrame.handlers.Count + 1));
|
|
BoundStatementList exceptionFilterPrologueOpt = node.ExceptionFilterPrologueOpt;
|
|
BoundExpression exceptionFilterOpt = node.ExceptionFilterOpt;
|
|
BoundCatchBlock result2;
|
|
ImmutableArray<LocalSymbol> locals;
|
|
if (exceptionFilterOpt == null)
|
|
{
|
|
result2 = node.Update(ImmutableArray.Create(localSymbol), _F.Local(localSymbol), typeSymbol, exceptionFilterPrologueOpt, null, _F.Block(_F.HiddenSequencePoint(), _F.ExpressionStatement(expr), boundExpressionStatement), node.IsSynthesizedAsyncCatchAll);
|
|
locals = node.Locals;
|
|
}
|
|
else
|
|
{
|
|
locals = ImmutableArray<LocalSymbol>.Empty;
|
|
ImmutableArray<LocalSymbol>.Enumerator enumerator = node.Locals.GetEnumerator();
|
|
while (enumerator.MoveNext())
|
|
{
|
|
LocalSymbol current = enumerator.Current;
|
|
awaitCatchFrame.HoistLocal(current, _F);
|
|
}
|
|
BoundStatementList boundStatementList = (BoundStatementList)Visit(exceptionFilterPrologueOpt);
|
|
ArrayBuilder<BoundStatement> instance = ArrayBuilder<BoundStatement>.GetInstance();
|
|
BoundExpression exceptionSourceOpt = node.ExceptionSourceOpt;
|
|
instance.Add((BoundStatement)_F.ExpressionStatement(expr));
|
|
if (exceptionSourceOpt != null)
|
|
{
|
|
instance.Add((BoundStatement)_F.ExpressionStatement(AssignCatchSource((BoundExpression)Visit(exceptionSourceOpt), awaitCatchFrame)));
|
|
}
|
|
if (boundStatementList != null)
|
|
{
|
|
instance.Add((BoundStatement)boundStatementList);
|
|
}
|
|
BoundStatementList exceptionFilterPrologueOpt2 = _F.StatementList(instance.ToImmutableAndFree());
|
|
BoundExpression exceptionFilterOpt2 = (BoundExpression)Visit(exceptionFilterOpt);
|
|
result2 = node.Update(ImmutableArray.Create(localSymbol), _F.Local(localSymbol), typeSymbol, exceptionFilterPrologueOpt2, exceptionFilterOpt2, _F.Block(_F.HiddenSequencePoint(), boundExpressionStatement), node.IsSynthesizedAsyncCatchAll);
|
|
}
|
|
ArrayBuilder<BoundStatement> instance2 = ArrayBuilder<BoundStatement>.GetInstance();
|
|
instance2.Add(_F.HiddenSequencePoint());
|
|
if (exceptionFilterOpt == null)
|
|
{
|
|
BoundExpression exceptionSourceOpt2 = node.ExceptionSourceOpt;
|
|
if (exceptionSourceOpt2 != null)
|
|
{
|
|
BoundExpression expr2 = AssignCatchSource((BoundExpression)Visit(exceptionSourceOpt2), awaitCatchFrame);
|
|
instance2.Add((BoundStatement)_F.ExpressionStatement(expr2));
|
|
}
|
|
}
|
|
instance2.Add((BoundStatement)Visit(node.Body));
|
|
BoundBlock item = _F.Block(locals, instance2.ToImmutableAndFree());
|
|
awaitCatchFrame.handlers.Add(item);
|
|
return result2;
|
|
}
|
|
|
|
private BoundExpression AssignCatchSource(BoundExpression rewrittenSource, AwaitCatchFrame currentAwaitCatchFrame)
|
|
{
|
|
BoundExpression result = null;
|
|
if (rewrittenSource != null)
|
|
{
|
|
result = _F.AssignmentExpression(rewrittenSource, _F.Convert(rewrittenSource.Type, _F.Local(currentAwaitCatchFrame.pendingCaughtException)));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode VisitLocal(BoundLocal node)
|
|
{
|
|
AwaitCatchFrame currentAwaitCatchFrame = _currentAwaitCatchFrame;
|
|
if (currentAwaitCatchFrame == null || !currentAwaitCatchFrame.TryGetHoistedLocal(node.LocalSymbol, out var hoistedLocal))
|
|
{
|
|
return base.VisitLocal(node);
|
|
}
|
|
return node.Update(hoistedLocal, node.ConstantValueOpt, hoistedLocal.Type);
|
|
}
|
|
|
|
public override BoundNode VisitThrowStatement(BoundThrowStatement node)
|
|
{
|
|
if (node.ExpressionOpt != null || _currentAwaitCatchFrame == null)
|
|
{
|
|
return base.VisitThrowStatement(node);
|
|
}
|
|
return Rethrow(_currentAwaitCatchFrame.pendingCaughtException);
|
|
}
|
|
|
|
public override BoundNode VisitLambda(BoundLambda node)
|
|
{
|
|
MethodSymbol currentFunction = _F.CurrentFunction;
|
|
AwaitFinallyFrame currentAwaitFinallyFrame = _currentAwaitFinallyFrame;
|
|
_F.CurrentFunction = node.Symbol;
|
|
_currentAwaitFinallyFrame = new AwaitFinallyFrame();
|
|
BoundNode? result = base.VisitLambda(node);
|
|
_F.CurrentFunction = currentFunction;
|
|
_currentAwaitFinallyFrame = currentAwaitFinallyFrame;
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement node)
|
|
{
|
|
MethodSymbol currentFunction = _F.CurrentFunction;
|
|
AwaitFinallyFrame currentAwaitFinallyFrame = _currentAwaitFinallyFrame;
|
|
_F.CurrentFunction = node.Symbol;
|
|
_currentAwaitFinallyFrame = new AwaitFinallyFrame();
|
|
BoundNode? result = base.VisitLocalFunctionStatement(node);
|
|
_F.CurrentFunction = currentFunction;
|
|
_currentAwaitFinallyFrame = currentAwaitFinallyFrame;
|
|
return result;
|
|
}
|
|
|
|
private AwaitFinallyFrame PushFrame(BoundTryStatement statement)
|
|
{
|
|
return _currentAwaitFinallyFrame = new AwaitFinallyFrame(_currentAwaitFinallyFrame, _analysis.Labels(statement), statement.Syntax);
|
|
}
|
|
|
|
private void PopFrame()
|
|
{
|
|
AwaitFinallyFrame currentAwaitFinallyFrame = _currentAwaitFinallyFrame;
|
|
_currentAwaitFinallyFrame = currentAwaitFinallyFrame.ParentOpt;
|
|
}
|
|
}
|