Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.ExtLibs/HeuristicLab.NRefactory/5.5.0/NRefactory.CSharp-5.5.0/Parser/mcs/async.cs @ 13397

Last change on this file since 13397 was 11700, checked in by jkarder, 9 years ago

#2077: created branch and added first version

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