private void DefineMethodParameterFields(FluentActionDefinition fluentActionDefinition)
        {
            var usingsForMethodParameters = fluentActionDefinition.Handlers
                                            .SelectMany(handler => handler.Usings)
                                            .Where(@using => @using.IsMethodParameter)
                                            .Distinct()
                                            .ToArray();

            var methodParameterIndices = usingsForMethodParameters
                                         .Select((@using, index) => new { Using = @using, Index = index })
                                         .ToDictionary(
                indexedUsing => indexedUsing.Using.GetHashCode(),
                indexedUsing => indexedUsing.Index + 1     // 1-based index
                );

            MethodParameterFields = new FieldBuilder[usingsForMethodParameters.Length];

            foreach (var usingDefinition in usingsForMethodParameters)
            {
                var methodParameterIndex = methodParameterIndices[usingDefinition.GetHashCode()];

                MethodParameterFields[methodParameterIndex - 1] = Type.DefineField(
                    $"parameter{methodParameterIndex}",
                    usingDefinition.Type,
                    FieldAttributes.Public);
            }
        }
        private void DefineFields(FluentActionDefinition fluentActionDefinition)
        {
            var asyncTaskMethodBuilderType = typeof(AsyncTaskMethodBuilder <>).MakeGenericType(ReturnType);

            ParentField = Type.DefineField("Parent", ParentType, FieldAttributes.Public);
            AsyncTaskMethodBuilderField = Type.DefineField("AsyncTaskMethodBuilder", asyncTaskMethodBuilderType, FieldAttributes.Public);
            StateField = Type.DefineField("State", typeof(int), FieldAttributes.Public);

            DefineMethodParameterFields(fluentActionDefinition);
            DefineStates(fluentActionDefinition);
        }
        public static AsyncStateMachineTypeBuilder Create(
            TypeBuilder parentTypeBuilder,
            FluentActionDefinition fluentActionDefinition,
            ILogger logger = null)
        {
            var builder = new AsyncStateMachineTypeBuilder
            {
                ParentType = parentTypeBuilder,
                ReturnType = fluentActionDefinition.Handlers.Last().ReturnType,
                Logger     = logger
            };

            if (logger != null)
            {
                builder.LoggerKey = FluentActionLoggers.Add(logger);
            }

            builder.DefineTypeAndDefaultConstructor(parentTypeBuilder);
            builder.DefineFields(fluentActionDefinition);
            builder.DefineMoveNextMethod(fluentActionDefinition);
            builder.DefineSetStateMachineMethod();

            return(builder);
        }
        private void EmitUsingDefinitionValue(ILGenerator ilGenerator, FluentActionDefinition fluentActionDefinition, FluentActionUsingDefinition usingDefinition, Dictionary <int, int> methodParameterIndices, StateMachineState state, int stateIndex, int handlerInStateIndex)
        {
            if (usingDefinition.IsMethodParameter)
            {
                var usingDefinitionHash  = usingDefinition.GetHashCode();
                var methodParameterIndex = methodParameterIndices[usingDefinitionHash];

                ilGenerator.Emit(OpCodes.Ldarg_0);
                ilGenerator.Emit(OpCodes.Ldfld, MethodParameterFields[methodParameterIndex - 1]);
            }
            else if (usingDefinition.IsControllerProperty)
            {
                ilGenerator.Emit(OpCodes.Ldarg_0);
                ilGenerator.Emit(OpCodes.Ldfld, ParentField);
                ilGenerator.Emit(OpCodes.Callvirt,
                                 typeof(Controller).GetProperty(usingDefinition.ControllerPropertyName).GetGetMethod());
            }
            else if (usingDefinition is FluentActionUsingPropertyDefinition)
            {
                var propertyName = ((FluentActionUsingPropertyDefinition)usingDefinition).PropertyName;
                var parentType   = fluentActionDefinition.ParentType ?? typeof(Controller);
                var property     = parentType.GetProperty(propertyName);
                if (property == null)
                {
                    throw new Exception($"Could not find property {propertyName} on type {parentType.FullName}.");
                }

                var propertyGetMethod = property.GetGetMethod();
                if (propertyGetMethod == null)
                {
                    throw new Exception($"Missing public get method on property {propertyName} on type {parentType.FullName}.");
                }

                ilGenerator.Emit(OpCodes.Ldarg_0);
                ilGenerator.Emit(OpCodes.Ldfld, ParentField);
                ilGenerator.Emit(OpCodes.Callvirt, propertyGetMethod);
            }
            else if (usingDefinition is FluentActionUsingParentDefinition)
            {
                ilGenerator.Emit(OpCodes.Ldarg_0);
                ilGenerator.Emit(OpCodes.Ldfld, ParentField);
            }
            else if (usingDefinition is FluentActionUsingResultDefinition)
            {
                FieldBuilder resultFieldToLoad;
                if (handlerInStateIndex > 0)
                {
                    resultFieldToLoad = state.Handlers[handlerInStateIndex - 1].ResultField;
                }
                else
                {
                    resultFieldToLoad = States[stateIndex - 1].ResultField;
                }

                ilGenerator.Emit(OpCodes.Ldarg_0);
                ilGenerator.Emit(OpCodes.Ldfld, resultFieldToLoad);

                if (resultFieldToLoad.FieldType.IsValueType)
                {
                    ilGenerator.Emit(OpCodes.Box, resultFieldToLoad.FieldType);
                }
            }
            else
            {
                throw new Exception($"Got unknown using definition: {usingDefinition.GetType()}");
            }
        }
        private void DefineMoveNextMethod(FluentActionDefinition fluentActionDefinition)
        {
            var usingsForMethodParameters = fluentActionDefinition.Handlers
                                            .SelectMany(handler => handler.Usings)
                                            .Where(@using => @using.IsMethodParameter)
                                            .Distinct()
                                            .ToArray();

            var methodParameterIndices = usingsForMethodParameters
                                         .Select((@using, index) => new { Using = @using, Index = index })
                                         .ToDictionary(
                indexedUsing => indexedUsing.Using.GetHashCode(),
                indexedUsing => indexedUsing.Index + 1     // 1-based index
                );

            var asyncTaskMethodBuilderType = typeof(AsyncTaskMethodBuilder <>).MakeGenericType(ReturnType);

            var handlersForEachState = fluentActionDefinition.Handlers
                                       .Divide(handler => handler.Async)
                                       .Select(handlers => handlers.ToArray())
                                       .ToArray();

            var moveNextBuilder = Type.DefineMethod("MoveNext", MethodAttributes.Public | MethodAttributes.Virtual);

            Type.DefineMethodOverride(moveNextBuilder, typeof(IAsyncStateMachine).GetMethod("MoveNext"));

            moveNextBuilder.SetImplementationFlags(MethodImplAttributes.Managed);

            var ilGenerator = moveNextBuilder.GetILGenerator();

            EmitDebugLog(ilGenerator, "Starting up...");

            for (var i = 0; i < handlersForEachState.Length; i++)
            {
                States[i].StartLabel  = ilGenerator.DefineLabel();
                States[i].WaitLabel   = ilGenerator.DefineLabel();
                States[i].FinishLabel = ilGenerator.DefineLabel();
            }

            var statesStartLabels = States.Select(state => state.StartLabel).ToArray();
            var leaveLabel        = ilGenerator.DefineLabel();

            var localVariableForThis   = ilGenerator.DeclareLocal(Type);
            var exceptionLocalVariable = ilGenerator.DeclareLocal(typeof(Exception));

            var exceptionBlock = ilGenerator.BeginExceptionBlock();

            EmitDebugLog(ilGenerator, "Checking State < 0");

            // If State < 0 it means we've got an exception and should leave
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Ldfld, StateField);
            ilGenerator.Emit(OpCodes.Ldc_I4_0);
            ilGenerator.Emit(OpCodes.Blt, leaveLabel);

            EmitDebugLog(ilGenerator, "Checking State >= handlersForEachState.Length");

            // If State >= handlersForEachState.Length it means we've got a result and should leave
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Ldfld, StateField);
            ilGenerator.Emit(OpCodes.Ldc_I4, handlersForEachState.Length);
            ilGenerator.Emit(OpCodes.Bge, leaveLabel);

            EmitDebugLog(ilGenerator, "Switching on State");

            // Jump to state (as long as 0 <= State && State < asyncHandlers.Length)
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Ldfld, StateField);
            ilGenerator.Emit(OpCodes.Switch, statesStartLabels);

            // ====== Leave-block ====== (With label so you can jump to it)
            ilGenerator.MarkLabel(leaveLabel);
            ilGenerator.Emit(OpCodes.Leave, exceptionBlock);

            // Define each state-block
            var stateIndex     = 0;
            var lastStateIndex = States.Length - 1;

            foreach (var state in States)
            {
                // ====== State-block ====== (Run all handlers in state and, potentially, wait for result of last async handler)
                ilGenerator.MarkLabel(state.StartLabel);

                EmitDebugLog(ilGenerator, $"State{stateIndex}::Start");

                var taskAwaiterType = state.ResultType != null ?
                                      typeof(TaskAwaiter <>).MakeGenericType(state.ResultType)
                    : typeof(TaskAwaiter);
                var taskAwaiterIsCompletedGetMethod = taskAwaiterType.GetProperty("IsCompleted").GetGetMethod();
                var taskAwaiterGetResultMethod      = taskAwaiterType.GetMethod("GetResult");

                EmitDebugLog(ilGenerator, $"State{stateIndex}::Checking wait-flag");

                if (state.Async)
                {
                    // If [Waiting]: jump to wait-block
                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    ilGenerator.Emit(OpCodes.Ldfld, state.WaitingField);
                    ilGenerator.Emit(OpCodes.Brtrue, state.WaitLabel);
                }

                EmitDebugLog(ilGenerator, $"State{stateIndex}::Not waiting, running handlers");

                // Else: Run all handlers in state
                for (var handlerInStateIndex = 0; handlerInStateIndex < state.Handlers.Length; handlerInStateIndex++)
                {
                    var handlerInState = state.Handlers[handlerInStateIndex];
                    var handler        = handlerInState.Definition;

                    EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::Start");

                    if (handler.Type == FluentActionHandlerType.Func)
                    {
                        EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::Beginning Func.Invoke");

                        var resultType             = BuilderHelper.GetReturnTypeOrTaskType(handler);
                        var localVariableForResult = ilGenerator.DeclareLocal(resultType);
                        var funcType = BuilderHelper.GetFuncType(handler);

                        EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::Pushing Delegate");

                        // Push Delegate
                        ilGenerator.Emit(OpCodes.Ldarg_0);
                        ilGenerator.Emit(OpCodes.Ldfld, handlerInState.DelegateField);

                        EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::Pushing arguments for Delegate");

                        // Push arguments for Delegate
                        foreach (var usingDefinition in handler.Usings)
                        {
                            EmitUsingDefinitionValue(ilGenerator, fluentActionDefinition, usingDefinition, methodParameterIndices, state, stateIndex, handlerInStateIndex);
                        }

                        EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::Invoking Delegate");

                        // Push Func.Invoke
                        ilGenerator.Emit(OpCodes.Callvirt, funcType.GetMethod("Invoke"));

                        EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::Result in local variable");

                        // Push result in local variable
                        ilGenerator.Emit(OpCodes.Stloc, localVariableForResult);

                        if (handler.Async)
                        {
                            EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::State.Awaiter = result.GetAwaiter();");

                            // State.Awaiter = result.GetAwaiter();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldloc, localVariableForResult);
                            ilGenerator.Emit(OpCodes.Call, resultType.GetMethod("GetAwaiter"));
                            ilGenerator.Emit(OpCodes.Stfld, state.TaskAwaiterField);

                            EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::State.Waiting = true;");

                            // State.Waiting = true;
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldc_I4_1);
                            ilGenerator.Emit(OpCodes.Stfld, state.WaitingField);

                            EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::If [result.IsCompleted]: Goto finish;");
                            // If [result.IsCompleted]: Goto finish
                            ilGenerator.Emit(OpCodes.Ldloc, localVariableForResult);
                            ilGenerator.Emit(OpCodes.Call, resultType.GetProperty("IsCompleted").GetGetMethod());
                            ilGenerator.Emit(OpCodes.Brtrue, state.FinishLabel);

                            // Else: AwaitUnsafeOnCompleted(ref Awaiter, ref self)

                            EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::Else: AwaitUnsafeOnCompleted(ref Awaiter, ref self)");

                            // Store executing instance ("this") in a local variable
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Stloc, localVariableForThis);

                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldflda, AsyncTaskMethodBuilderField);
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldflda, state.TaskAwaiterField);
                            ilGenerator.Emit(OpCodes.Ldloca, localVariableForThis);
                            ilGenerator.Emit(OpCodes.Call, asyncTaskMethodBuilderType
                                             .GetMethod("AwaitUnsafeOnCompleted")
                                             .MakeGenericMethod(state.TaskAwaiterType, Type));

                            EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::Called AwaitUnsafeOnCompleted.");
                        }
                        else if (handlerInStateIndex == state.Handlers.Length - 1)
                        {
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldloc, localVariableForResult);
                            ilGenerator.Emit(OpCodes.Stfld, state.ResultField);

                            ilGenerator.Emit(OpCodes.Br, state.FinishLabel);
                        }
                        else
                        {
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldloc, localVariableForResult);
                            ilGenerator.Emit(OpCodes.Stfld, handlerInState.ResultField);
                        }
                    }
                    else if (handler.Type == FluentActionHandlerType.Action)
                    {
                        EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::Beginning Action.Invoke");

                        var resultType             = typeof(Task);
                        var localVariableForResult = ilGenerator.DeclareLocal(resultType);
                        var funcType = BuilderHelper.GetDelegateType(handler);

                        EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::Pushing Delegate");

                        // Push Delegate
                        ilGenerator.Emit(OpCodes.Ldarg_0);
                        ilGenerator.Emit(OpCodes.Ldfld, handlerInState.DelegateField);

                        EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::Pushing arguments for Delegate");

                        // Push arguments for Delegate
                        foreach (var usingDefinition in handler.Usings)
                        {
                            EmitUsingDefinitionValue(ilGenerator, fluentActionDefinition, usingDefinition, methodParameterIndices, state, stateIndex, handlerInStateIndex);
                        }

                        EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::Invoking Delegate");

                        // Push Func.Invoke
                        ilGenerator.Emit(OpCodes.Callvirt, funcType.GetMethod("Invoke"));

                        if (handler.Async)
                        {
                            EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::Result in local variable");

                            // Push result in local variable
                            ilGenerator.Emit(OpCodes.Stloc, localVariableForResult);

                            EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::State.Awaiter = result.GetAwaiter();");

                            // State.Awaiter = result.GetAwaiter();
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldloc, localVariableForResult);
                            ilGenerator.Emit(OpCodes.Call, resultType.GetMethod("GetAwaiter"));
                            ilGenerator.Emit(OpCodes.Stfld, state.TaskAwaiterField);

                            EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::State.Waiting = true;");

                            // State.Waiting = true;
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldc_I4_1);
                            ilGenerator.Emit(OpCodes.Stfld, state.WaitingField);

                            EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::If [result.IsCompleted]: Goto finish;");
                            // If [result.IsCompleted]: Goto finish
                            ilGenerator.Emit(OpCodes.Ldloc, localVariableForResult);
                            ilGenerator.Emit(OpCodes.Call, resultType.GetProperty("IsCompleted").GetGetMethod());
                            ilGenerator.Emit(OpCodes.Brtrue, state.FinishLabel);

                            // Else: AwaitUnsafeOnCompleted(ref Awaiter, ref self)

                            EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::Else: AwaitUnsafeOnCompleted(ref Awaiter, ref self)");

                            // Store executing instance ("this") in a local variable
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Stloc, localVariableForThis);

                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldflda, AsyncTaskMethodBuilderField);
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldflda, state.TaskAwaiterField);
                            ilGenerator.Emit(OpCodes.Ldloca, localVariableForThis);
                            ilGenerator.Emit(OpCodes.Call, asyncTaskMethodBuilderType
                                             .GetMethod("AwaitUnsafeOnCompleted")
                                             .MakeGenericMethod(state.TaskAwaiterType, Type));

                            EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::Called AwaitUnsafeOnCompleted.");
                        }
                        else if (handlerInStateIndex == state.Handlers.Length - 1)
                        {
                            ilGenerator.Emit(OpCodes.Br, state.FinishLabel);
                        }
                    }
                    else if (handler.Type == FluentActionHandlerType.View ||
                             handler.Type == FluentActionHandlerType.PartialView ||
                             handler.Type == FluentActionHandlerType.ViewComponent)
                    {
                        EmitDebugLog(ilGenerator, $"State{stateIndex}::Handler::Beginning View call");

                        if (handler.ViewTarget == null)
                        {
                            throw new Exception("Must specify a view target.");
                        }

                        // Call one of the following controller methods:
                        //   Controller.View(string pathName, object model)
                        //   Controller.PartialView(string pathName, object model)
                        //   Controller.ViewComponent(string componentName, object arguments)

                        ilGenerator.Emit(OpCodes.Ldarg_0);
                        ilGenerator.Emit(OpCodes.Ldarg_0);
                        ilGenerator.Emit(OpCodes.Ldfld, ParentField);
                        ilGenerator.Emit(OpCodes.Ldstr, handler.ViewTarget);

                        Type[] viewMethodParameterTypes = null;
                        if (handler.Usings.Any())
                        {
                            EmitUsingDefinitionValue(ilGenerator, fluentActionDefinition, handler.Usings.Last(), methodParameterIndices, state, stateIndex, handlerInStateIndex);
                            viewMethodParameterTypes = new[] { typeof(string), typeof(object) };
                        }
                        else if (handlerInStateIndex > 0 && state.Handlers[handlerInStateIndex - 1].ResultField != null)
                        {
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldfld, state.Handlers[handlerInStateIndex - 1].ResultField);

                            viewMethodParameterTypes = new[] { typeof(string), typeof(object) };
                        }
                        else if (handlerInStateIndex == 0 && stateIndex > 0 && States[stateIndex - 1].ResultField != null)
                        {
                            ilGenerator.Emit(OpCodes.Ldarg_0);
                            ilGenerator.Emit(OpCodes.Ldfld, States[stateIndex - 1].ResultField);

                            viewMethodParameterTypes = new[] { typeof(string), typeof(object) };
                        }
                        else
                        {
                            viewMethodParameterTypes = new[] { typeof(string) };
                        }

                        MethodInfo viewMethod = null;
                        if (handler.Type == FluentActionHandlerType.View)
                        {
                            viewMethod = typeof(Controller).GetMethod("View", viewMethodParameterTypes);
                        }
                        else if (handler.Type == FluentActionHandlerType.PartialView)
                        {
                            viewMethod = typeof(Controller).GetMethod("PartialView", viewMethodParameterTypes);
                        }
                        else if (handler.Type == FluentActionHandlerType.ViewComponent)
                        {
                            viewMethod = typeof(Controller).GetMethod("ViewComponent", viewMethodParameterTypes);
                        }

                        ilGenerator.Emit(OpCodes.Callvirt, viewMethod);

                        if (handlerInStateIndex == state.Handlers.Length - 1)
                        {
                            ilGenerator.Emit(OpCodes.Stfld, state.ResultField);
                            ilGenerator.Emit(OpCodes.Br, state.FinishLabel);
                        }
                        else
                        {
                            ilGenerator.Emit(OpCodes.Stfld, handlerInState.ResultField);
                        }
                    }
                }

                EmitDebugLog(ilGenerator, $"State{stateIndex}::Leaving exception-block.");

                ilGenerator.Emit(OpCodes.Leave, exceptionBlock);

                if (state.Async)
                {
                    // ====== Wait-block ====== (Check Awaiter if it is done and then save result)
                    ilGenerator.MarkLabel(state.WaitLabel);

                    EmitDebugLog(ilGenerator, $"State{stateIndex}::Wait-block");

                    // If [!State.Awaiter.IsComplete]: leave
                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    ilGenerator.Emit(OpCodes.Ldflda, state.TaskAwaiterField);
                    ilGenerator.Emit(OpCodes.Call, taskAwaiterIsCompletedGetMethod);
                    ilGenerator.Emit(OpCodes.Brfalse, leaveLabel);
                }

                // ====== Finish-block ====== (Save result, update state and, potentially, set final result)
                ilGenerator.MarkLabel(state.FinishLabel);

                EmitDebugLog(ilGenerator, $"State{stateIndex}::Finish-block");

                if (state.Async && state.ResultType != null)
                {
                    // State.Result = State.Awaiter.GetResult()
                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    ilGenerator.Emit(OpCodes.Ldflda, state.TaskAwaiterField);
                    ilGenerator.Emit(OpCodes.Call, taskAwaiterGetResultMethod);
                    ilGenerator.Emit(OpCodes.Stfld, state.ResultField);
                }

                // State + 1
                ilGenerator.Emit(OpCodes.Ldarg_0);
                ilGenerator.Emit(OpCodes.Ldc_I4, stateIndex + 1);
                ilGenerator.Emit(OpCodes.Stfld, StateField);

                if (stateIndex == lastStateIndex)
                {
                    // MethodSetResult(StateResult)
                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    ilGenerator.Emit(OpCodes.Ldflda, AsyncTaskMethodBuilderField);
                    ilGenerator.Emit(OpCodes.Ldarg_0);
                    ilGenerator.Emit(OpCodes.Ldfld, state.ResultField);
                    ilGenerator.Emit(OpCodes.Call, asyncTaskMethodBuilderType.GetMethod("SetResult"));
                }
                else
                {
                    ilGenerator.Emit(OpCodes.Br, statesStartLabels[stateIndex + 1]);
                }

                ilGenerator.Emit(OpCodes.Leave, exceptionBlock);

                stateIndex++;
            }

            ilGenerator.BeginCatchBlock(typeof(Exception));

            EmitDebugLog(ilGenerator, $"Catch-block.");

            // Store exception locally
            ilGenerator.Emit(OpCodes.Stloc, exceptionLocalVariable);

            // State = -1
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Ldc_I4_M1);
            ilGenerator.Emit(OpCodes.Stfld, StateField);

            // StateMachine.SetException(exception)
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Ldflda, AsyncTaskMethodBuilderField);
            ilGenerator.Emit(OpCodes.Ldloc, exceptionLocalVariable);
            ilGenerator.Emit(OpCodes.Call, asyncTaskMethodBuilderType.GetMethod("SetException"));

            EmitDebugLog(ilGenerator, $"Got exception.");

            ilGenerator.Emit(OpCodes.Leave, exceptionBlock);

            EmitDebugLog(ilGenerator, $"Ending exception block...");

            ilGenerator.EndExceptionBlock();

            EmitDebugLog(ilGenerator, $"Ended exception block.");
            EmitDebugLog(ilGenerator, $"Returning...");

            ilGenerator.Emit(OpCodes.Ret);
        }
        private void DefineStates(FluentActionDefinition fluentActionDefinition)
        {
            var handlersPerState = fluentActionDefinition.Handlers
                                   .Divide(handler => handler.Async)
                                   .Select(handlers => handlers.ToArray())
                                   .ToArray();

            States = new StateMachineState[handlersPerState.Length];

            for (var stateIndex = 0; stateIndex < handlersPerState.Length; stateIndex++)
            {
                var handlersInState = handlersPerState[stateIndex];
                var state           = new StateMachineState
                {
                    Handlers = new StateMachineStateHandler[handlersInState.Length]
                };

                for (var handlerIndex = 0; handlerIndex < handlersInState.Length; handlerIndex++)
                {
                    var handlerDefinition = handlersInState[handlerIndex];

                    state.Handlers[handlerIndex] = new StateMachineStateHandler
                    {
                        Definition = handlerDefinition
                    };

                    if (handlerDefinition.Type == FluentActionHandlerType.Func)
                    {
                        state.Handlers[handlerIndex].DelegateField = Type.DefineField(
                            $"State{stateIndex}Handler{handlerIndex}Delegate",
                            BuilderHelper.GetDelegateType(handlerDefinition),
                            FieldAttributes.Public);

                        state.Handlers[handlerIndex].ResultField = Type.DefineField(
                            $"State{stateIndex}Handler{handlerIndex}Result",
                            handlerDefinition.ReturnType,
                            FieldAttributes.Public);
                    }
                    else if (handlerDefinition.Type == FluentActionHandlerType.Action)
                    {
                        state.Handlers[handlerIndex].DelegateField = Type.DefineField(
                            $"State{stateIndex}Handler{handlerIndex}Delegate",
                            BuilderHelper.GetDelegateType(handlerDefinition),
                            FieldAttributes.Public);
                    }
                    else if (
                        handlerDefinition.Type == FluentActionHandlerType.View ||
                        handlerDefinition.Type == FluentActionHandlerType.PartialView ||
                        handlerDefinition.Type == FluentActionHandlerType.ViewComponent)
                    {
                        state.Handlers[handlerIndex].ResultField = Type.DefineField(
                            $"State{stateIndex}Handler{handlerIndex}Result",
                            handlerDefinition.ReturnType,
                            FieldAttributes.Public);
                    }
                }

                var lastHandlerInState = handlersInState.Last();
                if (lastHandlerInState.Type == FluentActionHandlerType.Func)
                {
                    state.ResultType = lastHandlerInState.ReturnType;

                    state.ResultField = Type.DefineField(
                        $"State{stateIndex}ReturnType",
                        state.ResultType,
                        FieldAttributes.Public);

                    state.TaskAwaiterType = typeof(TaskAwaiter <>)
                                            .MakeGenericType(lastHandlerInState.ReturnType);

                    state.TaskAwaiterField = Type.DefineField(
                        $"State{stateIndex}Awaiter",
                        state.TaskAwaiterType,
                        FieldAttributes.Public);

                    state.WaitingField = Type.DefineField(
                        $"State{stateIndex}WaitingFlag",
                        typeof(bool),
                        FieldAttributes.Public);
                }
                else if (lastHandlerInState.Type == FluentActionHandlerType.Action)
                {
                    state.ResultType = lastHandlerInState.ReturnType;

                    // state.ResultField is not used since an Action returns void

                    state.TaskAwaiterType = typeof(TaskAwaiter);

                    state.TaskAwaiterField = Type.DefineField(
                        $"State{stateIndex}Awaiter",
                        state.TaskAwaiterType,
                        FieldAttributes.Public);

                    state.WaitingField = Type.DefineField(
                        $"State{stateIndex}WaitingFlag",
                        typeof(bool),
                        FieldAttributes.Public);
                }
                else if (lastHandlerInState.Type == FluentActionHandlerType.View ||
                         lastHandlerInState.Type == FluentActionHandlerType.PartialView ||
                         lastHandlerInState.Type == FluentActionHandlerType.ViewComponent)
                {
                    state.ResultType = lastHandlerInState.ReturnType;

                    state.ResultField = Type.DefineField(
                        $"State{stateIndex}ReturnType",
                        state.ResultType,
                        FieldAttributes.Public);
                }

                States[stateIndex] = state;
            }
        }
Beispiel #7
0
 public ControllerMethodBuilderForFluentActionAsync(FluentActionDefinition fluentActionDefinition, ILogger logger = null)
 {
     FluentActionDefinition = fluentActionDefinition;
     Logger = logger;
 }
 public ControllerMethodBuilderForFluentAction(FluentActionDefinition fluentActionDefinition)
 {
     FluentActionDefinition = fluentActionDefinition;
 }