From 2f7ef9e1b6930cc930b320a81b0e2555c4887564 Mon Sep 17 00:00:00 2001 From: Nikolay Pianikov Date: Fri, 13 Sep 2024 11:23:03 +0300 Subject: [PATCH] Support for injection of ref and out arguments for methods --- src/Pure.DI.Core/Core/Code/BuildTools.cs | 25 ++- .../Core/Code/ImplementationCodeBuilder.cs | 18 +- src/Pure.DI.Core/Core/Code/Variable.cs | 3 +- src/Pure.DI.Core/Generator.cs | 1 + .../MethodInjectionTests.cs | 177 +++++++++++++++++- 5 files changed, 216 insertions(+), 8 deletions(-) diff --git a/src/Pure.DI.Core/Core/Code/BuildTools.cs b/src/Pure.DI.Core/Core/Code/BuildTools.cs index 28c554224..70be35c48 100644 --- a/src/Pure.DI.Core/Core/Code/BuildTools.cs +++ b/src/Pure.DI.Core/Core/Code/BuildTools.cs @@ -5,7 +5,8 @@ namespace Pure.DI.Core.Code; internal class BuildTools( IFilter filter, ITypeResolver typeResolver, - IBaseSymbolsProvider baseSymbolsProvider) + IBaseSymbolsProvider baseSymbolsProvider, + [Tag("Injection")] IIdGenerator idGenerator) : IBuildTools { public void AddPureHeader(LinesBuilder code) @@ -17,8 +18,28 @@ public void AddPureHeader(LinesBuilder code) public string GetDeclaration(Variable variable, string separator = " ") => variable.IsDeclared ? "" : $"{typeResolver.Resolve(variable.Setup, variable.InstanceType)}{separator}"; - + public string OnInjected(BuildContext ctx, Variable variable) + { + var injection = OnInjectedInternal(ctx, variable); + var refKind = variable.RefKind switch + { + RefKind.Ref or RefKind.RefReadOnlyParameter => "ref", + RefKind.Out => "out", + _ => "" + }; + + if (!string.IsNullOrEmpty(refKind)) + { + var localVarName = $"{variable.VariableDeclarationName}_{refKind}{idGenerator.Generate()}"; + ctx.Code.AppendLine($"{variable.InstanceType} {localVarName} = {injection};"); + injection = $"{refKind} {localVarName}"; + } + + return injection; + } + + private string OnInjectedInternal(BuildContext ctx, Variable variable) { var variableCode = variable.VariableCode; if (variableCode == variable.VariableName) diff --git a/src/Pure.DI.Core/Core/Code/ImplementationCodeBuilder.cs b/src/Pure.DI.Core/Core/Code/ImplementationCodeBuilder.cs index dc1b426a1..961dfed28 100644 --- a/src/Pure.DI.Core/Core/Code/ImplementationCodeBuilder.cs +++ b/src/Pure.DI.Core/Core/Code/ImplementationCodeBuilder.cs @@ -146,7 +146,21 @@ private static void PropertyInjection(BuildContext ctx, DpProperty property, Var private static void MethodInjection(BuildContext ctx, DpMethod method, ImmutableArray methodArgs) { - var args = string.Join(", ", methodArgs.Select(i => ctx.BuildTools.OnInjected(ctx, i))); - ctx.Code.AppendLine($"{ctx.Variable.VariableName}.{method.Method.Name}({args});"); + var args = new List(); + for (var index = 0; index < methodArgs.Length; index++) + { + var variable = methodArgs[index]; + if (index < method.Parameters.Length) + { + variable = variable with + { + RefKind = method.Parameters[index].ParameterSymbol.RefKind + }; + } + + args.Add(ctx.BuildTools.OnInjected(ctx, variable)); + } + + ctx.Code.AppendLine($"{ctx.Variable.VariableName}.{method.Method.Name}({string.Join(", ", args)});"); } } \ No newline at end of file diff --git a/src/Pure.DI.Core/Core/Code/Variable.cs b/src/Pure.DI.Core/Core/Code/Variable.cs index 4d4efaf7f..b6a434762 100644 --- a/src/Pure.DI.Core/Core/Code/Variable.cs +++ b/src/Pure.DI.Core/Core/Code/Variable.cs @@ -10,7 +10,8 @@ internal record Variable( VariableInfo Info, bool IsLazy, bool HasCycle, - string NameOverride = "") + string NameOverride = "", + RefKind RefKind = RefKind.None) : IStatement { private string? _variableCode; diff --git a/src/Pure.DI.Core/Generator.cs b/src/Pure.DI.Core/Generator.cs index 1aa183cb2..7e4e518be 100644 --- a/src/Pure.DI.Core/Generator.cs +++ b/src/Pure.DI.Core/Generator.cs @@ -139,6 +139,7 @@ private void Setup() => DI.Setup() .Bind().To() .Bind("UniqueTags").To() .Bind("GenericType").To() + .Bind("Injection").To() .Bind().To() .Bind().To>(); } \ No newline at end of file diff --git a/tests/Pure.DI.IntegrationTests/MethodInjectionTests.cs b/tests/Pure.DI.IntegrationTests/MethodInjectionTests.cs index 2a4bdb13b..94863c1de 100644 --- a/tests/Pure.DI.IntegrationTests/MethodInjectionTests.cs +++ b/tests/Pure.DI.IntegrationTests/MethodInjectionTests.cs @@ -76,7 +76,7 @@ public class Program public static void Main() { var composition = new Composition("dep"); - var service = composition.Service; + var service = composition.Service; } } } @@ -173,7 +173,7 @@ public class Program public static void Main() { var composition = new Composition("dep"); - var service = composition.Service; + var service = composition.Service; } } } @@ -266,7 +266,7 @@ public class Program public static void Main() { var composition = new Composition("dep"); - var service = composition.Service; + var service = composition.Service; } } } @@ -276,4 +276,175 @@ public static void Main() result.Success.ShouldBeTrue(result); result.StdOut.ShouldBe(["Initialize dep", "Initialize dep", "Initialize", "True", "Activate"], result); } + + [Fact] + public async Task ShouldSupportMethodInjectionWhenRefParam() + { + // Given + + // When + var result = await """ +using System; +using Pure.DI; + +namespace Sample +{ + interface IDependency {} + + class Dependency: IDependency + { + [Ordinal(1)] + internal void Initialize([Tag(374)] ref string depName) + { + Console.WriteLine($"Initialize {depName}"); + } + } + + interface IService + { + IDependency Dep { get; } + } + + class Service: IService + { + private IDependency _dep; + public Service(IDependency dep) + { + _dep = dep; + } + + public IDependency Dep => _dep; + + public void Run() + { + Console.WriteLine("Run"); + } + + [Ordinal(7)] + public void Activate() + { + Console.WriteLine("Activate"); + } + + [Ordinal(1)] + internal void Initialize(IDependency dep) + { + Console.WriteLine("Initialize"); + Console.WriteLine(dep != Dep); + } + } + + static class Setup + { + private static void SetupComposition() + { + DI.Setup("Composition") + .Bind().To() + .Bind().To() + .Arg("depName", 374) + .Root("Service"); + } + } + + public class Program + { + public static void Main() + { + var composition = new Composition("dep"); + var service = composition.Service; + } + } +} +""".RunAsync(); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["Initialize dep", "Initialize dep", "Initialize", "True", "Activate"], result); + } + + [Fact] + public async Task ShouldSupportMethodInjectionWhenOutParam() + { + // Given + + // When + var result = await """ +using System; +using Pure.DI; + +namespace Sample +{ + interface IDependency {} + + class Dependency: IDependency + { + [Ordinal(1)] + internal void Initialize([Tag(374)] out string depName) + { + depName = ""; + Console.WriteLine($"Initialize"); + } + } + + interface IService + { + IDependency Dep { get; } + } + + class Service: IService + { + private IDependency _dep; + public Service(IDependency dep) + { + _dep = dep; + } + + public IDependency Dep => _dep; + + public void Run() + { + Console.WriteLine("Run"); + } + + [Ordinal(7)] + public void Activate() + { + Console.WriteLine("Activate"); + } + + [Ordinal(1)] + internal void Initialize(IDependency dep) + { + Console.WriteLine("Initialize"); + Console.WriteLine(dep != Dep); + } + } + + static class Setup + { + private static void SetupComposition() + { + DI.Setup("Composition") + .Bind().To() + .Bind().To() + .Arg("depName", 374) + .Root("Service"); + } + } + + public class Program + { + public static void Main() + { + var composition = new Composition("dep"); + var service = composition.Service; + } + } +} +""".RunAsync(); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["Initialize", "Initialize", "Initialize", "True", "Activate"], result); + } } \ No newline at end of file