Exemplo n.º 1
0
 public AsyncExpressionDecomposer(AsyncStateGenerator stateGenerator, Idioms idioms) : base(idioms)
 {
     this.stateGenerator = stateGenerator;
 }
Exemplo n.º 2
0
 public AsyncExpressionDecomposer(AsyncStateGenerator stateGenerator, Idioms idioms) : base(idioms)
 {
     this.stateGenerator = stateGenerator;
 }
Exemplo n.º 3
0
        /// <summary>
        /// This generates the entire state machine as an inline function.  State machine fields are represented
        /// as closed variables of an outer function.  The actual "MoveNext" function is an inner function.
        /// </summary>
        public JsBlockStatement GenerateAsyncMethod(CSharpSyntaxNode node, IMethodSymbol method, Action<BaseStateGenerator, JsTransformer> nodeAcceptor = null)
        {
            var block = new JsBlockStatement();
            var stateMachineBody = Js.Block();
            var stateMachine = block.Local(BaseStateGenerator.stateMachine, Js.Function().Body(stateMachineBody));

            // Declare state machine fields
            var @this = stateMachineBody.Local(BaseStateGenerator.@this, Js.This());
            var state = stateMachineBody.Local(BaseStateGenerator.state, Js.Primitive(0));
            var builder = stateMachineBody.Local(AsyncStateGenerator.builder, InvokeStatic(GetAsyncMethodBuilder(method)));

            // Start up the async process via a call to the builder's Start method.
            var moveNextBody = Js.Block();
            var moveNext = stateMachineBody.Local(BaseStateGenerator.moveNext, Js.Function().Body(moveNextBody));

            // Create state generator and generate states
            var stateGenerator = new AsyncStateGenerator(this, stateMachineBody, node, method, nodeAcceptor);
            stateGenerator.GenerateStates();
            var rootState = stateGenerator.TopState;

            // Implement moveNext
            var moveNextTry = Js.Try();
            moveNextBody.Add(moveNextTry);
            moveNextTry.Body = Js.Block(
                Js.Label("$top", Js.While(Js.Primitive(true), Js.Block(stateGenerator.GenerateSwitch(rootState), Js.Break())))
            );
            var moveNextCatchException = Js.Variable("$ex");
            moveNextTry.Catch(moveNextCatchException);
            moveNextTry.Catch.Body = Js.Block(
                state.SetReference().Assign(Js.Primitive(-1)).Express(),
                builder.GetReference().Member("SetException").Invoke(moveNextCatchException.GetReference()).Express(),
                Js.Return()
            );

            // Ensure the stateMachine function implements IAsyncStateMachine
            ImplementInterfaceOnAdhocObject(stateMachineBody, stateMachine, Context.Instance.IAsyncStateMachine, new Dictionary<IMethodSymbol, JsExpression>
            {
                { Context.Instance.IAsyncStateMachineMoveNext, Js.Function().Body(moveNext.GetReference().Member("call").Invoke(@this.GetReference()).Return()) },
                { Context.Instance.IAsyncStateMachineSetStateMachine, Js.Null() }
            });

            stateMachineBody.Express(builder.GetReference().Member("TrueStart").Invoke(stateMachine.GetReference()));

            // The state machine function will be invoked by the outer body.  It will expect the task 
            // to be returned for non-void async methods.  This task will be returned to the original 
            // caller of the async method.
            if (!method.ReturnsVoid)
                stateMachineBody.Return(builder.GetReference().Member("get_Task").Invoke());

            var invokeStateMachine = stateMachine.GetReference().Member("call").Invoke(Js.This());
            if (!method.ReturnsVoid)
                block.Add(invokeStateMachine.Return());
            else
                block.Add(invokeStateMachine.Express());

            return block;
        }
Exemplo n.º 4
0
        public ClassDeclarationSyntax CreateStateMachine()
        {
            var members = new List <MemberDeclarationSyntax>();
            FieldDeclarationSyntax thisField = null;

            if (!method.IsStatic)
            {
                thisField = Cs.Field(method.ContainingType.ToTypeSyntax(), "$this");
                members.Add(thisField);
            }

            var stateGenerator = new AsyncStateGenerator(compilation, node);

            stateGenerator.GenerateStates();
            var states = stateGenerator.States;

            var stateField = Cs.Field(Cs.Int(), state);

            members.Add(stateField);

            var builderField = Cs.Field(Context.Instance.AsyncVoidMethodBuilder.ToTypeSyntax(), builder);

            members.Add(builderField);

            foreach (var parameter in node.ParameterList.Parameters)
            {
                var parameterField = Cs.Field(parameter.Type, parameter.Identifier);
                members.Add(parameterField);
            }
            foreach (var variable in stateGenerator.HoistedVariables)
            {
                var variableField = Cs.Field(variable.Item2, variable.Item1);
                members.Add(variableField);
            }

            var className = "Async$" + method.GetMemberName();

            var constructorParameters = new List <ParameterSyntax>();

            if (!method.IsStatic)
            {
                constructorParameters.Add(SyntaxFactory.Parameter(SyntaxFactory.Identifier("$this")).WithType(thisField.Declaration.Type));
            }
            constructorParameters.AddRange(node.ParameterList.Parameters.Select(x => SyntaxFactory.Parameter(x.Identifier).WithType(x.Type)));

            var constructor = SyntaxFactory.ConstructorDeclaration(className)
                              .AddModifiers(Cs.Public())
                              .WithParameterList(constructorParameters.ToArray())
                              .WithBody(
                SyntaxFactory.Block(
                    // Assign fields
                    constructorParameters.Select(x => Cs.Express(Cs.This().Member(x.Identifier).Assign(SyntaxFactory.IdentifierName(x.Identifier))))
                    )
                .AddStatements(
                    Cs.Express(Cs.This().Member(state).Assign(Cs.Integer(-1))),
                    Cs.Express(Cs.This().Member(builder).Assign(builderField.Declaration.Type.New())),
                    Cs.Express(Cs.This().Member(builder).Member("Start").Invoke(Cs.This()))
                    )
                );

            members.Add(constructor);

            // Generate the MoveNext method, which looks something like:
            // $top:
            // while (true)
            // {
            //     switch (state)
            //     {
            //         case 0: ...
            //         case 1: ...
            //     }
            // }
            var moveNextBody = SyntaxFactory.LabeledStatement("$top", Cs.While(Cs.True(),
                                                                               Cs.Switch(Cs.This().Member(state), states.Select((x, i) =>
                                                                                                                                Cs.Section(Cs.Integer(i), x.Statements.ToArray())).ToArray())));
            var moveNext = SyntaxFactory.MethodDeclaration(Cs.Bool(), "MoveNext")
                           .AddModifiers(Cs.Public())
                           .WithBody(SyntaxFactory.Block(moveNextBody));

            members.Add(moveNext);

            var asyncStateMachine = SyntaxFactory.ParseTypeName("System.Runtime.CompilerServices.IAsyncStateMachine");
            var baseTypes         = new[] { asyncStateMachine };
            var result            = SyntaxFactory.ClassDeclaration(className).WithBaseList(baseTypes).WithMembers(members.ToArray());

            if (method.TypeParameters.Any())
            {
                result = result.WithTypeParameterList(node.TypeParameterList);
            }

            return(result);
        }