Esempio n. 1
0
        public override void Build()
        {
            StateMachineBuilder = AsyncStateMachineTypeBuilder.Create(TypeBuilder, FluentActionDefinition, Logger);

            NestedTypes.Add(StateMachineBuilder.Type);

            var usingsForMethodParameters = FluentActionDefinition.Handlers
                                            .SelectMany(usingHandler => usingHandler.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 methodParameterTypes = usingsForMethodParameters
                                       .Select(@using => @using.Type)
                                       .ToArray();

            var returnType     = FluentActionDefinition.Handlers.Last().ReturnType;
            var returnTypeTask = typeof(Task <>).MakeGenericType(returnType);

            MethodBuilder.SetReturnType(returnTypeTask);
            MethodBuilder.SetParameters(methodParameterTypes);

            SetHttpMethodAttribute(FluentActionDefinition.HttpMethod);
            SetRouteAttribute(FluentActionDefinition.RouteTemplate);

            foreach (var customAttribute in FluentActionDefinition.CustomAttributes)
            {
                SetCustomAttribute(customAttribute);
            }

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

                usingDefinition.DefineMethodParameter(MethodBuilder, FluentActionDefinition, usingDefinition, methodParameterIndex);
            }

            var dictionaryField = typeof(FluentActionDelegates)
                                  .GetField("All");
            var dictionaryGetMethod = typeof(ConcurrentDictionary <,>)
                                      .MakeGenericType(typeof(string), typeof(Delegate))
                                      .GetMethod("get_Item");

            var ilGenerator = MethodBuilder.GetILGenerator();

            // === Generate IL for action method ==========================

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

            // Declare local variables
            var stateMachineLocalVariable = ilGenerator.DeclareLocal(StateMachineBuilder.Type);

            // Create a StateMachine and store it locally
            ilGenerator.Emit(OpCodes.Newobj, StateMachineBuilder.Constructor);
            ilGenerator.Emit(OpCodes.Stloc, stateMachineLocalVariable);

            // Store reference to parent in field StateMachine.Parent
            ilGenerator.Emit(OpCodes.Ldloc, stateMachineLocalVariable);
            ilGenerator.Emit(OpCodes.Ldarg_0);
            ilGenerator.Emit(OpCodes.Stfld, StateMachineBuilder.ParentField);

            // Create an AsyncTaskMethodBuilder and store it in field StateMachine.AsyncTaskMethodBuilder
            ilGenerator.Emit(OpCodes.Ldloc, stateMachineLocalVariable);
            ilGenerator.Emit(OpCodes.Call, asyncTaskMethodBuilderType.GetMethod("Create"));
            ilGenerator.Emit(OpCodes.Stfld, StateMachineBuilder.AsyncTaskMethodBuilderField);

            // Set field StateMachine.State = 0
            ilGenerator.Emit(OpCodes.Ldloc, stateMachineLocalVariable);
            ilGenerator.Emit(OpCodes.Ldc_I4_0);
            ilGenerator.Emit(OpCodes.Stfld, StateMachineBuilder.StateField);

            // Store parameters to fields in StateMachine
            foreach (var usingDefinition in usingsForMethodParameters)
            {
                var methodParameterIndex = methodParameterIndices[usingDefinition.GetHashCode()];
                var methodParameterField = StateMachineBuilder.MethodParameterFields[methodParameterIndex - 1];

                ilGenerator.Emit(OpCodes.Ldloc, stateMachineLocalVariable);
                ilGenerator.Emit(OpCodes.Ldarg, methodParameterIndex);
                ilGenerator.Emit(OpCodes.Stfld, methodParameterField);
            }

            var handlers = StateMachineBuilder.States
                           .SelectMany(state => state.Handlers)
                           .Where(handler =>
                                  handler.Definition.Type == FluentActionHandlerType.Func ||
                                  handler.Definition.Type == FluentActionHandlerType.Action);

            // Store delegates to fields in StateMachine
            foreach (var handler in handlers)
            {
                var delegateKey = FluentActionDelegates.Add(handler.Definition.Delegate);

                // Push Delegate
                ilGenerator.Emit(OpCodes.Ldloc, stateMachineLocalVariable);
                ilGenerator.Emit(OpCodes.Ldsfld, FluentActionDelegates.FieldInfo);
                ilGenerator.Emit(OpCodes.Ldstr, delegateKey);
                ilGenerator.Emit(OpCodes.Callvirt, FluentActionDelegates.MethodInfo);

                // Store in field StateMachine.StateXHandlerYDelegate
                ilGenerator.Emit(OpCodes.Stfld, handler.DelegateField);
            }

            // Start the AsyncTaskMethodBuilder
            ilGenerator.Emit(OpCodes.Ldloc, stateMachineLocalVariable);
            ilGenerator.Emit(OpCodes.Ldflda, StateMachineBuilder.AsyncTaskMethodBuilderField);
            ilGenerator.Emit(OpCodes.Ldloca, stateMachineLocalVariable);
            ilGenerator.Emit(OpCodes.Call, asyncTaskMethodBuilderType.GetMethod("Start").MakeGenericMethod(StateMachineBuilder.Type));

            // Return the Task of AsyncTaskMethodBuilder
            ilGenerator.Emit(OpCodes.Ldloc, stateMachineLocalVariable);
            ilGenerator.Emit(OpCodes.Ldflda, StateMachineBuilder.AsyncTaskMethodBuilderField);
            ilGenerator.Emit(OpCodes.Call, asyncTaskMethodBuilderType.GetProperty("Task").GetGetMethod());

            ilGenerator.Emit(OpCodes.Ret);
        }
        public override void Build()
        {
            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 methodParameterTypes = usingsForMethodParameters
                                       .Select(@using => @using.Type)
                                       .ToArray();

            var returnType = FluentActionDefinition.Handlers.Last().ReturnType;

            if (FluentActionDefinition.IsAsync)
            {
                returnType = typeof(Task <>).MakeGenericType(returnType);
            }

            MethodBuilder.SetReturnType(returnType);
            MethodBuilder.SetParameters(methodParameterTypes);

            SetHttpMethodAttribute(FluentActionDefinition.HttpMethod);
            SetRouteAttribute(FluentActionDefinition.RouteTemplate);

            foreach (var customAttribute in FluentActionDefinition.CustomAttributes)
            {
                SetCustomAttribute(customAttribute);
            }

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

                usingDefinition.DefineMethodParameter(MethodBuilder, FluentActionDefinition, usingDefinition, methodParameterIndex);
            }

            var ilGenerator = MethodBuilder.GetILGenerator();

            LocalBuilder localVariableForPreviousReturnValue = null;

            foreach (var handler in FluentActionDefinition.Handlers)
            {
                if (handler.Type == FluentActionHandlerType.Func)
                {
                    var handlerReturnType           = BuilderHelper.GetReturnTypeOrTaskType(handler);
                    var localVariableForReturnValue = ilGenerator.DeclareLocal(handlerReturnType);

                    var funcType    = BuilderHelper.GetFuncType(handler);
                    var delegateKey = FluentActionDelegates.Add(handler.Delegate);

                    // Push Delegate
                    ilGenerator.Emit(OpCodes.Ldsfld, FluentActionDelegates.FieldInfo);
                    ilGenerator.Emit(OpCodes.Ldstr, delegateKey);
                    ilGenerator.Emit(OpCodes.Callvirt, FluentActionDelegates.MethodInfo);

                    // Push arguments for Func
                    foreach (var usingDefinition in handler.Usings)
                    {
                        EmitUsingDefinitionValue(ilGenerator, usingDefinition, methodParameterIndices, localVariableForPreviousReturnValue);
                    }

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

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

                    // Make sure next handler has access to previous handler's return value
                    localVariableForPreviousReturnValue = localVariableForReturnValue;
                }
                else if (handler.Type == FluentActionHandlerType.Action)
                {
                    var actionType  = BuilderHelper.GetActionType(handler);
                    var delegateKey = FluentActionDelegates.Add(handler.Delegate);

                    // Push Delegate
                    ilGenerator.Emit(OpCodes.Ldsfld, FluentActionDelegates.FieldInfo);
                    ilGenerator.Emit(OpCodes.Ldstr, delegateKey);
                    ilGenerator.Emit(OpCodes.Callvirt, FluentActionDelegates.MethodInfo);

                    // Push arguments for Action
                    foreach (var usingDefinition in handler.Usings)
                    {
                        EmitUsingDefinitionValue(ilGenerator, usingDefinition, methodParameterIndices, localVariableForPreviousReturnValue);
                    }

                    // Push Action.Invoke
                    ilGenerator.Emit(OpCodes.Callvirt, actionType.GetMethod("Invoke"));

                    // This handler does not produce a result
                    localVariableForPreviousReturnValue = null;
                }
                else if (handler.Type == FluentActionHandlerType.View ||
                         handler.Type == FluentActionHandlerType.PartialView ||
                         (handler.Type == FluentActionHandlerType.ViewComponent && handler.ViewComponentType == null))
                {
                    if (handler.ViewTarget == null)
                    {
                        throw new Exception("Must specify a view target.");
                    }

                    var localVariableForReturnValue = ilGenerator.DeclareLocal(handler.ReturnType);

                    // 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.Ldstr, handler.ViewTarget);

                    Type[] viewMethodParameterTypes = null;
                    if (handler.Usings.Any())
                    {
                        EmitUsingDefinitionValue(ilGenerator, handler.Usings.Last(), methodParameterIndices, localVariableForPreviousReturnValue);
                        viewMethodParameterTypes = new[] { typeof(string), typeof(object) };
                    }
                    else if (localVariableForPreviousReturnValue != null)
                    {
                        ilGenerator.Emit(OpCodes.Ldloc, localVariableForPreviousReturnValue);
                        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);

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

                    // Make sure next handler has access to previous handler's return value
                    localVariableForPreviousReturnValue = localVariableForReturnValue;
                }
                else if (handler.Type == FluentActionHandlerType.ViewComponent)
                {
                    if (handler.ViewComponentType == null)
                    {
                        throw new Exception("Must specify a target view component type.");
                    }

                    var localVariableForReturnValue = ilGenerator.DeclareLocal(handler.ReturnType);

                    // Call the following controller method:
                    //   Controller.ViewComponent(Type componentType, object arguments)

                    ilGenerator.Emit(OpCodes.Ldarg_0);

                    ilGenerator.Emit(OpCodes.Ldtoken, handler.ViewComponentType);
                    ilGenerator.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle", new[] { typeof(RuntimeTypeHandle) }));

                    Type[] viewMethodParameterTypes = null;
                    if (localVariableForPreviousReturnValue != null)
                    {
                        ilGenerator.Emit(OpCodes.Ldloc, localVariableForPreviousReturnValue);
                        viewMethodParameterTypes = new[] { typeof(Type), typeof(object) };
                    }
                    else
                    {
                        viewMethodParameterTypes = new[] { typeof(Type) };
                    }

                    var viewMethod = typeof(Controller).GetMethod("ViewComponent", viewMethodParameterTypes);

                    ilGenerator.Emit(OpCodes.Callvirt, viewMethod);

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

                    // Make sure next handler has access to previous handler's return value
                    localVariableForPreviousReturnValue = localVariableForReturnValue;
                }
            }

            // Return last handlers return value
            ilGenerator.Emit(OpCodes.Ldloc, localVariableForPreviousReturnValue);
            ilGenerator.Emit(OpCodes.Ret);
        }