From 77e69686947c25eaed1c0fcd0cc259e422d948fe Mon Sep 17 00:00:00 2001 From: Nikolay Pianikov Date: Mon, 20 May 2024 17:13:18 +0300 Subject: [PATCH] #55 Incorrect source generated --- .../Core/Code/BlockCodeBuilder.cs | 100 ++++---- .../Core/Code/StatementCodeBuilder.cs | 9 +- src/Pure.DI.Core/Core/Code/VariableInfo.cs | 17 +- tests/Pure.DI.IntegrationTests/CtorTests.cs | 2 +- .../Pure.DI.IntegrationTests/FactoryTests.cs | 2 +- tests/Pure.DI.IntegrationTests/FuncTests.cs | 219 ++++++++++++++++++ .../ShroedingersCatTests.cs | 4 +- 7 files changed, 273 insertions(+), 80 deletions(-) diff --git a/src/Pure.DI.Core/Core/Code/BlockCodeBuilder.cs b/src/Pure.DI.Core/Core/Code/BlockCodeBuilder.cs index 11405b39b..6770aa9d5 100644 --- a/src/Pure.DI.Core/Core/Code/BlockCodeBuilder.cs +++ b/src/Pure.DI.Core/Core/Code/BlockCodeBuilder.cs @@ -15,13 +15,8 @@ public void Build(BuildContext ctx, in Block block) var localMethodName = $"{Names.EnsureExistsMethodNamePrefix}_{variable.VariableDeclarationName}".Replace("__", "_"); var info = block.Current.Info; - if (info.HasLocalMethod) - { - ctx.Code.AppendLine($"{localMethodName}();"); - return; - } - var code = new LinesBuilder(); + var isEmpty = false; try { var level = ctx.Level; @@ -66,11 +61,27 @@ public void Build(BuildContext ctx, in Block block) code.IncIndent(); ctx = ctx with { Level = level + 1 }; } - + + var content = new LinesBuilder(); foreach (var statement in block.Statements) { - ctx.StatementBuilder.Build(ctx with { Variable = statement.Current, Code = code }, statement); + ctx.StatementBuilder.Build(ctx with { Variable = statement.Current, Code = content }, statement); } + + if (content.Count == 0) + { + isEmpty = true; + return; + } + + if (info.HasLocalMethod) + { + ctx.Code.AppendLine($"{localMethodName}();"); + isEmpty = true; + return; + } + + code.AppendLines(content.Lines); if (!toCheckExistence) { @@ -114,58 +125,37 @@ public void Build(BuildContext ctx, in Block block) } finally { - if (block.Parent is not null - && info is { PerBlockRefCount: > 1 } - && code.Count > 11) + if (!isEmpty) { - var localMethodCode = ctx.LocalFunctionsCode; - if (variable.Node.Binding.SemanticModel.Compilation.GetLanguageVersion() >= LanguageVersion.CSharp9) - { - localMethodCode.AppendLine($"[{Names.MethodImplAttributeName}({Names.MethodImplAggressiveInlining})]"); - } - - localMethodCode.AppendLine($"void {localMethodName}()"); - localMethodCode.AppendLine("{"); - using (localMethodCode.Indent()) + if (block.Parent is not null + && info is { PerBlockRefCount: > 1 } + && code.Count > 11) { - localMethodCode.AppendLines(code.Lines); + var localMethodCode = ctx.LocalFunctionsCode; + if (variable.Node.Binding.SemanticModel.Compilation.GetLanguageVersion() >= LanguageVersion.CSharp9) + { + localMethodCode.AppendLine($"[{Names.MethodImplAttributeName}({Names.MethodImplAggressiveInlining})]"); + } + + localMethodCode.AppendLine($"void {localMethodName}()"); + localMethodCode.AppendLine("{"); + using (localMethodCode.Indent()) + { + localMethodCode.AppendLines(code.Lines); + } + + localMethodCode.AppendLine("}"); + code = new LinesBuilder(); + code.AppendLine($"{localMethodName}();"); + info.HasLocalMethod = true; } - - localMethodCode.AppendLine("}"); - code = new LinesBuilder(); - code.AppendLine($"{localMethodName}();"); - info.HasLocalMethod = true; + + ctx.Code.AppendLines(code.Lines); } - - ctx.Code.AppendLines(code.Lines); } } - private static bool IsNewInstanceRequired(Variable variable) - { - if (variable.Node.Lifetime == Lifetime.Transient) - { - return true; - } - - if (variable.Current.HasCycle) - { - return false; - } - - var owners = variable - .GetPath() - .Skip(1) - .TakeWhile(i => !i.Current.IsLazy) - .OfType() - .ToArray(); - - if (variable.Info.Owners.Intersect(owners).Any()) - { - return false; - } - - variable.Info.Owners.Add(owners.FirstOrDefault()); - return true; - } + private static bool IsNewInstanceRequired(Variable variable) => + variable.Node.Lifetime == Lifetime.Transient + || !variable.Current.HasCycle; } \ No newline at end of file diff --git a/src/Pure.DI.Core/Core/Code/StatementCodeBuilder.cs b/src/Pure.DI.Core/Core/Code/StatementCodeBuilder.cs index b6d7b283f..5c3c8dacd 100644 --- a/src/Pure.DI.Core/Core/Code/StatementCodeBuilder.cs +++ b/src/Pure.DI.Core/Core/Code/StatementCodeBuilder.cs @@ -18,22 +18,15 @@ public void Build(BuildContext ctx, in IStatement statement) switch (statement) { case Variable variable: - if (variable.GetPath().OfType().Any(block => variable.Info.IsCreated(block))) + if (!variable.Info.Create(variable.ParentBlock)) { break; } - variable.Info.MarkAsCreated(variable.ParentBlock); variableBuilder.Build(ctx, variable); break; case Block block: - var blockVariable = block.Current; - if (blockVariable.GetPath().OfType().Any(b => blockVariable.Info.IsCreated(b))) - { - break; - } - blockBuilder.Build(ctx, block); break; } diff --git a/src/Pure.DI.Core/Core/Code/VariableInfo.cs b/src/Pure.DI.Core/Core/Code/VariableInfo.cs index df3b92672..3d01f1c3d 100644 --- a/src/Pure.DI.Core/Core/Code/VariableInfo.cs +++ b/src/Pure.DI.Core/Core/Code/VariableInfo.cs @@ -3,8 +3,7 @@ internal class VariableInfo { private readonly HashSet _perBlockRefCounts = []; - public readonly HashSet Owners = []; - public readonly HashSet Created = []; + private readonly HashSet _parentBlocks = []; public bool HasLocalMethod; public int RefCount { get; private set; } = 1; @@ -16,22 +15,14 @@ public void AddRef(Block parentBlock) RefCount++; _perBlockRefCounts.Add(parentBlock.Id); } - - public bool MarkAsCreated(Block block) - { - return Created.Add(block); - } - public bool IsCreated(Block block) - { - return Created.Contains(block); - } + public bool Create(Block parentBlock) => + _parentBlocks.Add(parentBlock); public void Reset() { _perBlockRefCounts.Clear(); - Owners.Clear(); - Created.Clear(); + _parentBlocks.Clear(); RefCount = 1; HasLocalMethod = false; } diff --git a/tests/Pure.DI.IntegrationTests/CtorTests.cs b/tests/Pure.DI.IntegrationTests/CtorTests.cs index 73b5239fa..3c485bcc0 100644 --- a/tests/Pure.DI.IntegrationTests/CtorTests.cs +++ b/tests/Pure.DI.IntegrationTests/CtorTests.cs @@ -352,7 +352,7 @@ public static void Main() } [Fact] - public async Task ShouldSupportUseParameterLessCtor() + public async Task ShouldSupportUseParameterlessCtor() { // Given diff --git a/tests/Pure.DI.IntegrationTests/FactoryTests.cs b/tests/Pure.DI.IntegrationTests/FactoryTests.cs index 67ebea0c8..a3acd26f1 100644 --- a/tests/Pure.DI.IntegrationTests/FactoryTests.cs +++ b/tests/Pure.DI.IntegrationTests/FactoryTests.cs @@ -1157,7 +1157,7 @@ public static void Main() // Then result.Success.ShouldBeTrue(result); - result.GeneratedCode.Split(Environment.NewLine).Count(i => i.Contains(" = new Sample.Dependency2();")).ShouldBe(1); + result.GeneratedCode.Split(Environment.NewLine).Count(i => i.Contains(" = new Sample.Dependency2();")).ShouldBe(2); } [Fact] diff --git a/tests/Pure.DI.IntegrationTests/FuncTests.cs b/tests/Pure.DI.IntegrationTests/FuncTests.cs index 063dae389..f7c5916f4 100644 --- a/tests/Pure.DI.IntegrationTests/FuncTests.cs +++ b/tests/Pure.DI.IntegrationTests/FuncTests.cs @@ -736,4 +736,223 @@ public static void Main() result.Success.ShouldBeTrue(result); result.StdOut.ShouldBe(["AbcXyz"], result); } + + [Fact] + public async Task ShouldSupportComplexFuncOfEnumerable() + { + // Given + + // When + var result = await """ +using System; +using Pure.DI; +using System.Collections.Generic; + +namespace Sample +{ + class ServiceApplication + { + public ServiceApplication(System.Func factory) + { + factory(true); + } + } + + interface IProcessRegistry + { + } + + class ProcessRegistry: IProcessRegistry + { + } + + class ServiceHostEnvironment + { + public ServiceHostEnvironment( + Func loggerFactory, + Func serviceHostFactory, + bool registerForRestart) + { + serviceHostFactory(); + } + } + + interface ILogger2: IDisposable + { + } + + class Logger2: ILogger2 + { + public void Dispose() + { + } + } + + class LoggerConfiguration + { + public ILogger2 CreateLogger() + { + return new Logger2(); + } + } + + interface IServiceHost + { + } + + interface IConfiguration + { + string this[string key] { get; } + + void Init(); + } + + class AppConfiguration : IConfiguration + { + public string this[string key] => ""; + + void IConfiguration.Init() + { + } + } + + interface ITelemetry + { + } + + class SentryIOTelemetry : ITelemetry + { + public SentryIOTelemetry() + { + } + } + + class TelemetryRuntime: IDisposable + { + public TelemetryRuntime(TelemetryRuntimeParameters parameters) + { + } + + public void Dispose() + { + } + } + + interface IDeploymentContext + { + object GetDeploymentTypeName(); + } + + class DeploymentContext: IDeploymentContext + { + public object GetDeploymentTypeName() + { + return new object(); + } + } + + class TelemetryRuntimeParameters + { + public TelemetryRuntimeParameters( + string mobisystemsServicehost, + string s, + string s1, + object getDeploymentTypeName, + Func func) + { + } + } + + interface IService + { + } + + class PackageUpdaterService: IService + { + public PackageUpdaterService(ITelemetry telemetry, ILogger2 logger2, IConfiguration configuration) + { + } + } + + class ServiceHost: IServiceHost + { + public ServiceHost( + ITelemetry telemetry, System.Func> services, IProcessRegistry processRegistry, ILogger2 logger2) + { + foreach (var service in services()) + { + } + } + } + + partial class Composition + { + private void Setup() => + DI.Setup(nameof(Composition)) + .Root("Root") + .Bind>().To(ctx => + { + ctx.Inject(out IReadOnlyCollection items); + return items; + }) + .Bind().As(Lifetime.Singleton).To(ctx => new ProcessRegistry()) + .Bind>().To>(ctx => (bool registerForRestart) => + { + ctx.Inject(out Func loggerFactory); + ctx.Inject(out Func serviceHostFactory); + return new ServiceHostEnvironment(loggerFactory, serviceHostFactory, registerForRestart); + }) + .Bind().As(Lifetime.Singleton).To(ctx => + { + var configuration = new AppConfiguration(); + ((IConfiguration)configuration).Init(); + return configuration; + }) + .Bind().As(Lifetime.Singleton).To(ctx => + { + //SentryIOTelemetry requires TelemetryRuntime to be initialized first + ctx.Inject(out TelemetryRuntime telemetryRuntime); + return new SentryIOTelemetry(); + }) + .Bind().As(Lifetime.Singleton).To(ctx => + { + ctx.Inject(out IDeploymentContext deploymentContext); + ctx.Inject(out IConfiguration configuration); + + var parameters = new TelemetryRuntimeParameters( + "MobiSystems.ServiceHost", + configuration["TelemetryHost"], + configuration["BuildType"], + deploymentContext.GetDeploymentTypeName(), + (_) => true); + + return new TelemetryRuntime(parameters); + }) + .Bind().As(Lifetime.Singleton).To() + .Bind(1).To() + .Bind().As(Lifetime.Singleton).To(ctx => + { + var logger = new LoggerConfiguration() + .CreateLogger(); + + return logger; + }) + .Bind().As(Lifetime.Singleton).To(); + } + + public class Program + { + public static void Main() + { + var app = new Composition().Root; + Console.WriteLine(app); + } + } +} +""".RunAsync(); + + // Then + result.Success.ShouldBeTrue(result); + result.StdOut.ShouldBe(["Sample.ServiceApplication"], result); + } } \ No newline at end of file diff --git a/tests/Pure.DI.IntegrationTests/ShroedingersCatTests.cs b/tests/Pure.DI.IntegrationTests/ShroedingersCatTests.cs index 40d600059..aadb4484a 100644 --- a/tests/Pure.DI.IntegrationTests/ShroedingersCatTests.cs +++ b/tests/Pure.DI.IntegrationTests/ShroedingersCatTests.cs @@ -106,7 +106,7 @@ public static void Main() result.Success.ShouldBeTrue(result); (result.StdOut.Contains("[Dead cat]") || result.StdOut.Contains("[Alive cat]")).ShouldBeTrue(result); var lines = result.GeneratedCode.Split(Environment.NewLine); - lines.Count(i => i.Contains(" = new Random();")).ShouldBe(1); - lines.Count(i => i.Contains("EnsureExistenceOf_")).ShouldBe(3); + lines.Count(i => i.Contains(" = new Random();")).ShouldBe(1, result); + lines.Count(i => i.Contains("EnsureExistenceOf_")).ShouldBe(3, result); } } \ No newline at end of file