public void EmitPrologue(EmitContext ec) { awaiter = ((AsyncTaskStorey)machine_initializer.Storey).AddAwaiter(expr.Type); var fe_awaiter = new FieldExpr(awaiter, loc); fe_awaiter.InstanceExpression = new CompilerGeneratedThis(ec.CurrentType, loc); Label skip_continuation = ec.DefineLabel(); using (ec.With(BuilderContext.Options.OmitDebugInfo, true)) { // // awaiter = expr.GetAwaiter (); // fe_awaiter.EmitAssign(ec, expr, false, false); Expression completed_expr; if (IsDynamic) { var rc = new ResolveContext(ec.MemberContext); Arguments dargs = new Arguments(1); dargs.Add(new Argument(fe_awaiter)); completed_expr = new DynamicMemberBinder("IsCompleted", dargs, loc).Resolve(rc); dargs = new Arguments(1); dargs.Add(new Argument(completed_expr)); completed_expr = new DynamicConversion(ec.Module.Compiler.BuiltinTypes.Bool, 0, dargs, loc).Resolve(rc); } else { var pe = PropertyExpr.CreatePredefined(awaiter_definition.IsCompleted, loc); pe.InstanceExpression = fe_awaiter; completed_expr = pe; } completed_expr.EmitBranchable(ec, skip_continuation, true); } base.DoEmit(ec); // // The stack has to be empty before calling await continuation. We handle this // by lifting values which would be left on stack into class fields. The process // is quite complicated and quite hard to test because any expression can possibly // leave a value on the stack. // // Following assert fails when some of expression called before is missing EmitToField // or parent expression fails to find await in children expressions // ec.AssertEmptyStack(); var storey = (AsyncTaskStorey)machine_initializer.Storey; if (IsDynamic) { storey.EmitAwaitOnCompletedDynamic(ec, fe_awaiter); } else { storey.EmitAwaitOnCompleted(ec, fe_awaiter); } // Return ok machine_initializer.EmitLeave(ec, unwind_protect); ec.MarkLabel(resume_point); ec.MarkLabel(skip_continuation); }
protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer) { if (li.Type == InternalType.Dynamic) { initializer = initializer.Resolve (bc); if (initializer == null) return null; // Once there is dynamic used defer conversion to runtime even if we know it will never succeed Arguments args = new Arguments (1); args.Add (new Argument (initializer)); initializer = new DynamicConversion (TypeManager.idisposable_type, 0, args, initializer.Location).Resolve (bc); if (initializer == null) return null; var var = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc); dispose_call = CreateDisposeCall (bc, var); dispose_call.Resolve (bc); return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc)); } if (li == Variable) { CheckIDiposableConversion (bc, li, initializer); dispose_call = CreateDisposeCall (bc, li); dispose_call.Resolve (bc); } return base.ResolveInitializer (bc, li, initializer); }