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