Files
2026-08-27 10:56:38 -06:00

350 lines
20 KiB
C#

using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CSharp.Emit.NoPia;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp;
internal class AsyncMethodToStateMachineRewriter : MethodToStateMachineRewriter
{
protected readonly MethodSymbol _method;
protected readonly FieldSymbol _asyncMethodBuilderField;
protected readonly AsyncMethodBuilderMemberCollection _asyncMethodBuilderMemberCollection;
protected readonly LabelSymbol _exprReturnLabel;
private readonly LabelSymbol _exitLabel;
private readonly LocalSymbol? _exprRetValue;
private readonly LoweredDynamicOperationFactory _dynamicFactory;
private readonly Dictionary<TypeSymbol, FieldSymbol> _awaiterFields;
private int _nextAwaiterId;
private readonly Dictionary<BoundValuePlaceholderBase, BoundExpression> _placeholderMap;
protected sealed override string EncMissingStateMessage => CodeAnalysisResources.EncCannotResumeSuspendedAsyncMethod;
protected sealed override StateMachineState FirstIncreasingResumableState => (StateMachineState)0;
internal AsyncMethodToStateMachineRewriter(MethodSymbol method, int methodOrdinal, AsyncMethodBuilderMemberCollection asyncMethodBuilderMemberCollection, 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(F, method, state, instanceIdField, hoistedVariables, nonReusableLocalProxies, synthesizedLocalOrdinals, stateMachineStateDebugInfoBuilder, slotAllocatorOpt, nextFreeHoistedLocalSlot, diagnostics)
{
_method = method;
_asyncMethodBuilderMemberCollection = asyncMethodBuilderMemberCollection;
_asyncMethodBuilderField = builder;
_exprReturnLabel = F.GenerateLabel("exprReturn");
_exitLabel = F.GenerateLabel("exitLabel");
_exprRetValue = (method.IsAsyncEffectivelyReturningGenericTask(F.Compilation) ? F.SynthesizedLocal(asyncMethodBuilderMemberCollection.ResultType, F.Syntax, isPinned: false, isKnownToReferToTempIfReferenceType: false, (RefKind)0, (SynthesizedLocalKind)20) : null);
_dynamicFactory = new LoweredDynamicOperationFactory(F, methodOrdinal);
_awaiterFields = new Dictionary<TypeSymbol, FieldSymbol>(SymbolEqualityComparer.IgnoringDynamicTupleNamesAndNullability);
_nextAwaiterId = ((slotAllocatorOpt != null) ? slotAllocatorOpt.PreviousAwaiterSlotCount : 0);
_placeholderMap = new Dictionary<BoundValuePlaceholderBase, BoundExpression>();
}
private FieldSymbol GetAwaiterField(TypeSymbol awaiterType)
{
if (!_awaiterFields.TryGetValue(awaiterType, out FieldSymbol value))
{
int slotIndex = default(int);
if (slotAllocatorOpt == null || !slotAllocatorOpt.TryGetPreviousAwaiterSlotIndex(((PEModuleBuilder<CSharpCompilation, SourceModuleSymbol, AssemblySymbol, TypeSymbol, NamedTypeSymbol, MethodSymbol, SyntaxNode, EmbeddedTypesManager, ModuleCompilationState>)F.ModuleBuilderOpt).Translate(awaiterType, F.Syntax, ((BindingDiagnosticBag)F.Diagnostics).DiagnosticBag), ((BindingDiagnosticBag)F.Diagnostics).DiagnosticBag, ref slotIndex))
{
slotIndex = _nextAwaiterId++;
}
string name = GeneratedNames.AsyncAwaiterFieldName(slotIndex);
value = F.StateMachineField(awaiterType, name, (SynthesizedLocalKind)256, slotIndex);
_awaiterFields.Add(awaiterType, value);
}
return value;
}
internal void GenerateMoveNext(BoundStatement body, MethodSymbol moveNextMethod)
{
//IL_016f: Unknown result type (might be due to invalid IL or missing references)
//IL_0174: Unknown result type (might be due to invalid IL or missing references)
//IL_0178: Unknown result type (might be due to invalid IL or missing references)
F.CurrentFunction = moveNextMethod;
BoundStatement statement = VisitBody(body);
MethodToStateMachineRewriter.TryUnwrapBoundStateMachineScope(ref statement, out var hoistedLocals);
ArrayBuilder<BoundStatement> instance = ArrayBuilder<BoundStatement>.GetInstance();
instance.Add(F.HiddenSequencePoint());
instance.Add((BoundStatement)F.Assignment(F.Local(cachedState), F.Field(F.This(), stateField)));
instance.Add(CacheThisIfNeeded());
LocalSymbol exceptionLocal = F.SynthesizedLocal(F.WellKnownType((WellKnownType)52), null, isPinned: false, isKnownToReferToTempIfReferenceType: false, (RefKind)0, (SynthesizedLocalKind)(-2));
instance.Add(GenerateTopLevelTry(F.Block(ImmutableArray<LocalSymbol>.Empty, F.HiddenSequencePoint(), Dispatch(isOutermost: true), statement), F.CatchBlocks(GenerateExceptionHandling(exceptionLocal, hoistedLocals))));
instance.Add((BoundStatement)F.Label(_exprReturnLabel));
BoundExpressionStatement boundExpressionStatement = F.Assignment(F.Field(F.This(), stateField), F.Literal((StateMachineState)(-2)));
if (!(body.Syntax is BlockSyntax blockSyntax))
{
instance.Add((BoundStatement)boundExpressionStatement);
}
else
{
SyntheticBoundNodeFactory f = F;
SyntaxToken closeBraceToken = blockSyntax.CloseBraceToken;
instance.Add(f.SequencePointWithSpan(blockSyntax, ((SyntaxToken)(ref closeBraceToken)).Span, boundExpressionStatement));
instance.Add(F.HiddenSequencePoint());
}
instance.Add(GenerateHoistedLocalsCleanup(hoistedLocals));
instance.Add(GenerateSetResultCall());
instance.Add((BoundStatement)F.Label(_exitLabel));
instance.Add((BoundStatement)F.Return());
ImmutableArray<BoundStatement> statements = instance.ToImmutableAndFree();
ArrayBuilder<LocalSymbol> instance2 = ArrayBuilder<LocalSymbol>.GetInstance();
instance2.Add(cachedState);
if ((object)cachedThis != null)
{
instance2.Add(cachedThis);
}
if ((object)_exprRetValue != null)
{
instance2.Add(_exprRetValue);
}
BoundStatement boundStatement = F.SequencePoint(body.Syntax, F.Block(instance2.ToImmutableAndFree(), statements));
if (hoistedLocals.Length > 0)
{
boundStatement = MakeStateMachineScope(hoistedLocals, boundStatement);
}
if (instrumentation != null)
{
boundStatement = F.Block(ImmutableArray.Create(instrumentation.Local), instrumentation.Prologue, F.Try(F.Block(boundStatement), ImmutableArray<BoundCatchBlock>.Empty, F.Block(instrumentation.Epilogue)));
}
F.CloseMethod(boundStatement);
}
protected virtual BoundStatement GenerateTopLevelTry(BoundBlock tryBlock, ImmutableArray<BoundCatchBlock> catchBlocks)
{
return F.Try(tryBlock, catchBlocks);
}
protected virtual BoundStatement GenerateSetResultCall()
{
return F.ExpressionStatement(F.Call(F.Field(F.This(), _asyncMethodBuilderField), _asyncMethodBuilderMemberCollection.SetResult, _method.IsAsyncEffectivelyReturningGenericTask(F.Compilation) ? ImmutableArray.Create((BoundExpression)F.Local(_exprRetValue)) : ImmutableArray<BoundExpression>.Empty));
}
protected BoundCatchBlock GenerateExceptionHandling(LocalSymbol exceptionLocal, ImmutableArray<StateMachineFieldSymbol> hoistedLocals)
{
BoundStatement boundStatement = F.ExpressionStatement(F.AssignmentExpression(F.Field(F.This(), stateField), F.Literal((StateMachineState)(-2))));
BoundStatement boundStatement2 = GenerateSetExceptionCall(exceptionLocal);
return new BoundCatchBlock(F.Syntax, ImmutableArray.Create(exceptionLocal), F.Local(exceptionLocal), exceptionLocal.Type, null, null, F.Block(boundStatement, GenerateHoistedLocalsCleanup(hoistedLocals), boundStatement2, GenerateReturn(finished: false)), isSynthesizedAsyncCatchAll: true);
}
protected BoundStatement GenerateHoistedLocalsCleanup(ImmutableArray<StateMachineFieldSymbol> hoistedLocals)
{
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
ArrayBuilder<BoundStatement> instance = ArrayBuilder<BoundStatement>.GetInstance();
ImmutableArray<StateMachineFieldSymbol>.Enumerator enumerator = hoistedLocals.GetEnumerator();
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = default(CompoundUseSiteInfo<AssemblySymbol>);
while (enumerator.MoveNext())
{
StateMachineFieldSymbol current = enumerator.Current;
useSiteInfo._002Ector((BindingDiagnosticBag<AssemblySymbol>)(object)F.Diagnostics, F.Compilation.Assembly);
bool num = current.Type.IsManagedType(ref useSiteInfo);
((BindingDiagnosticBag<AssemblySymbol>)(object)F.Diagnostics).Add(current.GetFirstLocationOrNone(), useSiteInfo);
if (num)
{
instance.Add((BoundStatement)F.Assignment(F.Field(F.This(), current), F.NullOrDefault(current.Type)));
}
}
return F.Block(instance.ToImmutableAndFree());
}
protected virtual BoundStatement GenerateSetExceptionCall(LocalSymbol exceptionLocal)
{
return F.ExpressionStatement(F.Call(F.Field(F.This(), _asyncMethodBuilderField), _asyncMethodBuilderMemberCollection.SetException, F.Local(exceptionLocal)));
}
protected sealed override BoundStatement GenerateReturn(bool finished)
{
return F.Goto(_exitLabel);
}
protected virtual BoundStatement VisitBody(BoundStatement body)
{
return (BoundStatement)Visit(body);
}
public sealed override BoundNode VisitExpressionStatement(BoundExpressionStatement node)
{
if (node.Expression.Kind == BoundKind.AwaitExpression)
{
return VisitAwaitExpression((BoundAwaitExpression)node.Expression, null);
}
if (node.Expression.Kind == BoundKind.AssignmentOperator)
{
BoundAssignmentOperator boundAssignmentOperator = (BoundAssignmentOperator)node.Expression;
if (boundAssignmentOperator.Right.Kind == BoundKind.AwaitExpression)
{
return VisitAwaitExpression((BoundAwaitExpression)boundAssignmentOperator.Right, boundAssignmentOperator.Left);
}
}
BoundExpression boundExpression = (BoundExpression)Visit(node.Expression);
if (boundExpression == null)
{
return F.StatementList();
}
return node.Update(boundExpression);
}
public sealed override BoundNode VisitAwaitExpression(BoundAwaitExpression node)
{
throw ExceptionUtilities.Unreachable("/_/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AsyncMethodToStateMachineRewriter.cs", 333);
}
public sealed override BoundNode VisitBadExpression(BoundBadExpression node)
{
return node;
}
private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpression resultPlace)
{
BoundExpression boundExpression = (BoundExpression)Visit(node.Expression);
BoundAwaitableValuePlaceholder awaitableInstancePlaceholder = node.AwaitableInfo.AwaitableInstancePlaceholder;
if (awaitableInstancePlaceholder != null)
{
_placeholderMap.Add(awaitableInstancePlaceholder, boundExpression);
}
BoundExpression boundExpression2 = (node.AwaitableInfo.IsDynamic ? MakeCallMaybeDynamic(boundExpression, null, "GetAwaiter") : ((BoundExpression)Visit(node.AwaitableInfo.GetAwaiter)));
resultPlace = (BoundExpression)Visit(resultPlace);
MethodSymbol methodSymbol = VisitMethodSymbol(node.AwaitableInfo.GetResult);
MethodSymbol getIsCompletedMethod = (((object)node.AwaitableInfo.IsCompleted != null) ? VisitMethodSymbol(node.AwaitableInfo.IsCompleted.GetMethod) : null);
TypeSymbol type = VisitType(node.Type);
if (awaitableInstancePlaceholder != null)
{
_placeholderMap.Remove(awaitableInstancePlaceholder);
}
LocalSymbol localSymbol = F.SynthesizedLocal(boundExpression2.Type, node.Syntax, isPinned: false, isKnownToReferToTempIfReferenceType: false, (RefKind)0, (SynthesizedLocalKind)33);
BoundBlock boundBlock = F.Block(F.Assignment(F.Local(localSymbol), boundExpression2), F.HiddenSequencePoint(), F.If(F.Not(GenerateGetIsCompleted(localSymbol, getIsCompletedMethod)), GenerateAwaitForIncompleteTask(localSymbol, node.DebugInfo)));
BoundExpression boundExpression3 = MakeCallMaybeDynamic(F.Local(localSymbol), methodSymbol, "GetResult", resultPlace == null);
BoundStatement boundStatement = ((resultPlace != null && !type.IsVoidType()) ? F.Assignment(resultPlace, boundExpression3) : F.ExpressionStatement(boundExpression3));
return F.Block(ImmutableArray.Create(localSymbol), boundBlock, boundStatement);
}
public override BoundNode VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node)
{
return _placeholderMap[node];
}
private BoundExpression MakeCallMaybeDynamic(BoundExpression receiver, MethodSymbol methodSymbol = null, string methodName = null, bool resultsDiscarded = false)
{
if ((object)methodSymbol != null)
{
if (!methodSymbol.IsStatic)
{
return F.Call(receiver, methodSymbol);
}
return F.StaticCall(methodSymbol.ContainingType, methodSymbol, receiver);
}
return _dynamicFactory.MakeDynamicMemberInvocation(methodName, receiver, ImmutableArray<TypeWithAnnotations>.Empty, ImmutableArray<BoundExpression>.Empty, ImmutableArray<string>.Empty, ImmutableArray<RefKind>.Empty, hasImplicitReceiver: false, resultsDiscarded).ToExpression();
}
private BoundExpression GenerateGetIsCompleted(LocalSymbol awaiterTemp, MethodSymbol getIsCompletedMethod)
{
if (awaiterTemp.Type.IsDynamic())
{
return _dynamicFactory.MakeDynamicConversion(_dynamicFactory.MakeDynamicGetMember(F.Local(awaiterTemp), "IsCompleted", resultIndexed: false).ToExpression(), isExplicit: true, isArrayIndex: false, isChecked: false, F.SpecialType((SpecialType)7)).ToExpression();
}
return F.Call(F.Local(awaiterTemp), getIsCompletedMethod);
}
private BoundBlock GenerateAwaitForIncompleteTask(LocalSymbol awaiterTemp, BoundAwaitExpressionDebugInfo debugInfo)
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
//IL_012a: Unknown result type (might be due to invalid IL or missing references)
//IL_012f: Unknown result type (might be due to invalid IL or missing references)
//IL_013e: Unknown result type (might be due to invalid IL or missing references)
SyntaxNode declaratorSyntax = awaiterTemp.GetDeclaratorSyntax();
AddResumableState(declaratorSyntax, debugInfo.AwaitId, out StateMachineState state, out GeneratedLabelSymbol resumeLabel);
TypeSymbol typeSymbol = (awaiterTemp.Type.IsVerifierReference() ? F.SpecialType((SpecialType)1) : awaiterTemp.Type);
FieldSymbol awaiterField = GetAwaiterField(typeSymbol);
ArrayBuilder<BoundStatement> instance = ArrayBuilder<BoundStatement>.GetInstance();
instance.Add((BoundStatement)GenerateSetBothStates(state));
instance.Add(F.NoOp(NoOpStatementFlavor.AwaitYieldPoint));
instance.Add((BoundStatement)F.Assignment(F.Field(F.This(), awaiterField), TypeSymbol.Equals(awaiterField.Type, awaiterTemp.Type, (TypeCompareKind)0) ? F.Local(awaiterTemp) : F.Convert(typeSymbol, F.Local(awaiterTemp))));
instance.Add(awaiterTemp.Type.IsDynamic() ? GenerateAwaitOnCompletedDynamic(awaiterTemp) : GenerateAwaitOnCompleted(awaiterTemp.Type, awaiterTemp));
instance.Add(GenerateReturn(finished: false));
if (((CompilationOptions)F.Compilation.Options).EnableEditAndContinue)
{
for (int i = 0; i < debugInfo.ReservedStateMachineCount; i++)
{
AwaitDebugId awaitId = debugInfo.AwaitId;
AddResumableState(declaratorSyntax, new AwaitDebugId((byte)(((AwaitDebugId)(ref awaitId)).RelativeStateOrdinal + 1 + i)), out StateMachineState _, out GeneratedLabelSymbol resumeLabel2);
instance.Add((BoundStatement)F.Label(resumeLabel2));
}
}
instance.Add((BoundStatement)F.Label(resumeLabel));
instance.Add(F.NoOp(NoOpStatementFlavor.AwaitResumePoint));
instance.Add((BoundStatement)F.Assignment(F.Local(awaiterTemp), TypeSymbol.Equals(awaiterTemp.Type, awaiterField.Type, (TypeCompareKind)0) ? F.Field(F.This(), awaiterField) : F.Convert(awaiterTemp.Type, F.Field(F.This(), awaiterField))));
instance.Add((BoundStatement)F.Assignment(F.Field(F.This(), awaiterField), F.NullOrDefault(awaiterField.Type)));
instance.Add((BoundStatement)GenerateSetBothStates((StateMachineState)(-1)));
return F.Block(instance.ToImmutableAndFree());
}
private BoundStatement GenerateAwaitOnCompletedDynamic(LocalSymbol awaiterTemp)
{
//IL_004f: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Invalid comparison between Unknown and I4
LocalSymbol localSymbol = F.SynthesizedLocal(F.WellKnownType((WellKnownType)241), null, isPinned: false, isKnownToReferToTempIfReferenceType: false, (RefKind)0, (SynthesizedLocalKind)(-2));
LocalSymbol localSymbol2 = F.SynthesizedLocal(F.WellKnownType((WellKnownType)172), null, isPinned: false, isKnownToReferToTempIfReferenceType: false, (RefKind)0, (SynthesizedLocalKind)(-2));
LocalSymbol localSymbol3 = (((int)F.CurrentType.TypeKind == 2) ? F.SynthesizedLocal(F.CurrentType, null, isPinned: false, isKnownToReferToTempIfReferenceType: false, (RefKind)0, (SynthesizedLocalKind)(-2)) : null);
ArrayBuilder<BoundStatement> instance = ArrayBuilder<BoundStatement>.GetInstance();
instance.Add((BoundStatement)F.Assignment(F.Local(localSymbol), F.As(F.Local(awaiterTemp), localSymbol.Type)));
if (localSymbol3 != null)
{
instance.Add((BoundStatement)F.Assignment(F.Local(localSymbol3), F.This()));
}
instance.Add(F.If(F.ObjectEqual(F.Local(localSymbol), F.Null(localSymbol.Type)), F.Block(ImmutableArray.Create(localSymbol2), F.Assignment(F.Local(localSymbol2), F.Convert(localSymbol2.Type, F.Local(awaiterTemp), Conversion.ExplicitReference)), F.ExpressionStatement(F.Call(F.Field(F.This(), _asyncMethodBuilderField), _asyncMethodBuilderMemberCollection.AwaitOnCompleted.Construct(localSymbol2.Type, F.This().Type), F.Local(localSymbol2), F.This(localSymbol3))), F.Assignment(F.Local(localSymbol2), F.NullOrDefault(localSymbol2.Type))), F.Block(F.ExpressionStatement(F.Call(F.Field(F.This(), _asyncMethodBuilderField), _asyncMethodBuilderMemberCollection.AwaitUnsafeOnCompleted.Construct(localSymbol.Type, F.This().Type), F.Local(localSymbol), F.This(localSymbol3))))));
instance.Add((BoundStatement)F.Assignment(F.Local(localSymbol), F.NullOrDefault(localSymbol.Type)));
return F.Block(SingletonOrPair(localSymbol, localSymbol3), instance.ToImmutableAndFree());
}
private BoundStatement GenerateAwaitOnCompleted(TypeSymbol loweredAwaiterType, LocalSymbol awaiterTemp)
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Invalid comparison between Unknown and I4
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
LocalSymbol localSymbol = (((int)F.CurrentType.TypeKind == 2) ? F.SynthesizedLocal(F.CurrentType, null, isPinned: false, isKnownToReferToTempIfReferenceType: false, (RefKind)0, (SynthesizedLocalKind)(-2)) : null);
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
MethodSymbol method = (F.Compilation.Conversions.ClassifyImplicitConversionFromType(loweredAwaiterType, F.Compilation.GetWellKnownType((WellKnownType)241), ref useSiteInfo).IsImplicit ? _asyncMethodBuilderMemberCollection.AwaitUnsafeOnCompleted : _asyncMethodBuilderMemberCollection.AwaitOnCompleted).Construct(loweredAwaiterType, F.This().Type);
if (_asyncMethodBuilderMemberCollection.CheckGenericMethodConstraints)
{
method.CheckConstraints(new ConstraintsHelper.CheckConstraintsArgs(F.Compilation, F.Compilation.Conversions, includeNullability: false, F.Syntax.Location, Diagnostics));
}
BoundExpression boundExpression = F.Call(F.Field(F.This(), _asyncMethodBuilderField), method, F.Local(awaiterTemp), F.This(localSymbol));
if (localSymbol != null)
{
boundExpression = F.Sequence(ImmutableArray.Create(localSymbol), ImmutableArray.Create(F.AssignmentExpression(F.Local(localSymbol), F.This())), boundExpression);
}
return F.ExpressionStatement(boundExpression);
}
private static ImmutableArray<LocalSymbol> SingletonOrPair(LocalSymbol first, LocalSymbol secondOpt)
{
if (!(secondOpt == null))
{
return ImmutableArray.Create(first, secondOpt);
}
return ImmutableArray.Create(first);
}
public sealed override BoundNode VisitReturnStatement(BoundReturnStatement node)
{
if (node.ExpressionOpt != null)
{
return F.Block(F.Assignment(F.Local(_exprRetValue), (BoundExpression)Visit(node.ExpressionOpt)), F.Goto(_exprReturnLabel));
}
return F.Goto(_exprReturnLabel);
}
}