2 // async.cs: Asynchronous functions
5 // Marek Safar (marek.safar@gmail.com)
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
9 // Copyright 2011 Novell, Inc.
10 // Copyright 2011 Xamarin Inc.
14 using System
.Collections
.Generic
;
16 using System
.Collections
;
19 using IKVM
.Reflection
.Emit
;
21 using System
.Reflection
.Emit
;
26 public class Await
: ExpressionStatement
31 public Await (Expression expr
, Location loc
)
37 public Expression Expr
{
43 public AwaitStatement Statement
{
49 protected override void CloneTo (CloneContext clonectx
, Expression target
)
51 var t
= (Await
) target
;
53 t
.expr
= expr
.Clone (clonectx
);
56 public override Expression
CreateExpressionTree (ResolveContext ec
)
58 throw new NotImplementedException ("ET");
61 public override bool ContainsEmitWithAwait ()
66 protected override Expression
DoResolve (ResolveContext rc
)
68 if (rc
.HasSet (ResolveContext
.Options
.LockScope
)) {
69 rc
.Report
.Error (1996, loc
,
70 "The `await' operator cannot be used in the body of a lock statement");
74 rc
.Report
.Error (4004, loc
,
75 "The `await' operator cannot be used in an unsafe context");
78 var bc
= (BlockContext
) rc
;
80 stmt
= new AwaitStatement (expr
, loc
);
81 if (!stmt
.Resolve (bc
))
84 type
= stmt
.ResultType
;
85 eclass
= ExprClass
.Variable
;
89 public override void Emit (EmitContext ec
)
91 stmt
.EmitPrologue (ec
);
93 using (ec
.With (BuilderContext
.Options
.OmitDebugInfo
, true)) {
98 public override Expression
EmitToField (EmitContext ec
)
100 stmt
.EmitPrologue (ec
);
101 return stmt
.GetResultExpression (ec
);
104 public void EmitAssign (EmitContext ec
, FieldExpr field
)
106 stmt
.EmitPrologue (ec
);
107 field
.InstanceExpression
.Emit (ec
);
111 public override void EmitStatement (EmitContext ec
)
113 stmt
.EmitStatement (ec
);
116 public override object Accept (StructuralVisitor visitor
)
118 return visitor
.Visit (this);
122 public class AwaitStatement
: YieldStatement
<AsyncInitializer
>
124 sealed class AwaitableMemberAccess
: MemberAccess
126 public AwaitableMemberAccess (Expression expr
)
127 : base (expr
, "GetAwaiter")
131 protected override void Error_TypeDoesNotContainDefinition (ResolveContext rc
, TypeSpec type
, string name
)
133 Error_OperatorCannotBeApplied (rc
, type
);
136 protected override void Error_OperatorCannotBeApplied (ResolveContext rc
, TypeSpec type
)
138 var invocation
= LeftExpression
as Invocation
;
139 if (invocation
!= null && invocation
.MethodGroup
!= null && (invocation
.MethodGroup
.BestCandidate
.Modifiers
& Modifiers
.ASYNC
) != 0) {
140 rc
.Report
.Error (4008, loc
, "Cannot await void method `{0}'. Consider changing method return type to `Task'",
141 invocation
.GetSignatureForError ());
143 rc
.Report
.Error (4001, loc
, "Cannot await `{0}' expression", type
.GetSignatureForError ());
148 sealed class GetResultInvocation
: Invocation
150 public GetResultInvocation (MethodGroupExpr mge
, Arguments arguments
)
151 : base (null, arguments
)
154 type
= mg
.BestCandidateReturnType
;
157 public override Expression
EmitToField (EmitContext ec
)
164 PropertySpec is_completed
;
165 MethodSpec get_result
;
167 TypeSpec result_type
;
169 public AwaitStatement (Expression expr
, Location loc
)
178 return is_completed
== null;
182 public TypeSpec ResultType
{
190 protected override void DoEmit (EmitContext ec
)
192 GetResultExpression (ec
).Emit (ec
);
195 public Expression
GetResultExpression (EmitContext ec
)
197 var fe_awaiter
= new FieldExpr (awaiter
, loc
);
198 fe_awaiter
.InstanceExpression
= new CompilerGeneratedThis (ec
.CurrentType
, loc
);
201 // result = awaiter.GetResult ();
204 var rc
= new ResolveContext (ec
.MemberContext
);
205 return new Invocation (new MemberAccess (fe_awaiter
, "GetResult"), new Arguments (0)).Resolve (rc
);
207 var mg_result
= MethodGroupExpr
.CreatePredefined (get_result
, fe_awaiter
.Type
, loc
);
208 mg_result
.InstanceExpression
= fe_awaiter
;
210 return new GetResultInvocation (mg_result
, new Arguments (0));
214 public void EmitPrologue (EmitContext ec
)
216 awaiter
= ((AsyncTaskStorey
) machine_initializer
.Storey
).AddAwaiter (expr
.Type
, loc
);
218 var fe_awaiter
= new FieldExpr (awaiter
, loc
);
219 fe_awaiter
.InstanceExpression
= new CompilerGeneratedThis (ec
.CurrentType
, loc
);
222 // awaiter = expr.GetAwaiter ();
224 fe_awaiter
.EmitAssign (ec
, expr
, false, false);
226 Label skip_continuation
= ec
.DefineLabel ();
228 Expression completed_expr
;
230 var rc
= new ResolveContext (ec
.MemberContext
);
232 Arguments dargs
= new Arguments (1);
233 dargs
.Add (new Argument (fe_awaiter
));
234 completed_expr
= new DynamicMemberBinder ("IsCompleted", dargs
, loc
).Resolve (rc
);
236 dargs
= new Arguments (1);
237 dargs
.Add (new Argument (completed_expr
));
238 completed_expr
= new DynamicConversion (ec
.Module
.Compiler
.BuiltinTypes
.Bool
, 0, dargs
, loc
).Resolve (rc
);
240 var pe
= PropertyExpr
.CreatePredefined (is_completed
, loc
);
241 pe
.InstanceExpression
= fe_awaiter
;
245 completed_expr
.EmitBranchable (ec
, skip_continuation
, true);
250 // The stack has to be empty before calling await continuation. We handle this
251 // by lifting values which would be left on stack into class fields. The process
252 // is quite complicated and quite hard to test because any expression can possibly
253 // leave a value on the stack.
255 // Following assert fails when some of expression called before is missing EmitToField
256 // or parent expression fails to find await in children expressions
258 ec
.AssertEmptyStack ();
260 var storey
= (AsyncTaskStorey
) machine_initializer
.Storey
;
262 storey
.EmitAwaitOnCompletedDynamic (ec
, fe_awaiter
);
264 storey
.EmitAwaitOnCompleted (ec
, fe_awaiter
);
268 machine_initializer
.EmitLeave (ec
, unwind_protect
);
270 ec
.MarkLabel (resume_point
);
271 ec
.MarkLabel (skip_continuation
);
274 public void EmitStatement (EmitContext ec
)
279 awaiter
.IsAvailableForReuse
= true;
281 if (ResultType
.Kind
!= MemberKind
.Void
) {
282 var storey
= (AsyncTaskStorey
) machine_initializer
.Storey
;
284 if (storey
.HoistedReturn
!= null)
285 storey
.HoistedReturn
.EmitAssign (ec
);
287 ec
.Emit (OpCodes
.Pop
);
291 void Error_WrongAwaiterPattern (ResolveContext rc
, TypeSpec awaiter
)
293 rc
.Report
.Error (4011, loc
, "The awaiter type `{0}' must have suitable IsCompleted and GetResult members",
294 awaiter
.GetSignatureForError ());
297 public override bool Resolve (BlockContext bc
)
299 if (bc
.CurrentBlock
is Linq
.QueryBlock
) {
300 bc
.Report
.Error (1995, loc
,
301 "The `await' operator may only be used in a query expression within the first collection expression of the initial `from' clause or within the collection expression of a `join' clause");
305 if (!base.Resolve (bc
))
308 Arguments args
= new Arguments (0);
313 // The await expression is of dynamic type
315 if (type
.BuiltinType
== BuiltinTypeSpec
.Type
.Dynamic
) {
317 expr
= new Invocation (new MemberAccess (expr
, "GetAwaiter"), args
).Resolve (bc
);
322 // Check whether the expression is awaitable
324 Expression ama
= new AwaitableMemberAccess (expr
).Resolve (bc
);
328 var errors_printer
= new SessionReportPrinter ();
329 var old
= bc
.Report
.SetPrinter (errors_printer
);
330 ama
= new Invocation (ama
, args
).Resolve (bc
);
331 bc
.Report
.SetPrinter (old
);
333 if (errors_printer
.ErrorsCount
> 0 || !MemberAccess
.IsValidDotExpression (ama
.Type
)) {
334 bc
.Report
.Error (1986, expr
.Location
,
335 "The `await' operand type `{0}' must have suitable GetAwaiter method",
336 expr
.Type
.GetSignatureForError ());
341 var awaiter_type
= ama
.Type
;
345 // Predefined: bool IsCompleted { get; }
347 is_completed
= MemberCache
.FindMember (awaiter_type
, MemberFilter
.Property ("IsCompleted", bc
.Module
.Compiler
.BuiltinTypes
.Bool
),
348 BindingRestriction
.InstanceOnly
) as PropertySpec
;
350 if (is_completed
== null || !is_completed
.HasGet
) {
351 Error_WrongAwaiterPattern (bc
, awaiter_type
);
356 // Predefined: GetResult ()
358 // The method return type is also result type of await expression
360 get_result
= MemberCache
.FindMember (awaiter_type
, MemberFilter
.Method ("GetResult", 0,
361 ParametersCompiled
.EmptyReadOnlyParameters
, null),
362 BindingRestriction
.InstanceOnly
) as MethodSpec
;
364 if (get_result
== null) {
365 Error_WrongAwaiterPattern (bc
, awaiter_type
);
370 // Predefined: INotifyCompletion.OnCompleted (System.Action)
372 var nc
= bc
.Module
.PredefinedTypes
.INotifyCompletion
;
373 if (nc
.Define () && !awaiter_type
.ImplementsInterface (nc
.TypeSpec
, false)) {
374 bc
.Report
.Error (4027, loc
, "The awaiter type `{0}' must implement interface `{1}'",
375 awaiter_type
.GetSignatureForError (), nc
.GetSignatureForError ());
379 result_type
= get_result
.ReturnType
;
385 public class AsyncInitializer
: StateMachineInitializer
387 TypeInferenceContext return_inference
;
389 public AsyncInitializer (ParametersBlock block
, TypeDefinition host
, TypeSpec returnType
)
390 : base (block
, host
, returnType
)
396 public override string ContainerType
{
398 return "async state machine block";
402 public override bool IsIterator
{
408 public TypeInferenceContext ReturnTypeInference
{
410 return return_inference
;
416 protected override BlockContext
CreateBlockContext (ResolveContext rc
)
418 var ctx
= base.CreateBlockContext (rc
);
419 var lambda
= rc
.CurrentAnonymousMethod
as LambdaMethod
;
421 return_inference
= lambda
.ReturnTypeInference
;
423 ctx
.StartFlowBranching (this, rc
.CurrentBranching
);
427 public override Expression
CreateExpressionTree (ResolveContext ec
)
429 return base.CreateExpressionTree (ec
);
432 public override void Emit (EmitContext ec
)
434 throw new NotImplementedException ();
437 protected override void EmitMoveNextEpilogue (EmitContext ec
)
439 var storey
= (AsyncTaskStorey
) Storey
;
440 storey
.EmitSetResult (ec
);
443 public override void EmitStatement (EmitContext ec
)
445 var storey
= (AsyncTaskStorey
) Storey
;
446 storey
.EmitInitializer (ec
);
447 ec
.Emit (OpCodes
.Ret
);
451 class AsyncTaskStorey
: StateMachine
455 readonly TypeSpec return_type
;
456 MethodSpec set_result
;
457 MethodSpec set_exception
;
458 MethodSpec builder_factory
;
459 MethodSpec builder_start
;
461 LocalVariable hoisted_return
;
463 Dictionary
<TypeSpec
, List
<Field
>> stack_fields
;
464 Dictionary
<TypeSpec
, List
<Field
>> awaiter_fields
;
466 public AsyncTaskStorey (ParametersBlock block
, IMemberContext context
, AsyncInitializer initializer
, TypeSpec type
)
467 : base (block
, initializer
.Host
, context
.CurrentMemberDefinition
as MemberBase
, context
.CurrentTypeParameters
, "async", MemberKind
.Class
)
470 awaiter_fields
= new Dictionary
<TypeSpec
, List
<Field
>> ();
475 public LocalVariable HoistedReturn
{
477 return hoisted_return
;
481 public TypeSpec ReturnType
{
487 public PropertySpec Task
{
495 public Field
AddAwaiter (TypeSpec type
, Location loc
)
498 type
= mutator
.Mutate (type
);
500 List
<Field
> existing_fields
= null;
501 if (awaiter_fields
.TryGetValue (type
, out existing_fields
)) {
502 foreach (var f
in existing_fields
) {
503 if (f
.IsAvailableForReuse
) {
504 f
.IsAvailableForReuse
= false;
510 var field
= AddCompilerGeneratedField ("$awaiter" + awaiters
++.ToString ("X"), new TypeExpression (type
, Location
), true);
513 if (existing_fields
== null) {
514 existing_fields
= new List
<Field
> ();
515 awaiter_fields
.Add (type
, existing_fields
);
518 existing_fields
.Add (field
);
522 public Field
AddCapturedLocalVariable (TypeSpec type
)
525 type
= mutator
.Mutate (type
);
527 List
<Field
> existing_fields
= null;
528 if (stack_fields
== null) {
529 stack_fields
= new Dictionary
<TypeSpec
, List
<Field
>> ();
530 } else if (stack_fields
.TryGetValue (type
, out existing_fields
)) {
531 foreach (var f
in existing_fields
) {
532 if (f
.IsAvailableForReuse
) {
533 f
.IsAvailableForReuse
= false;
539 var field
= AddCompilerGeneratedField ("$stack" + locals_captured
++.ToString ("X"), new TypeExpression (type
, Location
), true);
542 if (existing_fields
== null) {
543 existing_fields
= new List
<Field
> ();
544 stack_fields
.Add (type
, existing_fields
);
547 existing_fields
.Add (field
);
552 protected override bool DoDefineMembers ()
554 PredefinedType builder_type
;
555 PredefinedMember
<MethodSpec
> bf
;
556 PredefinedMember
<MethodSpec
> bs
;
557 PredefinedMember
<MethodSpec
> sr
;
558 PredefinedMember
<MethodSpec
> se
;
559 PredefinedMember
<MethodSpec
> sm
;
560 bool has_task_return_type
= false;
561 var pred_members
= Module
.PredefinedMembers
;
563 if (return_type
.Kind
== MemberKind
.Void
) {
564 builder_type
= Module
.PredefinedTypes
.AsyncVoidMethodBuilder
;
565 bf
= pred_members
.AsyncVoidMethodBuilderCreate
;
566 bs
= pred_members
.AsyncVoidMethodBuilderStart
;
567 sr
= pred_members
.AsyncVoidMethodBuilderSetResult
;
568 se
= pred_members
.AsyncVoidMethodBuilderSetException
;
569 sm
= pred_members
.AsyncVoidMethodBuilderSetStateMachine
;
570 } else if (return_type
== Module
.PredefinedTypes
.Task
.TypeSpec
) {
571 builder_type
= Module
.PredefinedTypes
.AsyncTaskMethodBuilder
;
572 bf
= pred_members
.AsyncTaskMethodBuilderCreate
;
573 bs
= pred_members
.AsyncTaskMethodBuilderStart
;
574 sr
= pred_members
.AsyncTaskMethodBuilderSetResult
;
575 se
= pred_members
.AsyncTaskMethodBuilderSetException
;
576 sm
= pred_members
.AsyncTaskMethodBuilderSetStateMachine
;
577 task
= pred_members
.AsyncTaskMethodBuilderTask
.Get ();
579 builder_type
= Module
.PredefinedTypes
.AsyncTaskMethodBuilderGeneric
;
580 bf
= pred_members
.AsyncTaskMethodBuilderGenericCreate
;
581 bs
= pred_members
.AsyncTaskMethodBuilderGenericStart
;
582 sr
= pred_members
.AsyncTaskMethodBuilderGenericSetResult
;
583 se
= pred_members
.AsyncTaskMethodBuilderGenericSetException
;
584 sm
= pred_members
.AsyncTaskMethodBuilderGenericSetStateMachine
;
585 task
= pred_members
.AsyncTaskMethodBuilderGenericTask
.Get ();
586 has_task_return_type
= true;
589 set_result
= sr
.Get ();
590 set_exception
= se
.Get ();
591 builder_factory
= bf
.Get ();
592 builder_start
= bs
.Get ();
594 var istate_machine
= Module
.PredefinedTypes
.IAsyncStateMachine
;
595 var set_statemachine
= sm
.Get ();
597 if (!builder_type
.Define () || !istate_machine
.Define () || set_result
== null || builder_factory
== null ||
598 set_exception
== null || set_statemachine
== null || builder_start
== null ||
599 !Module
.PredefinedTypes
.INotifyCompletion
.Define ()) {
600 Report
.Error (1993, Location
,
601 "Cannot find compiler required types for asynchronous functions support. Are you targeting the wrong framework version?");
602 return base.DoDefineMembers ();
605 var bt
= builder_type
.TypeSpec
;
608 // Inflate generic Task types
610 if (has_task_return_type
) {
611 var task_return_type
= return_type
.TypeArguments
;
613 task_return_type
= mutator
.Mutate (task_return_type
);
615 bt
= bt
.MakeGenericType (Module
, task_return_type
);
616 set_result
= MemberCache
.GetMember (bt
, set_result
);
617 set_exception
= MemberCache
.GetMember (bt
, set_exception
);
618 set_statemachine
= MemberCache
.GetMember (bt
, set_statemachine
);
621 task
= MemberCache
.GetMember (bt
, task
);
624 builder
= AddCompilerGeneratedField ("$builder", new TypeExpression (bt
, Location
));
626 var set_state_machine
= new Method (this, new TypeExpression (Compiler
.BuiltinTypes
.Void
, Location
),
627 Modifiers
.COMPILER_GENERATED
| Modifiers
.DEBUGGER_HIDDEN
| Modifiers
.PUBLIC
,
628 new MemberName ("SetStateMachine"),
629 ParametersCompiled
.CreateFullyResolved (
630 new Parameter (new TypeExpression (istate_machine
.TypeSpec
, Location
), "stateMachine", Parameter
.Modifier
.NONE
, null, Location
),
631 istate_machine
.TypeSpec
),
634 ToplevelBlock block
= new ToplevelBlock (Compiler
, set_state_machine
.ParameterInfo
, Location
);
635 block
.IsCompilerGenerated
= true;
636 set_state_machine
.Block
= block
;
638 Members
.Add (set_state_machine
);
640 if (!base.DoDefineMembers ())
644 // Fabricates SetStateMachine method
646 // public void SetStateMachine (IAsyncStateMachine stateMachine)
648 // $builder.SetStateMachine (stateMachine);
651 var mg
= MethodGroupExpr
.CreatePredefined (set_statemachine
, bt
, Location
);
652 mg
.InstanceExpression
= new FieldExpr (builder
, Location
);
654 var param_reference
= block
.GetParameterReference (0, Location
);
655 param_reference
.Type
= istate_machine
.TypeSpec
;
656 param_reference
.eclass
= ExprClass
.Variable
;
658 var args
= new Arguments (1);
659 args
.Add (new Argument (param_reference
));
660 set_state_machine
.Block
.AddStatement (new StatementExpression (new Invocation (mg
, args
)));
662 if (has_task_return_type
) {
663 hoisted_return
= LocalVariable
.CreateCompilerGenerated (bt
.TypeArguments
[0], StateMachineMethod
.Block
, Location
);
669 public void EmitAwaitOnCompletedDynamic (EmitContext ec
, FieldExpr awaiter
)
671 var critical
= Module
.PredefinedTypes
.ICriticalNotifyCompletion
;
672 if (!critical
.Define ()) {
673 throw new NotImplementedException ();
676 var temp_critical
= new LocalTemporary (critical
.TypeSpec
);
677 var label_critical
= ec
.DefineLabel ();
678 var label_end
= ec
.DefineLabel ();
681 // Special path for dynamic awaiters
683 // var awaiter = this.$awaiter as ICriticalNotifyCompletion;
684 // if (awaiter == null) {
685 // var completion = (INotifyCompletion) this.$awaiter;
686 // this.$builder.AwaitOnCompleted (ref completion, ref this);
688 // this.$builder.AwaitUnsafeOnCompleted (ref awaiter, ref this);
692 ec
.Emit (OpCodes
.Isinst
, critical
.TypeSpec
);
693 temp_critical
.Store (ec
);
694 temp_critical
.Emit (ec
);
695 ec
.Emit (OpCodes
.Brtrue_S
, label_critical
);
697 var temp
= new LocalTemporary (Module
.PredefinedTypes
.INotifyCompletion
.TypeSpec
);
699 ec
.Emit (OpCodes
.Castclass
, temp
.Type
);
701 EmitOnCompleted (ec
, temp
, false);
703 ec
.Emit (OpCodes
.Br_S
, label_end
);
705 ec
.MarkLabel (label_critical
);
707 EmitOnCompleted (ec
, temp_critical
, true);
709 ec
.MarkLabel (label_end
);
711 temp_critical
.Release (ec
);
714 public void EmitAwaitOnCompleted (EmitContext ec
, FieldExpr awaiter
)
716 bool unsafe_version
= false;
717 if (Module
.PredefinedTypes
.ICriticalNotifyCompletion
.Define ()) {
718 unsafe_version
= awaiter
.Type
.ImplementsInterface (Module
.PredefinedTypes
.ICriticalNotifyCompletion
.TypeSpec
, false);
721 EmitOnCompleted (ec
, awaiter
, unsafe_version
);
724 void EmitOnCompleted (EmitContext ec
, Expression awaiter
, bool unsafeVersion
)
726 var pm
= Module
.PredefinedMembers
;
727 PredefinedMember
<MethodSpec
> predefined
;
728 bool has_task_return_type
= false;
729 if (return_type
.Kind
== MemberKind
.Void
) {
730 predefined
= unsafeVersion
? pm
.AsyncVoidMethodBuilderOnCompletedUnsafe
: pm
.AsyncVoidMethodBuilderOnCompleted
;
731 } else if (return_type
== Module
.PredefinedTypes
.Task
.TypeSpec
) {
732 predefined
= unsafeVersion
? pm
.AsyncTaskMethodBuilderOnCompletedUnsafe
: pm
.AsyncTaskMethodBuilderOnCompleted
;
734 predefined
= unsafeVersion
? pm
.AsyncTaskMethodBuilderGenericOnCompletedUnsafe
: pm
.AsyncTaskMethodBuilderGenericOnCompleted
;
735 has_task_return_type
= true;
738 var on_completed
= predefined
.Resolve (Location
);
739 if (on_completed
== null)
742 if (has_task_return_type
)
743 on_completed
= MemberCache
.GetMember
<MethodSpec
> (set_result
.DeclaringType
, on_completed
);
745 on_completed
= on_completed
.MakeGenericMethod (this, awaiter
.Type
, ec
.CurrentType
);
747 var mg
= MethodGroupExpr
.CreatePredefined (on_completed
, on_completed
.DeclaringType
, Location
);
748 mg
.InstanceExpression
= new FieldExpr (builder
, Location
) {
749 InstanceExpression
= new CompilerGeneratedThis (ec
.CurrentType
, Location
)
752 // TODO: CompilerGeneratedThis is enough for structs
753 var temp_this
= new LocalTemporary (CurrentType
);
754 temp_this
.EmitAssign (ec
, new CompilerGeneratedThis (CurrentType
, Location
), false, false);
756 var args
= new Arguments (2);
757 args
.Add (new Argument (awaiter
, Argument
.AType
.Ref
));
758 args
.Add (new Argument (temp_this
, Argument
.AType
.Ref
));
759 mg
.EmitCall (ec
, args
);
761 temp_this
.Release (ec
);
764 public void EmitInitializer (EmitContext ec
)
767 // Some predefined types are missing
772 var instance
= (TemporaryVariableReference
) Instance
;
773 var builder_field
= builder
.Spec
;
774 if (MemberName
.Arity
> 0) {
775 builder_field
= MemberCache
.GetMember (instance
.Type
, builder_field
);
779 // Inflated factory method when task is of generic type
781 if (builder_factory
.DeclaringType
.IsGeneric
) {
782 var task_return_type
= return_type
.TypeArguments
;
783 var bt
= builder_factory
.DeclaringType
.MakeGenericType (Module
, task_return_type
);
784 builder_factory
= MemberCache
.GetMember (bt
, builder_factory
);
785 builder_start
= MemberCache
.GetMember (bt
, builder_start
);
789 // stateMachine.$builder = AsyncTaskMethodBuilder<{task-type}>.Create();
791 instance
.Emit (ec
); // .AddressOf (ec, AddressOp.Store);
792 ec
.Emit (OpCodes
.Call
, builder_factory
);
793 ec
.Emit (OpCodes
.Stfld
, builder_field
);
796 // stateMachine.$builder.Start<{storey-type}>(ref stateMachine);
798 instance
.Emit (ec
); //.AddressOf (ec, AddressOp.Store);
799 ec
.Emit (OpCodes
.Ldflda
, builder_field
);
801 ec
.Emit (OpCodes
.Dup
);
802 instance
.AddressOf (ec
, AddressOp
.Store
);
803 ec
.Emit (OpCodes
.Call
, builder_start
.MakeGenericMethod (Module
, instance
.Type
));
806 // Emits return stateMachine.$builder.Task;
809 var task_get
= Task
.Get
;
811 if (MemberName
.Arity
> 0) {
812 task_get
= MemberCache
.GetMember (builder_field
.MemberType
, task_get
);
815 var pe_task
= new PropertyExpr (Task
, Location
) {
816 InstanceExpression
= EmptyExpression
.Null
, // Comes from the dup above
824 public void EmitSetException (EmitContext ec
, LocalVariableReference exceptionVariable
)
827 // $builder.SetException (Exception)
829 var mg
= MethodGroupExpr
.CreatePredefined (set_exception
, set_exception
.DeclaringType
, Location
);
830 mg
.InstanceExpression
= new FieldExpr (builder
, Location
) {
831 InstanceExpression
= new CompilerGeneratedThis (ec
.CurrentType
, Location
)
834 Arguments args
= new Arguments (1);
835 args
.Add (new Argument (exceptionVariable
));
837 mg
.EmitCall (ec
, args
);
840 public void EmitSetResult (EmitContext ec
)
843 // $builder.SetResult ();
844 // $builder.SetResult<return-type> (value);
846 var mg
= MethodGroupExpr
.CreatePredefined (set_result
, set_result
.DeclaringType
, Location
);
847 mg
.InstanceExpression
= new FieldExpr (builder
, Location
) {
848 InstanceExpression
= new CompilerGeneratedThis (ec
.CurrentType
, Location
)
852 if (hoisted_return
== null) {
853 args
= new Arguments (0);
855 args
= new Arguments (1);
856 args
.Add (new Argument (new LocalVariableReference (hoisted_return
, Location
)));
859 mg
.EmitCall (ec
, args
);
862 protected override TypeSpec
[] ResolveBaseTypes (out FullNamedExpression base_class
)
864 base_type
= Compiler
.BuiltinTypes
.Object
; // ValueType;
867 var istate_machine
= Module
.PredefinedTypes
.IAsyncStateMachine
;
868 if (istate_machine
.Define ()) {
869 return new[] { istate_machine.TypeSpec }
;
876 class StackFieldExpr
: FieldExpr
, IExpressionCleanup
878 public StackFieldExpr (Field field
)
879 : base (field
, Location
.Null
)
883 public override void AddressOf (EmitContext ec
, AddressOp mode
)
885 base.AddressOf (ec
, mode
);
887 if (mode
== AddressOp
.Load
) {
888 var field
= (Field
) spec
.MemberDefinition
;
889 field
.IsAvailableForReuse
= true;
893 public override void Emit (EmitContext ec
)
897 var field
= (Field
) spec
.MemberDefinition
;
898 field
.IsAvailableForReuse
= true;
901 // Release any captured reference type stack variables
902 // to imitate real stack behavour and help GC stuff early
904 if (TypeSpec
.IsReferenceType (type
)) {
905 ec
.AddStatementEpilog (this);
909 void IExpressionCleanup
.EmitCleanup (EmitContext ec
)
911 EmitAssign (ec
, new NullConstant (type
, loc
), false, false);