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