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); }