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