Example #1
0
        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);
        }
Example #2
0
        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);
        }