187 lines
8.3 KiB
C#
187 lines
8.3 KiB
C#
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using Microsoft.CodeAnalysis.CSharp.Symbols;
|
|
using Microsoft.CodeAnalysis.CodeGen;
|
|
using Microsoft.CodeAnalysis.PooledObjects;
|
|
using Roslyn.Utilities;
|
|
|
|
namespace Microsoft.CodeAnalysis.CSharp;
|
|
|
|
internal sealed class AsyncIteratorMethodToStateMachineRewriter : AsyncMethodToStateMachineRewriter
|
|
{
|
|
private readonly AsyncIteratorInfo _asyncIteratorInfo;
|
|
|
|
private LabelSymbol _currentDisposalLabel;
|
|
|
|
private readonly LabelSymbol _exprReturnLabelTrue;
|
|
|
|
private readonly ResumableStateMachineStateAllocator _iteratorStateAllocator;
|
|
|
|
internal AsyncIteratorMethodToStateMachineRewriter(MethodSymbol method, int methodOrdinal, AsyncMethodBuilderMemberCollection asyncMethodBuilderMemberCollection, AsyncIteratorInfo asyncIteratorInfo, SyntheticBoundNodeFactory F, FieldSymbol state, FieldSymbol builder, FieldSymbol? instanceIdField, IReadOnlySet<Symbol> hoistedVariables, IReadOnlyDictionary<Symbol, CapturedSymbolReplacement> nonReusableLocalProxies, SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals, ArrayBuilder<StateMachineStateDebugInfo> stateMachineStateDebugInfoBuilder, VariableSlotAllocator? slotAllocatorOpt, int nextFreeHoistedLocalSlot, BindingDiagnosticBag diagnostics)
|
|
: base(method, methodOrdinal, asyncMethodBuilderMemberCollection, F, state, builder, instanceIdField, hoistedVariables, nonReusableLocalProxies, synthesizedLocalOrdinals, stateMachineStateDebugInfoBuilder, slotAllocatorOpt, nextFreeHoistedLocalSlot, diagnostics)
|
|
{
|
|
_asyncIteratorInfo = asyncIteratorInfo;
|
|
_currentDisposalLabel = _exprReturnLabel;
|
|
_exprReturnLabelTrue = F.GenerateLabel("yieldReturn");
|
|
_iteratorStateAllocator = new ResumableStateMachineStateAllocator(slotAllocatorOpt, (StateMachineState)(-4), increasing: false);
|
|
}
|
|
|
|
protected override BoundStatement? GenerateMissingStateDispatch()
|
|
{
|
|
BoundStatement boundStatement = base.GenerateMissingStateDispatch();
|
|
BoundStatement boundStatement2 = _iteratorStateAllocator.GenerateThrowMissingStateDispatch(F, F.Local(cachedState), CodeAnalysisResources.EncCannotResumeSuspendedIteratorMethod);
|
|
if (boundStatement2 == null)
|
|
{
|
|
return boundStatement;
|
|
}
|
|
if (boundStatement == null)
|
|
{
|
|
return boundStatement2;
|
|
}
|
|
return F.Block(boundStatement, boundStatement2);
|
|
}
|
|
|
|
protected override BoundStatement GenerateSetResultCall()
|
|
{
|
|
ArrayBuilder<BoundStatement> instance = ArrayBuilder<BoundStatement>.GetInstance();
|
|
AddDisposeCombinedTokensIfNeeded(instance);
|
|
instance.AddRange(new BoundStatement[6]
|
|
{
|
|
GenerateClearCurrent(),
|
|
GenerateCompleteOnBuilder(),
|
|
generateSetResultOnPromise(result: false),
|
|
F.Return(),
|
|
F.Label(_exprReturnLabelTrue),
|
|
generateSetResultOnPromise(result: true)
|
|
});
|
|
return F.Block(instance.ToImmutableAndFree());
|
|
BoundExpressionStatement generateSetResultOnPromise(bool result)
|
|
{
|
|
BoundFieldAccess receiver = F.InstanceField(_asyncIteratorInfo.PromiseOfValueOrEndField);
|
|
return F.ExpressionStatement(F.Call(receiver, _asyncIteratorInfo.SetResultMethod, F.Literal(result)));
|
|
}
|
|
}
|
|
|
|
private BoundExpressionStatement GenerateClearCurrent()
|
|
{
|
|
FieldSymbol currentField = _asyncIteratorInfo.CurrentField;
|
|
return F.Assignment(F.InstanceField(currentField), F.Default(currentField.Type));
|
|
}
|
|
|
|
private BoundExpressionStatement GenerateCompleteOnBuilder()
|
|
{
|
|
return F.ExpressionStatement(F.Call(F.Field(F.This(), _asyncMethodBuilderField), _asyncMethodBuilderMemberCollection.SetResult, ImmutableArray<BoundExpression>.Empty));
|
|
}
|
|
|
|
private void AddDisposeCombinedTokensIfNeeded(ArrayBuilder<BoundStatement> builder)
|
|
{
|
|
if ((object)_asyncIteratorInfo.CombinedTokensField != null)
|
|
{
|
|
BoundFieldAccess boundFieldAccess = F.Field(F.This(), _asyncIteratorInfo.CombinedTokensField);
|
|
TypeSymbol type = boundFieldAccess.Type;
|
|
builder.Add(F.If(F.ObjectNotEqual(boundFieldAccess, F.Null(type)), F.Block(F.ExpressionStatement(F.Call(boundFieldAccess, F.WellKnownMethod((WellKnownMember)460))), F.Assignment(boundFieldAccess, F.Null(type)))));
|
|
}
|
|
}
|
|
|
|
protected override BoundStatement GenerateSetExceptionCall(LocalSymbol exceptionLocal)
|
|
{
|
|
ArrayBuilder<BoundStatement> instance = ArrayBuilder<BoundStatement>.GetInstance();
|
|
AddDisposeCombinedTokensIfNeeded(instance);
|
|
instance.Add((BoundStatement)GenerateClearCurrent());
|
|
instance.Add((BoundStatement)GenerateCompleteOnBuilder());
|
|
instance.Add((BoundStatement)F.ExpressionStatement(F.Call(F.InstanceField(_asyncIteratorInfo.PromiseOfValueOrEndField), _asyncIteratorInfo.SetExceptionMethod, F.Local(exceptionLocal))));
|
|
return F.Block(instance.ToImmutableAndFree());
|
|
}
|
|
|
|
private BoundStatement GenerateJumpToCurrentDisposalLabel()
|
|
{
|
|
return F.If(F.InstanceField(_asyncIteratorInfo.DisposeModeField), F.Goto(_currentDisposalLabel));
|
|
}
|
|
|
|
private BoundStatement AppendJumpToCurrentDisposalLabel(BoundStatement node)
|
|
{
|
|
return F.Block(node, GenerateJumpToCurrentDisposalLabel());
|
|
}
|
|
|
|
protected override BoundBinaryOperator ShouldEnterFinallyBlock()
|
|
{
|
|
return F.IntEqual(F.Local(cachedState), F.Literal((StateMachineState)(-1)));
|
|
}
|
|
|
|
protected override BoundStatement VisitBody(BoundStatement body)
|
|
{
|
|
AddState((StateMachineState)(-3), out GeneratedLabelSymbol resumeLabel);
|
|
BoundStatement boundStatement = (BoundStatement)Visit(body);
|
|
return F.Block(F.Label(resumeLabel), GenerateJumpToCurrentDisposalLabel(), GenerateSetBothStates((StateMachineState)(-1)), boundStatement);
|
|
}
|
|
|
|
public override BoundNode VisitYieldReturnStatement(BoundYieldReturnStatement node)
|
|
{
|
|
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
|
|
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
|
|
AddResumableState(_iteratorStateAllocator, node.Syntax, default(AwaitDebugId), out StateMachineState stateNumber, out GeneratedLabelSymbol resumeLabel);
|
|
BoundExpression right = (BoundExpression)Visit(node.Expression);
|
|
ArrayBuilder<BoundStatement> instance = ArrayBuilder<BoundStatement>.GetInstance();
|
|
instance.Add((BoundStatement)F.Assignment(F.InstanceField(_asyncIteratorInfo.CurrentField), right));
|
|
instance.Add((BoundStatement)GenerateSetBothStates(stateNumber));
|
|
instance.Add((BoundStatement)F.Goto(_exprReturnLabelTrue));
|
|
instance.Add((BoundStatement)F.Label(resumeLabel));
|
|
instance.Add(F.HiddenSequencePoint());
|
|
instance.Add((BoundStatement)GenerateSetBothStates((StateMachineState)(-1)));
|
|
instance.Add(GenerateJumpToCurrentDisposalLabel());
|
|
instance.Add(F.HiddenSequencePoint());
|
|
return F.Block(instance.ToImmutableAndFree());
|
|
}
|
|
|
|
public override BoundNode VisitYieldBreakStatement(BoundYieldBreakStatement node)
|
|
{
|
|
return F.Block(SetDisposeMode(value: true), F.Goto(_currentDisposalLabel));
|
|
}
|
|
|
|
private BoundExpressionStatement SetDisposeMode(bool value)
|
|
{
|
|
return F.Assignment(F.InstanceField(_asyncIteratorInfo.DisposeModeField), F.Literal(value));
|
|
}
|
|
|
|
public override BoundNode VisitTryStatement(BoundTryStatement node)
|
|
{
|
|
LabelSymbol currentDisposalLabel = _currentDisposalLabel;
|
|
if (node.FinallyBlockOpt != null)
|
|
{
|
|
GeneratedLabelSymbol label = (GeneratedLabelSymbol)(_currentDisposalLabel = F.GenerateLabel("finallyEntry"));
|
|
node = node.Update(F.Block(node.TryBlock, F.Label(label)), node.CatchBlocks, node.FinallyBlockOpt, node.FinallyLabelOpt, node.PreferFaultHandler);
|
|
}
|
|
else if ((object)node.FinallyLabelOpt != null)
|
|
{
|
|
_currentDisposalLabel = node.FinallyLabelOpt;
|
|
}
|
|
BoundStatement boundStatement = (BoundStatement)base.VisitTryStatement(node);
|
|
_currentDisposalLabel = currentDisposalLabel;
|
|
if (node.FinallyBlockOpt != null && (object)_currentDisposalLabel != null)
|
|
{
|
|
boundStatement = AppendJumpToCurrentDisposalLabel(boundStatement);
|
|
}
|
|
return boundStatement;
|
|
}
|
|
|
|
protected override BoundBlock VisitFinally(BoundBlock finallyBlock)
|
|
{
|
|
LabelSymbol currentDisposalLabel = _currentDisposalLabel;
|
|
_currentDisposalLabel = null;
|
|
BoundBlock result = base.VisitFinally(finallyBlock);
|
|
_currentDisposalLabel = currentDisposalLabel;
|
|
return result;
|
|
}
|
|
|
|
public override BoundNode VisitExtractedFinallyBlock(BoundExtractedFinallyBlock extractedFinally)
|
|
{
|
|
BoundStatement boundStatement = VisitFinally(extractedFinally.FinallyBlock);
|
|
if ((object)_currentDisposalLabel != null)
|
|
{
|
|
boundStatement = AppendJumpToCurrentDisposalLabel(boundStatement);
|
|
}
|
|
return boundStatement;
|
|
}
|
|
}
|