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