Esempio n. 1
0
        private Expression ReduceCore()
        {
            //
            // First, check to see whether await expressions occur in forbidden places such as the body
            // of a lock statement or a filter of a catch clause. This is done using a C# visitor so we
            // can analyze the original (non-reduced) intent of the nodes.
            //
            AwaitChecker.Check(Body);

            const int ExprCount = 1 /* new builder */ + 1 /* new state machine */ + 1 /* initial state */ + 1 /* start state machine */;

            //
            // Next, analyze what kind of async method builder we need by analyzing the return type of
            // the async lambda. Based on this we will determine how many expressions we need in the
            // rewritten top-level async method which sets up the builder, state machine, and kicks off
            // the async logic.
            //
            var invokeMethod = typeof(TDelegate).GetMethod("Invoke");
            var returnType   = invokeMethod.ReturnType;

            var builderInfo = Helpers.GetAsyncMethodBuilderInfo(returnType);
            var builderType = builderInfo.BuilderType;

            Expression[] exprs;

            if (returnType == typeof(void))
            {
                Debug.Assert(builderType == typeof(AsyncVoidMethodBuilder));
                exprs = new Expression[ExprCount];
            }
            else
            {
                exprs = new Expression[ExprCount + 1];
            }

            var i = 0;

            var builderVar      = Expression.Parameter(builderType, "__builder");
            var stateMachineVar = Expression.Parameter(typeof(RuntimeAsyncStateMachine), "__statemachine");
            var stateVar        = Expression.Parameter(typeof(int), "__state");

            //
            // __builder = ATMB.Create();
            //
            var createBuilder = Expression.Assign(builderVar, Expression.Call(builderInfo.Create));

            exprs[i++] = createBuilder;

            //
            // This is where we rewrite the body of the async lambda into an Action delegate we can pass
            // to the runtime async state machine. The collection of variables returned by the rewrite
            // step will contain all the variables that need to be hoisted to the heap. We do this by
            // simply declaring them in the scope around the rewritten (synchronous) body lambda, thus
            // causing the lambda compiler to create a closure.
            //
            // Note we could take the rewritten body and the variables collection and construct a struct
            // implementing IAsyncStateMachine here. However, we don't readily have access to a TypeBuilder
            // for the dynamically generated method created by the lambda compiler higher up, so we'd
            // have to go through some hoops here. For now, we'll rely on a delegate with a closure that
            // consists of a set of StrongBox objects and gets passed to a RuntimeAsyncStateMachine. We
            // can decide to optimize this using a struct later (but then it may be worth making closures
            // in the lambda compiler a bit cheaper by creating a display class as well).
            //
            var body = RewriteBody(builderInfo, stateVar, builderVar, stateMachineVar, out IEnumerable <ParameterExpression> variables);

            //
            // __statemachine = new RuntimeAsyncStateMachine(body);
            //
            var stateMachineCtor   = stateMachineVar.Type.GetConstructor(new[] { typeof(Action) });
            var createStateMachine = Expression.Assign(stateMachineVar, Expression.New(stateMachineCtor, body));

            exprs[i++] = createStateMachine;

            //
            // __state = -1;
            //
            exprs[i++] = Expression.Assign(stateVar, Helpers.CreateConstantInt32(-1));

            //
            // __builder.Start(ref __statemachine);
            //
            var startMethod = builderInfo.Start;

            exprs[i++] = Expression.Call(builderVar, startMethod.MakeGenericMethod(typeof(RuntimeAsyncStateMachine)), stateMachineVar);

            //
            // return __builder.Task;
            //
            if (returnType != typeof(void))
            {
                exprs[i] = Expression.Property(builderVar, builderInfo.Task);
            }

            //
            // The body of the top-level reduced method contains the setup and kick off of the state
            // machine. Note the variables returned from the rewrite step of the body are included here
            // in order to hoist them to the heap.
            //

            // REVIEW: Should we ensure all hoisted variables get assigned default values? This could
            //         matter if the resulting lambda gets inlined in an invocation expression and is
            //         invoked repeatedly, causing locals to be reused. Or should we assume definite
            //         assignment here (or enforce it)?
            var rewritten = Expression.Block(new[] { builderVar, stateMachineVar, stateVar }.Concat(variables), exprs);

            //
            // Finally, run a fairly trivial optimizer to reduce excessively nested blocks that were
            // introduced by the rewrite steps above. This is strictly optional and we could consider
            // an optimization flag of LambdaExpression and AsyncLambdaCSharpExpression that's more
            // generally useful (see ExpressionOptimizationExtensions for an early sketch of some of
            // these optimizations).
            //
            var optimized = Optimizer.Optimize(rewritten);

            //
            // The result is a synchronous lambda that kicks off the async state machine and returns the
            // resulting task (if non-void-returning).
            //
            var res = Expression.Lambda <TDelegate>(optimized, Parameters);

            return(res);
        }