public void EmitPrologue(EmitContext ec) { var fe_awaiter = new FieldExpr(awaiter, loc); fe_awaiter.InstanceExpression = new CompilerGeneratedThis(ec.CurrentType, loc); // // awaiter = expr.GetAwaiter (); // fe_awaiter.EmitAssign(ec, expr, false, false); is_completed.InstanceExpression = fe_awaiter; is_completed.EmitBranchable(ec, resume_point, true); var mg_completed = MethodGroupExpr.CreatePredefined(on_completed, fe_awaiter.Type, loc); mg_completed.InstanceExpression = fe_awaiter; var args = new Arguments(1); var storey = (AsyncTaskStorey)machine_initializer.Storey; var fe_cont = new FieldExpr(storey.Continuation, loc); fe_cont.InstanceExpression = new CompilerGeneratedThis(ec.CurrentType, loc); args.Add(new Argument(fe_cont)); // // awaiter.OnCompleted (continuation); // mg_completed.EmitCall(ec, args); base.DoEmit(ec); }
public void EmitPrologue(EmitContext ec) { var fe_awaiter = new FieldExpr(awaiter, loc); fe_awaiter.InstanceExpression = new CompilerGeneratedThis(ec.CurrentType, loc); // // awaiter = expr.GetAwaiter (); // fe_awaiter.EmitAssign(ec, expr, false, false); Label skip_continuation = ec.DefineLabel(); is_completed.InstanceExpression = fe_awaiter; is_completed.EmitBranchable(ec, skip_continuation, true); base.DoEmit(ec); FieldSpec[] stack_fields = null; TypeSpec[] stack = null; // // Here is the clever bit. We know that await statement has to yield the control // back but it can appear inside almost any expression. This means the stack can // contain any depth of values and same values have to be present when the continuation // handles control back. // // For example: await a + await b // // In this case we fabricate a static stack forwarding method which moves the values // from the stack to async storey fields. On re-entry point we restore exactly same // stack using these fields. // // We fabricate a static method because we don't want to touch original stack and // the instance method would require `this' as the first stack value on the stack // if (ec.StackHeight > 0) { var async_storey = (AsyncTaskStorey)machine_initializer.Storey; stack = ec.GetStackTypes(); var method = async_storey.GetStackForwarder(stack, out stack_fields); ec.EmitThis(); ec.Emit(OpCodes.Call, method); } var mg_completed = MethodGroupExpr.CreatePredefined(on_completed, fe_awaiter.Type, loc); mg_completed.InstanceExpression = fe_awaiter; var args = new Arguments(1); var storey = (AsyncTaskStorey)machine_initializer.Storey; var fe_cont = new FieldExpr(storey.Continuation, loc); fe_cont.InstanceExpression = new CompilerGeneratedThis(ec.CurrentType, loc); args.Add(new Argument(fe_cont)); // // awaiter.OnCompleted (continuation); // mg_completed.EmitCall(ec, args); // Return ok machine_initializer.EmitLeave(ec, unwind_protect); ec.MarkLabel(resume_point); if (stack_fields != null) { for (int i = 0; i < stack_fields.Length; ++i) { ec.EmitThis(); var field = stack_fields[i]; // // We don't store `this' because it can be easily re-created // if (field == null) { continue; } if (stack[i] is ReferenceContainer) { ec.Emit(OpCodes.Ldflda, field); } else { ec.Emit(OpCodes.Ldfld, field); } } } ec.MarkLabel(skip_continuation); }