public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { var ss = new BoundSpillSequence2(); var replacement = VisitExpression(ref ss, node); return(ss.Update(replacement)); }
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { var builder = new BoundSpillSequenceBuilder(); var replacement = VisitExpression(ref builder, node); return(builder.Update(replacement)); }
private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpression resultPlace) { var expression = (BoundExpression)Visit(node.Expression); var awaitablePlaceholder = node.AwaitableInfo.AwaitableInstancePlaceholder; if (awaitablePlaceholder != null) { _placeholderMap.Add(awaitablePlaceholder, expression); } var getAwaiter = node.AwaitableInfo.IsDynamic ? MakeCallMaybeDynamic(expression, null, WellKnownMemberNames.GetAwaiter) : (BoundExpression)Visit(node.AwaitableInfo.GetAwaiter); resultPlace = (BoundExpression)Visit(resultPlace); MethodSymbol getResult = VisitMethodSymbol(node.AwaitableInfo.GetResult); MethodSymbol isCompletedMethod = ((object)node.AwaitableInfo.IsCompleted != null) ? VisitMethodSymbol(node.AwaitableInfo.IsCompleted.GetMethod) : null; TypeSymbol type = VisitType(node.Type); if (awaitablePlaceholder != null) { _placeholderMap.Remove(awaitablePlaceholder); } // The awaiter temp facilitates EnC method remapping and thus have to be long-lived. // It transfers the awaiter objects from the old version of the MoveNext method to the new one. Debug.Assert(node.Syntax.IsKind(SyntaxKind.AwaitExpression) || node.WasCompilerGenerated); var awaiterTemp = F.SynthesizedLocal(getAwaiter.Type, syntax: node.Syntax, kind: SynthesizedLocalKind.Awaiter); var awaitIfIncomplete = F.Block( // temp $awaiterTemp = <expr>.GetAwaiter(); F.Assignment( F.Local(awaiterTemp), getAwaiter), // hidden sequence point facilitates EnC method remapping, see explanation on SynthesizedLocalKind.Awaiter: F.HiddenSequencePoint(), // if(!($awaiterTemp.IsCompleted)) { ... } F.If( condition: F.Not(GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)), thenClause: GenerateAwaitForIncompleteTask(awaiterTemp))); BoundExpression getResultCall = MakeCallMaybeDynamic( F.Local(awaiterTemp), getResult, WellKnownMemberNames.GetResult, resultsDiscarded: resultPlace == null); // [$resultPlace = ] $awaiterTemp.GetResult(); BoundStatement getResultStatement = resultPlace != null && !type.IsVoidType() ? F.Assignment(resultPlace, getResultCall) : F.ExpressionStatement(getResultCall); return(F.Block( ImmutableArray.Create(awaiterTemp), awaitIfIncomplete, getResultStatement)); }
private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpression resultPlace) { var expression = (BoundExpression)Visit(node.Expression); resultPlace = (BoundExpression)Visit(resultPlace); MethodSymbol getAwaiter = VisitMethodSymbol(node.GetAwaiter); MethodSymbol getResult = VisitMethodSymbol(node.GetResult); MethodSymbol isCompletedMethod = ((object)node.IsCompleted != null) ? VisitMethodSymbol(node.IsCompleted.GetMethod) : null; TypeSymbol type = VisitType(node.Type); // The lifespan of awaiter temp doesn't cross sequence points (user code in between awaits), so it doesn't need to be named. TypeSymbol awaiterType = node.IsDynamic ? DynamicTypeSymbol.Instance : getAwaiter.ReturnType; var awaiterTemp = F.SynthesizedLocal(awaiterType); var awaitIfIncomplete = F.Block( // temp $awaiterTemp = <expr>.GetAwaiter(); F.Assignment( F.Local(awaiterTemp), MakeCallMaybeDynamic(expression, getAwaiter, WellKnownMemberNames.GetAwaiter)), // if(!($awaiterTemp.IsCompleted)) { ... } F.If( condition: F.Not(GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)), thenClause: GenerateAwaitForIncompleteTask(awaiterTemp))); BoundExpression getResultCall = MakeCallMaybeDynamic( F.Local(awaiterTemp), getResult, WellKnownMemberNames.GetResult, resultsDiscarded: resultPlace == null); var nullAwaiter = F.AssignmentExpression(F.Local(awaiterTemp), F.NullOrDefault(awaiterTemp.Type)); if (resultPlace != null && type.SpecialType != SpecialType.System_Void) { // $resultTemp = $awaiterTemp.GetResult(); // $awaiterTemp = null; // $resultTemp LocalSymbol resultTemp = F.SynthesizedLocal(type); return(F.Block( ImmutableArray.Create(awaiterTemp, resultTemp), awaitIfIncomplete, F.Assignment(F.Local(resultTemp), getResultCall), F.ExpressionStatement(nullAwaiter), F.Assignment(resultPlace, F.Local(resultTemp)))); } else { // $awaiterTemp.GetResult(); // $awaiterTemp = null; return(F.Block( ImmutableArray.Create(awaiterTemp), awaitIfIncomplete, F.ExpressionStatement(getResultCall), F.ExpressionStatement(nullAwaiter))); } }
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { // An await expression has already been wrapped in a BoundSpillSequence if not at the top level, so // the spilling will occur in the enclosing node. BoundSpillSequenceBuilder builder = null; var expr = VisitExpression(ref builder, node.Expression); return(UpdateExpression(builder, node.Update(expr, node.AwaitableInfo, node.Type))); }
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { if (this.ExceptionHandleNesting != 0) { Debug.Assert(this.ExceptionHandleNesting > 0); this.sawAwaitInExceptionHandler = true; } return(base.VisitAwaitExpression(node)); }
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { if (this.ExceptionHandleNesting != 0) { Debug.Assert(this.ExceptionHandleNesting > 0); this.sawAwaitInExceptionHandler = true; } return base.VisitAwaitExpression(node); }
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { return(VisitAwaitExpression(node, resultsDiscarded: false)); }
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { // await expressions must, by now, have been moved to the top level. throw ExceptionUtilities.Unreachable; }
private BoundExpression VisitAwaitExpression(BoundAwaitExpression node, bool resultsDiscarded) { var expression = (BoundExpression)Visit(node.Expression); MethodSymbol getAwaiter = VisitMethodSymbol(node.GetAwaiter); MethodSymbol getResult = VisitMethodSymbol(node.GetResult); MethodSymbol isCompletedMethod = (node.IsCompleted != null) ? VisitMethodSymbol(node.IsCompleted.GetMethod) : null; TypeSymbol type = VisitType(node.Type); LocalSymbol awaiterTemp; if (getResult == null) { awaiterTemp = F.SynthesizedLocal(DynamicTypeSymbol.Instance, name: null); } else if (type.IsDynamic()) { var awaiterType = ((NamedTypeSymbol)getAwaiter.ReturnType).OriginalNamedTypeDefinition.Construct(F.SpecialType(SpecialType.System_Object)); awaiterTemp = F.SynthesizedLocal(awaiterType, null); getResult = ((MethodSymbol)getResult.OriginalDefinition).AsMember(awaiterType); isCompletedMethod = ((MethodSymbol)isCompletedMethod.OriginalDefinition).AsMember(awaiterType); } else { awaiterTemp = F.SynthesizedLocal(getAwaiter.ReturnType, name: null); } var awaitIfIncomplete = F.Block( // temp $awaiterTemp = <expr>.GetAwaiter(); F.Assignment( F.Local(awaiterTemp), MakeCallMaybeDynamic(expression, getAwaiter, "GetAwaiter")), // if(!($awaiterTemp.IsCompleted)) { ... } F.If( condition: F.Not(GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)), thenClause: GenerateAwaitForIncompleteTask(awaiterTemp))); BoundExpression getResultCall = MakeCallMaybeDynamic( F.Local(awaiterTemp), getResult, "GetResult", resultsDiscarded: resultsDiscarded); var nullAwaiter = F.AssignmentExpression(F.Local(awaiterTemp), F.NullOrDefault(awaiterTemp.Type)); BoundExpression onAwaitFinished; if (!resultsDiscarded && type.SpecialType != SpecialType.System_Void) { // $resultTemp = $awaiterTemp.GetResult(); // $awaiterTemp = null; // $resultTemp LocalSymbol resultTemp = F.SynthesizedLocal(type, null); onAwaitFinished = F.Sequence( resultTemp, F.AssignmentExpression(F.Local(resultTemp), getResultCall), nullAwaiter, F.Local(resultTemp)); } else { // $awaiterTemp.GetResult(); // $awaiterTemp = null; onAwaitFinished = F.Sequence( ReadOnlyArray <LocalSymbol> .Empty, getResultCall, nullAwaiter); } return(F.SpillSequence( ReadOnlyArray <LocalSymbol> .CreateFrom(awaiterTemp), ReadOnlyArray <BoundSpillTemp> .Empty, ReadOnlyArray <FieldSymbol> .Empty, ReadOnlyArray <BoundStatement> .CreateFrom(awaitIfIncomplete), onAwaitFinished)); }
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { base.VisitAwaitExpression(node); MarkLocalsUnassigned(); return null; }
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { return VisitAwaitExpression(node, resultsDiscarded: false); }
private BoundExpression VisitAwaitExpression(BoundAwaitExpression node, bool resultsDiscarded) { var expression = (BoundExpression)Visit(node.Expression); MethodSymbol getAwaiter = VisitMethodSymbol(node.GetAwaiter); MethodSymbol getResult = VisitMethodSymbol(node.GetResult); MethodSymbol isCompletedMethod = (node.IsCompleted != null) ? VisitMethodSymbol(node.IsCompleted.GetMethod) : null; TypeSymbol type = VisitType(node.Type); LocalSymbol awaiterTemp; if (getResult == null) { awaiterTemp = F.SynthesizedLocal(DynamicTypeSymbol.Instance, name: null); } else if (type.IsDynamic()) { var awaiterType = ((NamedTypeSymbol)getAwaiter.ReturnType).OriginalNamedTypeDefinition.Construct(F.SpecialType(SpecialType.System_Object)); awaiterTemp = F.SynthesizedLocal(awaiterType, null); getResult = ((MethodSymbol)getResult.OriginalDefinition).AsMember(awaiterType); isCompletedMethod = ((MethodSymbol)isCompletedMethod.OriginalDefinition).AsMember(awaiterType); } else { awaiterTemp = F.SynthesizedLocal(getAwaiter.ReturnType, name: null); } var awaitIfIncomplete = F.Block( // temp $awaiterTemp = <expr>.GetAwaiter(); F.Assignment( F.Local(awaiterTemp), MakeCallMaybeDynamic(expression, getAwaiter, "GetAwaiter")), // if(!($awaiterTemp.IsCompleted)) { ... } F.If( condition: F.Not(GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)), thenClause: GenerateAwaitForIncompleteTask(awaiterTemp))); BoundExpression getResultCall = MakeCallMaybeDynamic( F.Local(awaiterTemp), getResult, "GetResult", resultsDiscarded: resultsDiscarded); var nullAwaiter = F.AssignmentExpression(F.Local(awaiterTemp), F.NullOrDefault(awaiterTemp.Type)); BoundExpression onAwaitFinished; if (!resultsDiscarded && type.SpecialType != SpecialType.System_Void) { // $resultTemp = $awaiterTemp.GetResult(); // $awaiterTemp = null; // $resultTemp LocalSymbol resultTemp = F.SynthesizedLocal(type, null); onAwaitFinished = F.Sequence( resultTemp, F.AssignmentExpression(F.Local(resultTemp), getResultCall), nullAwaiter, F.Local(resultTemp)); } else { // $awaiterTemp.GetResult(); // $awaiterTemp = null; onAwaitFinished = F.Sequence( ReadOnlyArray<LocalSymbol>.Empty, getResultCall, nullAwaiter); } return F.SpillSequence( ReadOnlyArray<LocalSymbol>.CreateFrom(awaiterTemp), ReadOnlyArray<BoundSpillTemp>.Empty, ReadOnlyArray<FieldSymbol>.Empty, ReadOnlyArray<BoundStatement>.CreateFrom(awaitIfIncomplete), onAwaitFinished); }
private BoundStatement RewriteUsingStatementTryFinally(SyntaxNode syntax, BoundBlock tryBlock, BoundLocal local, SyntaxToken awaitKeywordOpt, AwaitableInfo awaitOpt) { // SPEC: When ResourceType is a non-nullable value type, the expansion is: // SPEC: // SPEC: { // SPEC: ResourceType resource = expr; // SPEC: try { statement; } // SPEC: finally { ((IDisposable)resource).Dispose(); } // SPEC: } // SPEC: // SPEC: Otherwise, when Resource type is a nullable value type or // SPEC: a reference type other than dynamic, the expansion is: // SPEC: // SPEC: { // SPEC: ResourceType resource = expr; // SPEC: try { statement; } // SPEC: finally { if (resource != null) ((IDisposable)resource).Dispose(); } // SPEC: } // SPEC: // SPEC: Otherwise, when ResourceType is dynamic, the expansion is: // SPEC: { // SPEC: dynamic resource = expr; // SPEC: IDisposable d = (IDisposable)resource; // SPEC: try { statement; } // SPEC: finally { if (d != null) d.Dispose(); } // SPEC: } // SPEC: // SPEC: An implementation is permitted to implement a given using statement // SPEC: differently -- for example, for performance reasons -- as long as the // SPEC: behavior is consistent with the above expansion. // // In the case of using-await statement, we'll use "IAsyncDisposable" instead of "IDisposable", "await DisposeAsync()" instead of "Dispose()" // // And we do in fact generate the code slightly differently than precisely how it is // described above. // // First: if the type is a non-nullable value type then we do not do the // *boxing conversion* from the resource to IDisposable. Rather, we do // a *constrained virtual call* that elides the boxing if possible. // // Now, you might wonder if that is legal; isn't skipping the boxing producing // an observable difference? Because if the value type is mutable and the Dispose // mutates it, then skipping the boxing means that we are now mutating the original, // not the boxed copy. But this is never observable. Either (1) we have "using(R r = x){}" // and r is out of scope after the finally, so it is not possible to observe the mutation, // or (2) we have "using(x) {}". But that has the semantics of "using(R temp = x){}", // so again, we are not mutating x to begin with; we're always mutating a copy. Therefore // it doesn't matter if we skip making *a copy of the copy*. // // This is what the dev10 compiler does, and we do so as well. // // Second: if the type is a nullable value type then we can similarly elide the boxing. // We can generate // // { // ResourceType resource = expr; // try { statement; } // finally { if (resource.HasValue) resource.GetValueOrDefault().Dispose(); } // } // // Where again we do a constrained virtual call to Dispose, rather than boxing // the value to IDisposable. // // Note that this optimization is *not* what the native compiler does; in this case // the native compiler behavior is to test for HasValue, then *box* and convert // the boxed value to IDisposable. There's no need to do that. // // Third: if we have "using(x)" and x is dynamic then obviously we need not generate // "{ dynamic temp1 = x; IDisposable temp2 = (IDisposable) temp1; ... }". Rather, we elide // the completely unnecessary first temporary. Debug.Assert((awaitKeywordOpt == default) == (awaitOpt == default(AwaitableInfo))); BoundExpression disposedExpression; bool isNullableValueType = local.Type.IsNullableType(); if (isNullableValueType) { MethodSymbol getValueOrDefault = UnsafeGetNullableMethod(syntax, local.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); // local.GetValueOrDefault() disposedExpression = BoundCall.Synthesized(syntax, local, getValueOrDefault); } else { // local disposedExpression = local; } BoundExpression disposeCall; if (awaitOpt == null && Binder.TryGetSpecialTypeMember(_compilation, SpecialMember.System_IDisposable__Dispose, syntax, _diagnostics, out MethodSymbol disposeMethodSymbol)) { // local.Dispose() disposeCall = BoundCall.Synthesized(syntax, disposedExpression, disposeMethodSymbol); } else if (awaitOpt != null && TryGetWellKnownTypeMember(syntax: null, WellKnownMember.System_IAsyncDisposable__DisposeAsync, out MethodSymbol disposeAsyncMethodSymbol, location: awaitKeywordOpt.GetLocation())) { // await local.DisposeAsync() _sawAwaitInExceptionHandler = true; var callExpr = BoundCall.Synthesized(syntax, disposedExpression, disposeAsyncMethodSymbol); TypeSymbol awaitExpressionType = awaitOpt.GetResult?.ReturnType.TypeSymbol ?? _compilation.DynamicType; BoundAwaitExpression awaitExpr = new BoundAwaitExpression(syntax, callExpr, awaitOpt, awaitExpressionType) { WasCompilerGenerated = true }; disposeCall = (BoundExpression)VisitAwaitExpression(awaitExpr); }
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { _seenAwait = true; return base.VisitAwaitExpression(node); }
private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpression resultPlace) { var expression = (BoundExpression)Visit(node.Expression); resultPlace = (BoundExpression)Visit(resultPlace); MethodSymbol getAwaiter = VisitMethodSymbol(node.GetAwaiter); MethodSymbol getResult = VisitMethodSymbol(node.GetResult); MethodSymbol isCompletedMethod = ((object)node.IsCompleted != null) ? VisitMethodSymbol(node.IsCompleted.GetMethod) : null; TypeSymbol type = VisitType(node.Type); // The awaiter temp facilitates EnC method remapping and thus have to be long-lived. // It transfers the awaiter objects from the old version of the MoveNext method to the new one. Debug.Assert(node.Syntax.IsKind(SyntaxKind.AwaitExpression)); TypeSymbol awaiterType = node.IsDynamic ? DynamicTypeSymbol.Instance : getAwaiter.ReturnType; var awaiterTemp = F.SynthesizedLocal(awaiterType, syntax: node.Syntax, kind: SynthesizedLocalKind.Awaiter); var awaitIfIncomplete = F.Block( // temp $awaiterTemp = <expr>.GetAwaiter(); F.Assignment( F.Local(awaiterTemp), MakeCallMaybeDynamic(expression, getAwaiter, WellKnownMemberNames.GetAwaiter)), // hidden sequence point facilitates EnC method remapping, see explanation on SynthesizedLocalKind.Awaiter: F.HiddenSequencePoint(), // if(!($awaiterTemp.IsCompleted)) { ... } F.If( condition: F.Not(GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)), thenClause: GenerateAwaitForIncompleteTask(awaiterTemp))); BoundExpression getResultCall = MakeCallMaybeDynamic( F.Local(awaiterTemp), getResult, WellKnownMemberNames.GetResult, resultsDiscarded: resultPlace == null); var nullAwaiter = F.AssignmentExpression(F.Local(awaiterTemp), F.NullOrDefault(awaiterTemp.Type)); if (resultPlace != null && type.SpecialType != SpecialType.System_Void) { // $resultTemp = $awaiterTemp.GetResult(); // $awaiterTemp = null; // $resultTemp LocalSymbol resultTemp = F.SynthesizedLocal(type); return(F.Block( ImmutableArray.Create(awaiterTemp, resultTemp), awaitIfIncomplete, F.Assignment(F.Local(resultTemp), getResultCall), F.ExpressionStatement(nullAwaiter), F.Assignment(resultPlace, F.Local(resultTemp)))); } else { // $awaiterTemp.GetResult(); // $awaiterTemp = null; return(F.Block( ImmutableArray.Create(awaiterTemp), awaitIfIncomplete, F.ExpressionStatement(getResultCall), F.ExpressionStatement(nullAwaiter))); } }
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { _seenAwait = true; return(base.VisitAwaitExpression(node)); }
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { var ss = new BoundSpillSequence2(); var replacement = VisitExpression(ref ss, node); return ss.Update(replacement); }
public BoundExpression VisitAwaitExpression(BoundAwaitExpression node, bool used) { return(RewriteAwaitExpression((BoundExpression)base.VisitAwaitExpression(node) !, used)); }
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { return(VisitAwaitExpression(node, true)); }
private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpression resultPlace) { var expression = (BoundExpression)Visit(node.Expression); resultPlace = (BoundExpression)Visit(resultPlace); MethodSymbol getAwaiter = VisitMethodSymbol(node.GetAwaiter); MethodSymbol getResult = VisitMethodSymbol(node.GetResult); MethodSymbol isCompletedMethod = ((object)node.IsCompleted != null) ? VisitMethodSymbol(node.IsCompleted.GetMethod) : null; TypeSymbol type = VisitType(node.Type); LocalSymbol awaiterTemp; if ((object)getResult == null) { awaiterTemp = F.SynthesizedLocal(DynamicTypeSymbol.Instance); } else if (type.IsDynamic()) { var awaiterType = ((NamedTypeSymbol)getAwaiter.ReturnType).OriginalDefinition.Construct(F.SpecialType(SpecialType.System_Object)); awaiterTemp = F.SynthesizedLocal(awaiterType); getResult = ((MethodSymbol)getResult.OriginalDefinition).AsMember(awaiterType); isCompletedMethod = ((MethodSymbol)isCompletedMethod.OriginalDefinition).AsMember(awaiterType); } else { awaiterTemp = F.SynthesizedLocal(getAwaiter.ReturnType); } var awaitIfIncomplete = F.Block( // temp $awaiterTemp = <expr>.GetAwaiter(); F.Assignment( F.Local(awaiterTemp), MakeCallMaybeDynamic(expression, getAwaiter, WellKnownMemberNames.GetAwaiter)), // if(!($awaiterTemp.IsCompleted)) { ... } F.If( condition: F.Not(GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)), thenClause: GenerateAwaitForIncompleteTask(awaiterTemp))); BoundExpression getResultCall = MakeCallMaybeDynamic( F.Local(awaiterTemp), getResult, WellKnownMemberNames.GetResult, resultsDiscarded: resultPlace == null); var nullAwaiter = F.AssignmentExpression(F.Local(awaiterTemp), F.NullOrDefault(awaiterTemp.Type)); if (resultPlace != null && type.SpecialType != SpecialType.System_Void) { // $resultTemp = $awaiterTemp.GetResult(); // $awaiterTemp = null; // $resultTemp LocalSymbol resultTemp = F.SynthesizedLocal(type); return(F.Block( ImmutableArray.Create <LocalSymbol>(awaiterTemp, resultTemp), awaitIfIncomplete, F.Assignment(F.Local(resultTemp), getResultCall), F.ExpressionStatement(nullAwaiter), F.Assignment(resultPlace, F.Local(resultTemp)))); } else { // $awaiterTemp.GetResult(); // $awaiterTemp = null; return(F.Block( ImmutableArray.Create <LocalSymbol>(awaiterTemp), awaitIfIncomplete, F.ExpressionStatement(getResultCall), F.ExpressionStatement(nullAwaiter))); } }
private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpression resultPlace) { var expression = (BoundExpression)Visit(node.Expression); resultPlace = (BoundExpression)Visit(resultPlace); MethodSymbol getAwaiter = VisitMethodSymbol(node.GetAwaiter); MethodSymbol getResult = VisitMethodSymbol(node.GetResult); MethodSymbol isCompletedMethod = ((object)node.IsCompleted != null) ? VisitMethodSymbol(node.IsCompleted.GetMethod) : null; TypeSymbol type = VisitType(node.Type); LocalSymbol awaiterTemp; if ((object)getResult == null) { awaiterTemp = F.SynthesizedLocal(DynamicTypeSymbol.Instance); } else if (type.IsDynamic()) { var awaiterType = ((NamedTypeSymbol)getAwaiter.ReturnType).OriginalDefinition.Construct(F.SpecialType(SpecialType.System_Object)); awaiterTemp = F.SynthesizedLocal(awaiterType); getResult = ((MethodSymbol)getResult.OriginalDefinition).AsMember(awaiterType); isCompletedMethod = ((MethodSymbol)isCompletedMethod.OriginalDefinition).AsMember(awaiterType); } else { awaiterTemp = F.SynthesizedLocal(getAwaiter.ReturnType); } var awaitIfIncomplete = F.Block( // temp $awaiterTemp = <expr>.GetAwaiter(); F.Assignment( F.Local(awaiterTemp), MakeCallMaybeDynamic(expression, getAwaiter, WellKnownMemberNames.GetAwaiter)), // if(!($awaiterTemp.IsCompleted)) { ... } F.If( condition: F.Not(GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)), thenClause: GenerateAwaitForIncompleteTask(awaiterTemp))); BoundExpression getResultCall = MakeCallMaybeDynamic( F.Local(awaiterTemp), getResult, WellKnownMemberNames.GetResult, resultsDiscarded: resultPlace == null); var nullAwaiter = F.AssignmentExpression(F.Local(awaiterTemp), F.NullOrDefault(awaiterTemp.Type)); if (resultPlace != null && type.SpecialType != SpecialType.System_Void) { // $resultTemp = $awaiterTemp.GetResult(); // $awaiterTemp = null; // $resultTemp LocalSymbol resultTemp = F.SynthesizedLocal(type); return F.Block( ImmutableArray.Create<LocalSymbol>(awaiterTemp, resultTemp), awaitIfIncomplete, F.Assignment(F.Local(resultTemp), getResultCall), F.ExpressionStatement(nullAwaiter), F.Assignment(resultPlace, F.Local(resultTemp))); } else { // $awaiterTemp.GetResult(); // $awaiterTemp = null; return F.Block( ImmutableArray.Create<LocalSymbol>(awaiterTemp), awaitIfIncomplete, F.ExpressionStatement(getResultCall), F.ExpressionStatement(nullAwaiter)); } }
private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpression resultPlace) { var expression = (BoundExpression)Visit(node.Expression); resultPlace = (BoundExpression)Visit(resultPlace); MethodSymbol getAwaiter = VisitMethodSymbol(node.GetAwaiter); MethodSymbol getResult = VisitMethodSymbol(node.GetResult); MethodSymbol isCompletedMethod = ((object)node.IsCompleted != null) ? VisitMethodSymbol(node.IsCompleted.GetMethod) : null; TypeSymbol type = VisitType(node.Type); // The awaiter temp facilitates EnC method remapping and thus have to be long-lived. // It transfers the awaiter objects from the old version of the MoveNext method to the new one. Debug.Assert(node.Syntax.IsKind(SyntaxKind.AwaitExpression)); TypeSymbol awaiterType = node.IsDynamic ? DynamicTypeSymbol.Instance : getAwaiter.ReturnType; var awaiterTemp = F.SynthesizedLocal(awaiterType, syntax: node.Syntax, kind: SynthesizedLocalKind.Awaiter); var awaitIfIncomplete = F.Block( // temp $awaiterTemp = <expr>.GetAwaiter(); F.Assignment( F.Local(awaiterTemp), MakeCallMaybeDynamic(expression, getAwaiter, WellKnownMemberNames.GetAwaiter)), // hidden sequence point facilitates EnC method remapping, see explanation on SynthesizedLocalKind.Awaiter: F.HiddenSequencePoint(), // if(!($awaiterTemp.IsCompleted)) { ... } F.If( condition: F.Not(GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)), thenClause: GenerateAwaitForIncompleteTask(awaiterTemp))); BoundExpression getResultCall = MakeCallMaybeDynamic( F.Local(awaiterTemp), getResult, WellKnownMemberNames.GetResult, resultsDiscarded: resultPlace == null); var nullAwaiter = F.AssignmentExpression(F.Local(awaiterTemp), F.NullOrDefault(awaiterTemp.Type)); if (resultPlace != null && type.SpecialType != SpecialType.System_Void) { // $resultTemp = $awaiterTemp.GetResult(); // $awaiterTemp = null; // $resultTemp LocalSymbol resultTemp = F.SynthesizedLocal(type); return F.Block( ImmutableArray.Create(awaiterTemp, resultTemp), ImmutableArray<LocalFunctionSymbol>.Empty, awaitIfIncomplete, F.Assignment(F.Local(resultTemp), getResultCall), F.ExpressionStatement(nullAwaiter), F.Assignment(resultPlace, F.Local(resultTemp))); } else { // $awaiterTemp.GetResult(); // $awaiterTemp = null; return F.Block( ImmutableArray.Create(awaiterTemp), ImmutableArray<LocalFunctionSymbol>.Empty, awaitIfIncomplete, F.ExpressionStatement(getResultCall), F.ExpressionStatement(nullAwaiter)); } }
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { base.VisitAwaitExpression(node); MarkLocalsUnassigned(); return(null); }
public override BoundNode VisitAwaitExpression(BoundAwaitExpression node) { _sawAwait = true; return(null); }
public override BoundNode?VisitAwaitExpression(BoundAwaitExpression node) { ContainsAwait = true; return(null); }
private BoundBlock VisitAwaitExpression(BoundAwaitExpression node, BoundExpression resultPlace) { var expression = (BoundExpression)Visit(node.Expression); resultPlace = (BoundExpression)Visit(resultPlace); MethodSymbol getAwaiter = VisitMethodSymbol(node.GetAwaiter); MethodSymbol getResult = VisitMethodSymbol(node.GetResult); MethodSymbol isCompletedMethod = ((object)node.IsCompleted != null) ? VisitMethodSymbol(node.IsCompleted.GetMethod) : null; TypeSymbol type = VisitType(node.Type); // The lifespan of awaiter temp doesn't cross sequence points (user code in between awaits), so it doesn't need to be named. TypeSymbol awaiterType = node.IsDynamic ? DynamicTypeSymbol.Instance : getAwaiter.ReturnType; var awaiterTemp = F.SynthesizedLocal(awaiterType); var awaitIfIncomplete = F.Block( // temp $awaiterTemp = <expr>.GetAwaiter(); F.Assignment( F.Local(awaiterTemp), MakeCallMaybeDynamic(expression, getAwaiter, WellKnownMemberNames.GetAwaiter)), // if(!($awaiterTemp.IsCompleted)) { ... } F.If( condition: F.Not(GenerateGetIsCompleted(awaiterTemp, isCompletedMethod)), thenClause: GenerateAwaitForIncompleteTask(awaiterTemp))); BoundExpression getResultCall = MakeCallMaybeDynamic( F.Local(awaiterTemp), getResult, WellKnownMemberNames.GetResult, resultsDiscarded: resultPlace == null); var nullAwaiter = F.AssignmentExpression(F.Local(awaiterTemp), F.NullOrDefault(awaiterTemp.Type)); if (resultPlace != null && type.SpecialType != SpecialType.System_Void) { // $resultTemp = $awaiterTemp.GetResult(); // $awaiterTemp = null; // $resultTemp LocalSymbol resultTemp = F.SynthesizedLocal(type); return F.Block( ImmutableArray.Create(awaiterTemp, resultTemp), awaitIfIncomplete, F.Assignment(F.Local(resultTemp), getResultCall), F.ExpressionStatement(nullAwaiter), F.Assignment(resultPlace, F.Local(resultTemp))); } else { // $awaiterTemp.GetResult(); // $awaiterTemp = null; return F.Block( ImmutableArray.Create(awaiterTemp), awaitIfIncomplete, F.ExpressionStatement(getResultCall), F.ExpressionStatement(nullAwaiter)); } }