22e4c1b4b8c3efd233b937c9de66f7ad722b6ca8
[mono.git] / mcs / mcs / async.cs
1 //
2 // async.cs: Asynchronous functions
3 //
4 // Author:
5 //   Marek Safar (marek.safar@gmail.com)
6 //
7 // Dual licensed under the terms of the MIT X11 or GNU GPL
8 //
9 // Copyright 2011 Novell, Inc.
10 // Copyright 2011-2012 Xamarin Inc.
11 //
12
13 using System;
14 using System.Collections.Generic;
15 using System.Linq;
16 using System.Collections;
17
18 #if STATIC
19 using IKVM.Reflection;
20 using IKVM.Reflection.Emit;
21 #else
22 using System.Reflection;
23 using System.Reflection.Emit;
24 #endif
25
26 namespace Mono.CSharp
27 {
28         public class Await : ExpressionStatement
29         {
30                 Expression expr;
31                 AwaitStatement stmt;
32
33                 public Await (Expression expr, Location loc)
34                 {
35                         this.expr = expr;
36                         this.loc = loc;
37                 }
38
39                 public Expression Expr {
40                         get {
41                                 return expr;
42                         }
43                 }
44
45                 public AwaitStatement Statement {
46                         get {
47                                 return stmt;
48                         }
49                 }
50
51                 protected override void CloneTo (CloneContext clonectx, Expression target)
52                 {
53                         var t = (Await) target;
54
55                         t.expr = expr.Clone (clonectx);
56                 }
57
58                 public override Expression CreateExpressionTree (ResolveContext ec)
59                 {
60                         throw new NotImplementedException ("ET");
61                 }
62
63                 public override bool ContainsEmitWithAwait ()
64                 {
65                         return true;
66                 }
67
68                 public override void FlowAnalysis (FlowAnalysisContext fc)
69                 {
70                         stmt.Expr.FlowAnalysis (fc);
71
72                         stmt.RegisterResumePoint ();
73                 }
74
75                 protected override Expression DoResolve (ResolveContext rc)
76                 {
77                         if (rc.HasSet (ResolveContext.Options.LockScope)) {
78                                 rc.Report.Error (1996, loc,
79                                         "The `await' operator cannot be used in the body of a lock statement");
80                         }
81
82                         if (rc.IsUnsafe) {
83                                 rc.Report.Error (4004, loc,
84                                         "The `await' operator cannot be used in an unsafe context");
85                         }
86
87                         var bc = (BlockContext) rc;
88
89                         stmt = new AwaitStatement (expr, loc);
90                         if (!stmt.Resolve (bc))
91                                 return null;
92
93                         if (rc.HasSet (ResolveContext.Options.FinallyScope) && rc.CurrentAnonymousMethod != null) {
94                                 var ats = (AsyncTaskStorey)rc.CurrentAnonymousMethod.Storey;
95                                 ats.HasAwaitInsideFinally = true;
96                         }
97
98                         type = stmt.ResultType;
99                         eclass = ExprClass.Variable;
100                         return this;
101                 }
102
103                 public override void Emit (EmitContext ec)
104                 {
105                         stmt.EmitPrologue (ec);
106
107                         using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
108                                 stmt.Emit (ec);
109                         }
110                 }
111                 
112                 public override Expression EmitToField (EmitContext ec)
113                 {
114                         stmt.EmitPrologue (ec);
115                         return stmt.GetResultExpression (ec);
116                 }
117                 
118                 public void EmitAssign (EmitContext ec, FieldExpr field)
119                 {
120                         stmt.EmitPrologue (ec);
121                         field.InstanceExpression.Emit (ec);
122                         stmt.Emit (ec);
123                 }
124
125                 public override void EmitStatement (EmitContext ec)
126                 {
127                         stmt.EmitStatement (ec);
128                 }
129
130                 public override void MarkReachable (Reachability rc)
131                 {
132                         base.MarkReachable (rc);
133                         stmt.MarkReachable (rc);
134                 }
135
136                 public override object Accept (StructuralVisitor visitor)
137                 {
138                         return visitor.Visit (this);
139                 }
140         }
141
142         public class AwaitStatement : YieldStatement<AsyncInitializer>
143         {
144                 public sealed class AwaitableMemberAccess : MemberAccess
145                 {
146                         public AwaitableMemberAccess (Expression expr)
147                                 : base (expr, "GetAwaiter")
148                         {
149                         }
150
151                         public bool ProbingMode { get; set; }
152
153                         public override void Error_TypeDoesNotContainDefinition (ResolveContext rc, TypeSpec type, string name)
154                         {
155                                 Error_OperatorCannotBeApplied (rc, type);
156                         }
157
158                         protected override void Error_OperatorCannotBeApplied (ResolveContext rc, TypeSpec type)
159                         {
160                                 if (ProbingMode)
161                                         return;
162
163                                 var invocation = LeftExpression as Invocation;
164                                 if (invocation != null && invocation.MethodGroup != null && (invocation.MethodGroup.BestCandidate.Modifiers & Modifiers.ASYNC) != 0) {
165                                         rc.Report.Error (4008, loc, "Cannot await void method `{0}'. Consider changing method return type to `Task'",
166                                                 invocation.GetSignatureForError ());
167                                 } else if (type != InternalType.ErrorType) {
168                                         rc.Report.Error (4001, loc, "Cannot await `{0}' expression", type.GetSignatureForError ());
169                                 }
170                         }
171                 }
172
173                 sealed class GetResultInvocation : Invocation
174                 {
175                         public GetResultInvocation (MethodGroupExpr mge, Arguments arguments)
176                                 : base (null, arguments)
177                         {
178                                 mg = mge;
179                                 type = mg.BestCandidateReturnType;
180                         }
181
182                         public override Expression EmitToField (EmitContext ec)
183                         {
184                                 return this;
185                         }
186                 }
187
188                 Field awaiter;
189                 AwaiterDefinition awaiter_definition;
190                 TypeSpec type;
191                 TypeSpec result_type;
192
193                 public AwaitStatement (Expression expr, Location loc)
194                         : base (expr, loc)
195                 {
196                         unwind_protect = true;
197                 }
198
199                 #region Properties
200
201                 bool IsDynamic {
202                         get {
203                                 return awaiter_definition == null;
204                         }
205                 }
206
207                 public TypeSpec ResultType {
208                         get {
209                                 return result_type;
210                         }
211                 }
212
213                 #endregion
214
215                 protected override void DoEmit (EmitContext ec)
216                 {
217                         using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
218                                 GetResultExpression (ec).Emit (ec);
219                         }
220                 }
221
222                 public Expression GetResultExpression (EmitContext ec)
223                 {
224                         var fe_awaiter = new FieldExpr (awaiter, loc);
225                         fe_awaiter.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
226
227                         //
228                         // result = awaiter.GetResult ();
229                         //
230                         if (IsDynamic) {
231                                 var rc = new ResolveContext (ec.MemberContext);
232                                 return new Invocation (new MemberAccess (fe_awaiter, "GetResult"), new Arguments (0)).Resolve (rc);
233                         }
234                         
235                         var mg_result = MethodGroupExpr.CreatePredefined (awaiter_definition.GetResult, fe_awaiter.Type, loc);
236                         mg_result.InstanceExpression = fe_awaiter;
237
238                         return new GetResultInvocation (mg_result, new Arguments (0));
239                 }
240
241                 public void EmitPrologue (EmitContext ec)
242                 {
243                         awaiter = ((AsyncTaskStorey) machine_initializer.Storey).AddAwaiter (expr.Type);
244
245                         var fe_awaiter = new FieldExpr (awaiter, loc);
246                         fe_awaiter.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
247
248                         Label skip_continuation = ec.DefineLabel ();
249
250                         using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
251                                 //
252                                 // awaiter = expr.GetAwaiter ();
253                                 //
254                                 fe_awaiter.EmitAssign (ec, expr, false, false);
255
256                                 Expression completed_expr;
257                                 if (IsDynamic) {
258                                         var rc = new ResolveContext (ec.MemberContext);
259
260                                         Arguments dargs = new Arguments (1);
261                                         dargs.Add (new Argument (fe_awaiter));
262                                         completed_expr = new DynamicMemberBinder ("IsCompleted", dargs, loc).Resolve (rc);
263
264                                         dargs = new Arguments (1);
265                                         dargs.Add (new Argument (completed_expr));
266                                         completed_expr = new DynamicConversion (ec.Module.Compiler.BuiltinTypes.Bool, 0, dargs, loc).Resolve (rc);
267                                 } else {
268                                         var pe = PropertyExpr.CreatePredefined (awaiter_definition.IsCompleted, loc);
269                                         pe.InstanceExpression = fe_awaiter;
270                                         completed_expr = pe;
271                                 }
272
273                                 completed_expr.EmitBranchable (ec, skip_continuation, true);
274                         }
275
276                         base.DoEmit (ec);
277
278                         //
279                         // The stack has to be empty before calling await continuation. We handle this
280                         // by lifting values which would be left on stack into class fields. The process
281                         // is quite complicated and quite hard to test because any expression can possibly
282                         // leave a value on the stack.
283                         //
284                         // Following assert fails when some of expression called before is missing EmitToField
285                         // or parent expression fails to find await in children expressions
286                         //
287                         ec.AssertEmptyStack ();
288
289                         var storey = (AsyncTaskStorey) machine_initializer.Storey;
290                         if (IsDynamic) {
291                                 storey.EmitAwaitOnCompletedDynamic (ec, fe_awaiter);
292                         } else {
293                                 storey.EmitAwaitOnCompleted (ec, fe_awaiter);
294                         }
295
296                         // Return ok
297                         machine_initializer.EmitLeave (ec, unwind_protect);
298
299                         ec.MarkLabel (resume_point);
300                         ec.MarkLabel (skip_continuation);
301                 }
302
303                 public void EmitStatement (EmitContext ec)
304                 {
305                         EmitPrologue (ec);
306                         DoEmit (ec);
307
308                         awaiter.IsAvailableForReuse = true;
309
310                         if (ResultType.Kind != MemberKind.Void)
311                                 ec.Emit (OpCodes.Pop);
312                 }
313
314                 void Error_WrongAwaiterPattern (ResolveContext rc, TypeSpec awaiter)
315                 {
316                         rc.Report.Error (4011, loc, "The awaiter type `{0}' must have suitable IsCompleted and GetResult members",
317                                 awaiter.GetSignatureForError ());
318                 }
319
320                 public override bool Resolve (BlockContext bc)
321                 {
322                         if (bc.CurrentBlock is Linq.QueryBlock) {
323                                 bc.Report.Error (1995, loc,
324                                         "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");
325                                 return false;
326                         }
327
328                         if (!base.Resolve (bc))
329                                 return false;
330
331                         type = expr.Type;
332                         Arguments args = new Arguments (0);
333
334                         //
335                         // The await expression is of dynamic type
336                         //
337                         if (type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
338                                 result_type = type;
339                                 expr = new Invocation (new MemberAccess (expr, "GetAwaiter"), args).Resolve (bc);
340                                 return true;
341                         }
342
343                         //
344                         // Check whether the expression is awaitable
345                         //
346                         Expression ama = new AwaitableMemberAccess (expr).Resolve (bc);
347                         if (ama == null)
348                                 return false;
349
350                         var errors_printer = new SessionReportPrinter ();
351                         var old = bc.Report.SetPrinter (errors_printer);
352                         ama = new Invocation (ama, args).Resolve (bc);
353                         bc.Report.SetPrinter (old);
354
355                         if (errors_printer.ErrorsCount > 0 || !MemberAccess.IsValidDotExpression (ama.Type)) {
356                                 bc.Report.Error (1986, expr.Location,
357                                         "The `await' operand type `{0}' must have suitable GetAwaiter method",
358                                         expr.Type.GetSignatureForError ());
359
360                                 return false;
361                         }
362
363                         var awaiter_type = ama.Type;
364
365                         awaiter_definition = bc.Module.GetAwaiter (awaiter_type);
366
367                         if (!awaiter_definition.IsValidPattern) {
368                                 Error_WrongAwaiterPattern (bc, awaiter_type);
369                                 return false;
370                         }
371
372                         if (!awaiter_definition.INotifyCompletion) {
373                                 bc.Report.Error (4027, loc, "The awaiter type `{0}' must implement interface `{1}'",
374                                         awaiter_type.GetSignatureForError (), bc.Module.PredefinedTypes.INotifyCompletion.GetSignatureForError ());
375                                 return false;
376                         }
377
378                         expr = ama;
379                         result_type = awaiter_definition.GetResult.ReturnType;
380
381                         return true;
382                 }
383         }
384
385         class AsyncInitializerStatement : StatementExpression
386         {
387                 public AsyncInitializerStatement (AsyncInitializer expr)
388                         : base (expr)
389                 {
390                 }
391
392                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
393                 {
394                         base.DoFlowAnalysis (fc);
395
396                         var init = (AsyncInitializer) Expr;
397                         var res = !init.Block.HasReachableClosingBrace;
398                         var storey = (AsyncTaskStorey) init.Storey;
399
400                         if (storey.ReturnType.IsGenericTask)
401                                 return res;
402
403                         return true;
404                 }
405
406                 public override Reachability MarkReachable (Reachability rc)
407                 {
408                         if (!rc.IsUnreachable)
409                                 reachable = true;
410
411                         var init = (AsyncInitializer) Expr;
412                         rc = init.Block.MarkReachable (rc);
413
414                         var storey = (AsyncTaskStorey) init.Storey;
415
416                         //
417                         // Explicit return is required for Task<T> state machine
418                         //
419                         if (storey.ReturnType != null && storey.ReturnType.IsGenericTask)
420                                 return rc;
421
422                     return Reachability.CreateUnreachable ();
423                 }
424         }
425
426         public class AsyncInitializer : StateMachineInitializer
427         {
428                 TypeInferenceContext return_inference;
429
430                 public AsyncInitializer (ParametersBlock block, TypeDefinition host, TypeSpec returnType)
431                         : base (block, host, returnType)
432                 {
433                 }
434
435                 #region Properties
436
437                 public override string ContainerType {
438                         get {
439                                 return "async state machine block";
440                         }
441                 }
442
443                 public TypeSpec DelegateType {
444                         get; set;
445                 }
446
447                 public StackFieldExpr HoistedReturnState {
448                         get; set;
449                 }
450
451                 public override bool IsIterator {
452                         get {
453                                 return false;
454                         }
455                 }
456
457                 public TypeInferenceContext ReturnTypeInference {
458                         get {
459                                 return return_inference;
460                         }
461                 }
462
463                 #endregion
464
465                 protected override BlockContext CreateBlockContext (BlockContext bc)
466                 {
467                         var ctx = base.CreateBlockContext (bc);
468                         var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
469                         if (am != null)
470                                 return_inference = am.ReturnTypeInference;
471
472                         ctx.Set (ResolveContext.Options.TryScope);
473
474                         return ctx;
475                 }
476
477                 public override void Emit (EmitContext ec)
478                 {
479                         throw new NotImplementedException ();
480                 }
481
482                 public void EmitCatchBlock (EmitContext ec)
483                 {
484                         var catch_value = LocalVariable.CreateCompilerGenerated (ec.Module.Compiler.BuiltinTypes.Exception, block, Location);
485
486                         ec.BeginCatchBlock (catch_value.Type);
487                         catch_value.EmitAssign (ec);
488
489                         ec.EmitThis ();
490                         ec.EmitInt ((int) IteratorStorey.State.After);
491                         ec.Emit (OpCodes.Stfld, storey.PC.Spec);
492
493                         ((AsyncTaskStorey) Storey).EmitSetException (ec, new LocalVariableReference (catch_value, Location));
494
495                         ec.Emit (OpCodes.Leave, move_next_ok);
496                         ec.EndExceptionBlock ();
497
498                 }
499
500                 protected override void EmitMoveNextEpilogue (EmitContext ec)
501                 {
502                         var storey = (AsyncTaskStorey) Storey;
503                         storey.EmitSetResult (ec);
504                 }
505
506                 public override void EmitStatement (EmitContext ec)
507                 {
508                         var storey = (AsyncTaskStorey) Storey;
509                         storey.EmitInitializer (ec);
510                         ec.Emit (OpCodes.Ret);
511                 }
512
513                 public override void MarkReachable (Reachability rc)
514                 {
515                         //
516                         // Reachability has been done in AsyncInitializerStatement
517                         //
518                 }
519         }
520
521         class AsyncTaskStorey : StateMachine
522         {
523                 int awaiters;
524                 Field builder;
525                 readonly TypeSpec return_type;
526                 MethodSpec set_result;
527                 MethodSpec set_exception;
528                 MethodSpec builder_factory;
529                 MethodSpec builder_start;
530                 PropertySpec task;
531                 int locals_captured;
532                 Dictionary<TypeSpec, List<Field>> stack_fields;
533                 Dictionary<TypeSpec, List<Field>> awaiter_fields;
534
535                 public AsyncTaskStorey (ParametersBlock block, IMemberContext context, AsyncInitializer initializer, TypeSpec type)
536                         : base (block, initializer.Host, context.CurrentMemberDefinition as MemberBase, context.CurrentTypeParameters, "async", MemberKind.Struct)
537                 {
538                         return_type = type;
539                         awaiter_fields = new Dictionary<TypeSpec, List<Field>> ();
540                 }
541
542                 #region Properties
543
544                 public bool HasAwaitInsideFinally { get; set; }
545
546                 public Expression HoistedReturnValue { get; set; }
547
548                 public TypeSpec ReturnType {
549                         get {
550                                 return return_type;
551                         }
552                 }
553
554                 public PropertySpec Task {
555                         get {
556                                 return task;
557                         }
558                 }
559
560                 protected override TypeAttributes TypeAttr {
561                         get {
562                                 return base.TypeAttr & ~TypeAttributes.SequentialLayout;
563                         }
564                 }
565
566                 #endregion
567
568                 public Field AddAwaiter (TypeSpec type)
569                 {
570                         if (mutator != null)
571                                 type = mutator.Mutate (type);
572
573                         List<Field> existing_fields;
574                         if (awaiter_fields.TryGetValue (type, out existing_fields)) {
575                                 foreach (var f in existing_fields) {
576                                         if (f.IsAvailableForReuse) {
577                                                 f.IsAvailableForReuse = false;
578                                                 return f;
579                                         }
580                                 }
581                         }
582
583                         var field = AddCompilerGeneratedField ("$awaiter" + awaiters++.ToString ("X"), new TypeExpression (type, Location), true);
584                         field.Define ();
585
586                         if (existing_fields == null) {
587                                 existing_fields = new List<Field> ();
588                                 awaiter_fields.Add (type, existing_fields);
589                         }
590
591                         existing_fields.Add (field);
592                         return field;
593                 }
594
595                 public Field AddCapturedLocalVariable (TypeSpec type, bool requiresUninitialized = false)
596                 {
597                         if (mutator != null)
598                                 type = mutator.Mutate (type);
599
600                         List<Field> existing_fields = null;
601                         if (stack_fields == null) {
602                                 stack_fields = new Dictionary<TypeSpec, List<Field>> ();
603                         } else if (stack_fields.TryGetValue (type, out existing_fields) && !requiresUninitialized) {
604                                 foreach (var f in existing_fields) {
605                                         if (f.IsAvailableForReuse) {
606                                                 f.IsAvailableForReuse = false;
607                                                 return f;
608                                         }
609                                 }
610                         }
611
612                         var field = AddCompilerGeneratedField ("$stack" + locals_captured++.ToString ("X"), new TypeExpression (type, Location), true);
613                         field.Define ();
614
615                         if (existing_fields == null) {
616                                 existing_fields = new List<Field> ();
617                                 stack_fields.Add (type, existing_fields);
618                         }
619
620                         existing_fields.Add (field);
621
622                         return field;
623                 }
624
625                 protected override bool DoDefineMembers ()
626                 {
627                         PredefinedType builder_type;
628                         PredefinedMember<MethodSpec> bf;
629                         PredefinedMember<MethodSpec> bs;
630                         PredefinedMember<MethodSpec> sr;
631                         PredefinedMember<MethodSpec> se;
632                         PredefinedMember<MethodSpec> sm;
633                         bool has_task_return_type = false;
634                         var pred_members = Module.PredefinedMembers;
635
636                         if (return_type.Kind == MemberKind.Void) {
637                                 builder_type = Module.PredefinedTypes.AsyncVoidMethodBuilder;
638                                 bf = pred_members.AsyncVoidMethodBuilderCreate;
639                                 bs = pred_members.AsyncVoidMethodBuilderStart;
640                                 sr = pred_members.AsyncVoidMethodBuilderSetResult;
641                                 se = pred_members.AsyncVoidMethodBuilderSetException;
642                                 sm = pred_members.AsyncVoidMethodBuilderSetStateMachine;
643                         } else if (return_type == Module.PredefinedTypes.Task.TypeSpec) {
644                                 builder_type = Module.PredefinedTypes.AsyncTaskMethodBuilder;
645                                 bf = pred_members.AsyncTaskMethodBuilderCreate;
646                                 bs = pred_members.AsyncTaskMethodBuilderStart;
647                                 sr = pred_members.AsyncTaskMethodBuilderSetResult;
648                                 se = pred_members.AsyncTaskMethodBuilderSetException;
649                                 sm = pred_members.AsyncTaskMethodBuilderSetStateMachine;
650                                 task = pred_members.AsyncTaskMethodBuilderTask.Get ();
651                         } else {
652                                 builder_type = Module.PredefinedTypes.AsyncTaskMethodBuilderGeneric;
653                                 bf = pred_members.AsyncTaskMethodBuilderGenericCreate;
654                                 bs = pred_members.AsyncTaskMethodBuilderGenericStart;
655                                 sr = pred_members.AsyncTaskMethodBuilderGenericSetResult;
656                                 se = pred_members.AsyncTaskMethodBuilderGenericSetException;
657                                 sm = pred_members.AsyncTaskMethodBuilderGenericSetStateMachine;
658                                 task = pred_members.AsyncTaskMethodBuilderGenericTask.Get ();
659                                 has_task_return_type = true;
660                         }
661
662                         set_result = sr.Get ();
663                         set_exception = se.Get ();
664                         builder_factory = bf.Get ();
665                         builder_start = bs.Get ();
666
667                         var istate_machine = Module.PredefinedTypes.IAsyncStateMachine;
668                         var set_statemachine = sm.Get ();
669
670                         if (!builder_type.Define () || !istate_machine.Define () || set_result == null || builder_factory == null ||
671                                 set_exception == null || set_statemachine == null || builder_start == null ||
672                                 !Module.PredefinedTypes.INotifyCompletion.Define ()) {
673                                 Report.Error (1993, Location,
674                                         "Cannot find compiler required types for asynchronous functions support. Are you targeting the wrong framework version?");
675                                 return base.DoDefineMembers ();
676                         }
677
678                         var bt = builder_type.TypeSpec;
679
680                         //
681                         // Inflate generic Task types
682                         //
683                         if (has_task_return_type) {
684                                 var task_return_type = return_type.TypeArguments;
685                                 if (mutator != null)
686                                         task_return_type = mutator.Mutate (task_return_type);
687
688                                 bt = bt.MakeGenericType (Module, task_return_type);
689                                 set_result = MemberCache.GetMember (bt, set_result);
690                                 set_exception = MemberCache.GetMember (bt, set_exception);
691                                 set_statemachine = MemberCache.GetMember (bt, set_statemachine);
692
693                                 if (task != null)
694                                         task = MemberCache.GetMember (bt, task);
695                         }
696
697                         builder = AddCompilerGeneratedField ("$builder", new TypeExpression (bt, Location));
698
699                         Field rfield;
700                         if (has_task_return_type && HasAwaitInsideFinally) {
701                                 //
702                                 // Special case async block with return value from finally clause. In such case
703                                 // we rewrite all return expresison stores to stfld to $return. Instead of treating
704                                 // returns outside of finally and inside of finally differently.
705                                 //
706                                 rfield = AddCompilerGeneratedField ("$return", new TypeExpression (bt.TypeArguments [0], Location));
707                         } else {
708                                 rfield = null;
709                         }
710
711                         var set_state_machine = new Method (this, new TypeExpression (Compiler.BuiltinTypes.Void, Location),
712                                 Modifiers.COMPILER_GENERATED | Modifiers.DEBUGGER_HIDDEN | Modifiers.PUBLIC,
713                                 new MemberName ("SetStateMachine"),
714                                 ParametersCompiled.CreateFullyResolved (
715                                         new Parameter (new TypeExpression (istate_machine.TypeSpec, Location), "stateMachine", Parameter.Modifier.NONE, null, Location),
716                                         istate_machine.TypeSpec),
717                                 null);
718
719                         ToplevelBlock block = new ToplevelBlock (Compiler, set_state_machine.ParameterInfo, Location);
720                         block.IsCompilerGenerated = true;
721                         set_state_machine.Block = block;
722
723                         Members.Add (set_state_machine);
724
725                         if (!base.DoDefineMembers ())
726                                 return false;
727
728                         //
729                         // Fabricates SetStateMachine method
730                         //
731                         // public void SetStateMachine (IAsyncStateMachine stateMachine)
732                         // {
733                         //    $builder.SetStateMachine (stateMachine);
734                         // }
735                         //
736                         var mg = MethodGroupExpr.CreatePredefined (set_statemachine, bt, Location);
737                         mg.InstanceExpression = new FieldExpr (builder, Location);
738
739                         var param_reference = block.GetParameterReference (0, Location);
740                         param_reference.Type = istate_machine.TypeSpec;
741                         param_reference.eclass = ExprClass.Variable;
742
743                         var args = new Arguments (1);
744                         args.Add (new Argument (param_reference));
745                         set_state_machine.Block.AddStatement (new StatementExpression (new Invocation (mg, args)));
746
747                         if (has_task_return_type) {
748                                 if (rfield != null) {
749                                         HoistedReturnValue = new FieldExpr (rfield, Location) {
750                                                 InstanceExpression = new CompilerGeneratedThis (CurrentType, Location.Null)
751                                         };
752                                 } else {
753                                         HoistedReturnValue = TemporaryVariableReference.Create (bt.TypeArguments [0], StateMachineMethod.Block, Location);
754                                 }
755                         }
756
757                         return true;
758                 }
759
760                 public void EmitAwaitOnCompletedDynamic (EmitContext ec, FieldExpr awaiter)
761                 {
762                         var critical = Module.PredefinedTypes.ICriticalNotifyCompletion;
763                         if (!critical.Define ()) {
764                                 throw new NotImplementedException ();
765                         }
766
767                         var temp_critical = new LocalTemporary (critical.TypeSpec);
768                         var label_critical = ec.DefineLabel ();
769                         var label_end = ec.DefineLabel ();
770
771                         //
772                         // Special path for dynamic awaiters
773                         //
774                         // var awaiter = this.$awaiter as ICriticalNotifyCompletion;
775                         // if (awaiter == null) {
776                         //    var completion = (INotifyCompletion) this.$awaiter;
777                         //    this.$builder.AwaitOnCompleted (ref completion, ref this);
778                         // } else {
779                         //    this.$builder.AwaitUnsafeOnCompleted (ref awaiter, ref this);
780                         // }
781                         //
782                         awaiter.Emit (ec);
783                         ec.Emit (OpCodes.Isinst, critical.TypeSpec);
784                         temp_critical.Store (ec);
785                         temp_critical.Emit (ec);
786                         ec.Emit (OpCodes.Brtrue_S, label_critical);
787
788                         var temp = new LocalTemporary (Module.PredefinedTypes.INotifyCompletion.TypeSpec);
789                         awaiter.Emit (ec);
790                         ec.Emit (OpCodes.Castclass, temp.Type);
791                         temp.Store (ec);
792                         EmitOnCompleted (ec, temp, false);
793                         temp.Release (ec);
794                         ec.Emit (OpCodes.Br_S, label_end);
795
796                         ec.MarkLabel (label_critical);
797
798                         EmitOnCompleted (ec, temp_critical, true);
799
800                         ec.MarkLabel (label_end);
801
802                         temp_critical.Release (ec);
803                 }
804
805                 public void EmitAwaitOnCompleted (EmitContext ec, FieldExpr awaiter)
806                 {
807                         bool unsafe_version = false;
808                         if (Module.PredefinedTypes.ICriticalNotifyCompletion.Define ()) {
809                                 unsafe_version = awaiter.Type.ImplementsInterface (Module.PredefinedTypes.ICriticalNotifyCompletion.TypeSpec, false);
810                         }
811
812                         EmitOnCompleted (ec, awaiter, unsafe_version);
813                 }
814
815                 void EmitOnCompleted (EmitContext ec, Expression awaiter, bool unsafeVersion)
816                 {
817                         var pm = Module.PredefinedMembers;
818                         PredefinedMember<MethodSpec> predefined;
819                         bool has_task_return_type = false;
820                         if (return_type.Kind == MemberKind.Void) {
821                                 predefined = unsafeVersion ? pm.AsyncVoidMethodBuilderOnCompletedUnsafe : pm.AsyncVoidMethodBuilderOnCompleted;
822                         } else if (return_type == Module.PredefinedTypes.Task.TypeSpec) {
823                                 predefined = unsafeVersion ? pm.AsyncTaskMethodBuilderOnCompletedUnsafe : pm.AsyncTaskMethodBuilderOnCompleted;
824                         } else {
825                                 predefined = unsafeVersion ? pm.AsyncTaskMethodBuilderGenericOnCompletedUnsafe : pm.AsyncTaskMethodBuilderGenericOnCompleted;
826                                 has_task_return_type = true;
827                         }
828
829                         var on_completed = predefined.Resolve (Location);
830                         if (on_completed == null)
831                                 return;
832
833                         if (has_task_return_type)
834                                 on_completed = MemberCache.GetMember<MethodSpec> (set_result.DeclaringType, on_completed);
835
836                         on_completed = on_completed.MakeGenericMethod (this, awaiter.Type, ec.CurrentType);
837
838                         var mg = MethodGroupExpr.CreatePredefined (on_completed, on_completed.DeclaringType, Location);
839                         mg.InstanceExpression = new FieldExpr (builder, Location) {
840                                 InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location)
841                         };
842
843                         var args = new Arguments (2);
844                         args.Add (new Argument (awaiter, Argument.AType.Ref));
845                         args.Add (new Argument (new CompilerGeneratedThis (CurrentType, Location), Argument.AType.Ref));
846                         using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
847                                 mg.EmitCall (ec, args, true);
848                         }
849                 }
850
851                 public void EmitInitializer (EmitContext ec)
852                 {
853                         //
854                         // Some predefined types are missing
855                         //
856                         if (builder == null)
857                                 return;
858
859                         var instance = (TemporaryVariableReference) Instance;
860                         var builder_field = builder.Spec;
861                         if (MemberName.Arity > 0) {
862                                 builder_field = MemberCache.GetMember (instance.Type, builder_field);
863                         }
864
865                         //
866                         // Inflated factory method when task is of generic type
867                         //
868                         if (builder_factory.DeclaringType.IsGeneric) {
869                                 var task_return_type = return_type.TypeArguments;
870                                 var bt = builder_factory.DeclaringType.MakeGenericType (Module, task_return_type);
871                                 builder_factory = MemberCache.GetMember (bt, builder_factory);
872                                 builder_start = MemberCache.GetMember (bt, builder_start);
873                         }
874
875                         //
876                         // stateMachine.$builder = AsyncTaskMethodBuilder<{task-type}>.Create();
877                         //
878                         instance.AddressOf (ec, AddressOp.Store);
879                         ec.Emit (OpCodes.Call, builder_factory);
880                         ec.Emit (OpCodes.Stfld, builder_field);
881
882                         //
883                         // stateMachine.$builder.Start<{storey-type}>(ref stateMachine);
884                         //
885                         instance.AddressOf (ec, AddressOp.Store);
886                         ec.Emit (OpCodes.Ldflda, builder_field);
887                         if (Task != null)
888                                 ec.Emit (OpCodes.Dup);
889                         instance.AddressOf (ec, AddressOp.Store);
890                         ec.Emit (OpCodes.Call, builder_start.MakeGenericMethod (Module, instance.Type));
891
892                         //
893                         // Emits return stateMachine.$builder.Task;
894                         //
895                         if (Task != null) {
896                                 var task_get = Task.Get;
897
898                                 if (MemberName.Arity > 0) {
899                                         task_get = MemberCache.GetMember (builder_field.MemberType, task_get);
900                                 }
901
902                                 var pe_task = new PropertyExpr (Task, Location) {
903                                         InstanceExpression = EmptyExpression.Null,      // Comes from the dup above
904                                         Getter = task_get
905                                 };
906
907                                 pe_task.Emit (ec);
908                         }
909                 }
910
911                 public void EmitSetException (EmitContext ec, LocalVariableReference exceptionVariable)
912                 {
913                         //
914                         // $builder.SetException (Exception)
915                         //
916                         var mg = MethodGroupExpr.CreatePredefined (set_exception, set_exception.DeclaringType, Location);
917                         mg.InstanceExpression = new FieldExpr (builder, Location) {
918                                 InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location)
919                         };
920
921                         Arguments args = new Arguments (1);
922                         args.Add (new Argument (exceptionVariable));
923
924                         using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
925                                 mg.EmitCall (ec, args, true);
926                         }
927                 }
928
929                 public void EmitSetResult (EmitContext ec)
930                 {
931                         //
932                         // $builder.SetResult ();
933                         // $builder.SetResult<return-type> (value);
934                         //
935                         var mg = MethodGroupExpr.CreatePredefined (set_result, set_result.DeclaringType, Location);
936                         mg.InstanceExpression = new FieldExpr (builder, Location) {
937                                 InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, Location)
938                         };
939
940                         Arguments args;
941                         if (HoistedReturnValue == null) {
942                                 args = new Arguments (0);
943                         } else {
944                                 args = new Arguments (1);
945                                 args.Add (new Argument (HoistedReturnValue));
946                         }
947
948                         using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
949                                 mg.EmitCall (ec, args, true);
950                         }
951                 }
952
953                 protected override TypeSpec[] ResolveBaseTypes (out FullNamedExpression base_class)
954                 {
955                         base_type = Compiler.BuiltinTypes.ValueType;
956                         base_class = null;
957
958                         var istate_machine = Module.PredefinedTypes.IAsyncStateMachine;
959                         if (istate_machine.Define ()) {
960                                 return new[] { istate_machine.TypeSpec };
961                         }
962
963                         return null;
964                 }
965         }
966
967         public class StackFieldExpr : FieldExpr, IExpressionCleanup
968         {
969                 public StackFieldExpr (Field field)
970                         : base (field, Location.Null)
971                 {
972                 }
973
974                 public bool IsAvailableForReuse {
975                         get {
976                                 var field = (Field) spec.MemberDefinition;
977                                 return field.IsAvailableForReuse;
978                         }
979                         set {
980                                 var field = (Field) spec.MemberDefinition;
981                                 field.IsAvailableForReuse = value;
982                         }
983                 }
984
985                 public override void AddressOf (EmitContext ec, AddressOp mode)
986                 {
987                         base.AddressOf (ec, mode);
988
989                         if (mode == AddressOp.Load) {
990                                 IsAvailableForReuse = true;
991                         }
992                 }
993
994                 public override void Emit (EmitContext ec)
995                 {
996                         base.Emit (ec);
997
998                         PrepareCleanup (ec);
999                 }
1000
1001                 public void EmitLoad (EmitContext ec)
1002                 {
1003                         base.Emit (ec);
1004                 }
1005
1006                 public void PrepareCleanup (EmitContext ec)
1007                 {
1008                         IsAvailableForReuse = true;
1009
1010                         //
1011                         // Release any captured reference type stack variables
1012                         // to imitate real stack behavour and help GC stuff early
1013                         //
1014                         if (TypeSpec.IsReferenceType (type)) {
1015                                 ec.AddStatementEpilog (this);
1016                         }
1017                 }
1018
1019                 void IExpressionCleanup.EmitCleanup (EmitContext ec)
1020                 {
1021                         EmitAssign (ec, new NullConstant (type, loc), false, false);
1022                 }
1023         }
1024 }