diff --git a/Examples/ByFeature/FunctionalInjection/FunctionalInjection.fsproj b/Examples/ByFeature/FunctionalInjection/FunctionalInjection.fsproj index 7623056..170a9f5 100644 --- a/Examples/ByFeature/FunctionalInjection/FunctionalInjection.fsproj +++ b/Examples/ByFeature/FunctionalInjection/FunctionalInjection.fsproj @@ -5,6 +5,7 @@ net6.0;net452 + diff --git a/Examples/ByFeature/FunctionalInjection/Logger.fs b/Examples/ByFeature/FunctionalInjection/Logger.fs new file mode 100644 index 0000000..8c9232e --- /dev/null +++ b/Examples/ByFeature/FunctionalInjection/Logger.fs @@ -0,0 +1,23 @@ +module Logger + +open TickSpec +open System + +type LogMessages = string list + +type LoggerContext = { Messages: LogMessages } + +let [] setup () = + { Messages = "Before scenario" |> List.singleton } + +let [] beforeStep (previousMessages: LoggerContext) = + { Messages = "Before step" :: previousMessages.Messages } + +let [] afterStep (previousMessages: LoggerContext) = + { Messages = "After step" :: previousMessages.Messages } + +let [] afterScenario (previousMessages: LoggerContext) = + let allMessages = + "After scenario" :: previousMessages.Messages + |> List.rev + allMessages |> List.iter Console.WriteLine diff --git a/TickSpec/ScenarioGen.fs b/TickSpec/ScenarioGen.fs index 13660f5..a3a154c 100644 --- a/TickSpec/ScenarioGen.fs +++ b/TickSpec/ScenarioGen.fs @@ -334,6 +334,38 @@ let emitArgument else emitValue gen providerField parsers paramType arg +/// Emit arguments through injection +let emitInjectionArguments + (gen:ILGenerator) + (providerField:FieldBuilder) + (parameters: ParameterInfo array) = + parameters + |> Array.iter (fun p -> emitInstance gen providerField p.ParameterType) + +let storeMethodResultInProvider + (gen:ILGenerator) + (providerField:FieldBuilder) + (mi:MethodInfo) = + if mi.ReturnType <> typeof then + gen.Emit(OpCodes.Box,mi.ReturnType) + let local0 = gen.DeclareLocal(typeof).LocalIndex + gen.Emit(OpCodes.Stloc, local0) + + if FSharpType.IsTuple mi.ReturnType then + let types = FSharpType.GetTupleElements mi.ReturnType + for i = 0 to (types.Length - 1) do + let t = types.[i] + let local1 = gen.DeclareLocal(typeof).LocalIndex + + gen.Emit(OpCodes.Ldloc, local0) + gen.Emit(OpCodes.Ldc_I4, i) + gen.EmitCall(OpCodes.Call, typeof.GetMethod("GetTupleField"), null) + gen.Emit(OpCodes.Stloc, local1) + + emitRegisterInstanceCall gen t local1 providerField + else + emitRegisterInstanceCall gen (mi.ReturnType) local0 providerField + /// Defines step method let defineStepMethod doc @@ -442,9 +474,9 @@ let defineStepMethod let bulletsCount = line.Bullets |> Option.count let docCount = line.Doc |> Option.count args.Length + tableCount + bulletsCount + docCount + Array.sub ps a (ps.Length - a) - |> Array.iter (fun (p:ParameterInfo) -> - emitInstance gen providerField p.ParameterType) + |> emitInjectionArguments gen providerField // Emit method invoke if mi.IsStatic then @@ -452,25 +484,7 @@ let defineStepMethod else gen.Emit(OpCodes.Callvirt, mi) - if mi.ReturnType <> typeof then - gen.Emit(OpCodes.Box,mi.ReturnType) - let local0 = gen.DeclareLocal(typeof).LocalIndex - gen.Emit(OpCodes.Stloc, local0) - - if FSharpType.IsTuple mi.ReturnType then - let types = FSharpType.GetTupleElements mi.ReturnType - for i = 0 to (types.Length - 1) do - let t = types.[i] - let local1 = gen.DeclareLocal(typeof).LocalIndex - - gen.Emit(OpCodes.Ldloc, local0) - gen.Emit(OpCodes.Ldc_I4, i) - gen.EmitCall(OpCodes.Call, typeof.GetMethod("GetTupleField"), null) - gen.Emit(OpCodes.Stloc, local1) - - emitRegisterInstanceCall gen t local1 providerField - else - emitRegisterInstanceCall gen (mi.ReturnType) local0 providerField + storeMethodResultInProvider gen providerField mi // Emit return gen.Emit(OpCodes.Ret) @@ -498,11 +512,16 @@ let defineRunMethod // Emit event methods let emitEvents (ms:MethodInfo seq) = ms |> Seq.iter (fun mi -> + mi.GetParameters() + |> emitInjectionArguments gen providerField + if mi.IsStatic then gen.EmitCall(OpCodes.Call, mi, null) else emitInstance gen providerField mi.DeclaringType gen.EmitCall(OpCodes.Callvirt, mi, null) + + storeMethodResultInProvider gen providerField mi ) beforeScenarioEvents |> emitEvents diff --git a/TickSpec/ScenarioRun.fs b/TickSpec/ScenarioRun.fs index 9853083..14e651b 100644 --- a/TickSpec/ScenarioRun.fs +++ b/TickSpec/ScenarioRun.fs @@ -17,9 +17,17 @@ let getInstance (provider:IInstanceProvider) (m:MethodInfo) = else provider.GetService m.DeclaringType /// Invokes specified method with specified parameters -let invoke (provider:IInstanceProvider) (m:MethodInfo) ps = +let invoke (provider:IInstanceProvider) (m: MethodInfo) (ps: obj array) = + let injectionArgs = + let pars = m.GetParameters() + let a = ps.Length + Array.sub pars a (pars.Length - a) + |> Array.map (fun (p:ParameterInfo) -> provider.GetService(p.ParameterType)) + + let args = Array.append ps injectionArgs + let instance = getInstance provider m - let ret = m.Invoke(instance,ps) + let ret = m.Invoke(instance, args) if m.ReturnType <> typeof then if FSharpType.IsTuple m.ReturnType then let types = FSharpType.GetTupleElements m.ReturnType @@ -148,15 +156,9 @@ let invokeStep else failwithf "Expected a Table or array argument at position %d" args.Length | None,None,Some doc -> [|box doc|] | _,_,_ -> [||] - let args = - let stArgs = Array.append args tail - let injectionArgs = - let pars = meth.GetParameters() - let a = stArgs.Length - Array.sub pars a (pars.Length - a) - |> Array.map (fun (p:ParameterInfo) -> provider.GetService(p.ParameterType)) - Array.append stArgs injectionArgs - invoke provider meth args + + Array.append args tail + |> invoke provider meth /// Generate scenario execution function let generate events parsers (scenario: ScenarioMetadata, lines) (serviceProviderFactory: Ref IInstanceProvider>) =