Skip to content

Commit

Permalink
#44 Disposable Instances Handling
Browse files Browse the repository at this point in the history
  • Loading branch information
NikolayPianikov committed Mar 20, 2024
1 parent 96c4e75 commit 03ee3d8
Show file tree
Hide file tree
Showing 28 changed files with 646 additions and 248 deletions.
75 changes: 75 additions & 0 deletions src/Pure.DI.Core/Components/Api.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1087,6 +1087,70 @@ internal enum Tag
/// </example>
Type
}

/// <summary>
/// Gives the opportunity to collect disposable objects.
/// </summary>
public class Owned : global::System.IDisposable
{
private bool _isDisposed;
private global::System.Collections.Generic.List<global::System.IDisposable> _disposables
= new global::System.Collections.Generic.List<global::System.IDisposable>();

/// <summary>
/// Adds a disposable instance.
/// </summary>
/// <param name="disposable">The disposable instance.</param>
[global::System.Runtime.CompilerServices.MethodImpl((global::System.Runtime.CompilerServices.MethodImplOptions)256)]
public void Add(global::System.IDisposable disposable)
{
lock (_disposables)
{
_disposables.Add(disposable);
}
}

/// <inheritdoc />
[global::System.Runtime.CompilerServices.MethodImpl((global::System.Runtime.CompilerServices.MethodImplOptions)512)]
public void Dispose()
{
global::System.Collections.Generic.List<global::System.IDisposable> disposables;
lock (_disposables)
{
if (_isDisposed)
{
return;
}

_isDisposed = true;
disposables = _disposables;
}

disposables.Reverse();
global::System.Collections.Generic.List<global::System.Exception> errors = null;
foreach (var disposable in disposables)
{
try
{
disposable.Dispose();
}
catch (global::System.Exception error)
{
if (errors == null)
{
errors = new global::System.Collections.Generic.List<global::System.Exception>();
}

errors.Add(error);
}
}

if (errors != null)
{
throw new global::System.AggregateException(errors);
}
}
}

/// <summary>
/// An API for a Dependency Injection setup.
Expand Down Expand Up @@ -1374,6 +1438,9 @@ internal interface IConfiguration
/// <returns>Reference to the setup continuation chain.</returns>
/// <seealso cref="Pure.DI.Hint"/>
IConfiguration Hint(Hint hint, string value);

IConfiguration Accumulate<T, TAccumulator>(Lifetime lifetime)
where TAccumulator: new();
}

/// <summary>
Expand Down Expand Up @@ -1929,6 +1996,14 @@ public IConfiguration Hint(Hint hint, string value)
{
return Configuration.Shared;
}

/// <inheritdoc />
[global::System.Runtime.CompilerServices.MethodImpl((global::System.Runtime.CompilerServices.MethodImplOptions)256)]
public IConfiguration Accumulate<T, TAccumulator>(Lifetime lifetime)
where TAccumulator: new()
{
return Configuration.Shared;
}
}

private sealed class Binding : IBinding
Expand Down
12 changes: 12 additions & 0 deletions src/Pure.DI.Core/Core/ApiInvocationProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ public void ProcessInvocation(
ImmutableArray<MdTypeAttribute>.Empty,
ImmutableArray<MdTagAttribute>.Empty,
ImmutableArray<MdOrdinalAttribute>.Empty,
ImmutableArray<MdAccumulator>.Empty,
comments.FilterHints(invocationComments).ToList()));
break;

Expand Down Expand Up @@ -234,6 +235,17 @@ public void ProcessInvocation(
metadataVisitor.VisitOrdinalAttribute(new MdOrdinalAttribute(semanticModel, invocation.ArgumentList, semanticModel.GetTypeSymbol<ITypeSymbol>(ordinalAttributeType, cancellationToken), BuildConstantArgs<object>(semanticModel, invocation.ArgumentList.Arguments) is [int positionVal] ? positionVal : 0));
}

break;

case nameof(IConfiguration.Accumulate):
if (genericName.TypeArgumentList.Arguments is [var typeSyntax, var accumulatorTypeSyntax]
&& arguments.GetArgs(invocation.ArgumentList, "lifetime") is [{ Expression: {} lifetimeArgExpression }])
{
var typeSymbol = semanticModel.GetTypeSymbol<INamedTypeSymbol>(typeSyntax, cancellationToken);
var accumulatorTypeSymbol = semanticModel.GetTypeSymbol<INamedTypeSymbol>(accumulatorTypeSyntax, cancellationToken);
metadataVisitor.VisitAccumulator(new MdAccumulator(semanticModel, invocation, typeSymbol, accumulatorTypeSymbol, semanticModel.GetConstantValue<Lifetime>(lifetimeArgExpression)));
}

break;
}

Expand Down
7 changes: 7 additions & 0 deletions src/Pure.DI.Core/Core/Code/Accumulator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Pure.DI.Core.Code;

internal record Accumulator(
string Name,
bool IsDeclared,
ITypeSymbol Type,
ITypeSymbol AccumulatorType);
26 changes: 25 additions & 1 deletion src/Pure.DI.Core/Core/Code/BlockCodeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,23 @@ variable.Node.Lifetime is Lifetime.Singleton or Lifetime.Scoped
parent = $"{Names.ParentFieldName}.";
}

var accumulators = new List<Accumulator>();
var uniqueAccumulators = ctx.Accumulators
.Where(accumulator => !accumulator.IsDeclared)
.GroupBy(i => i.AccumulatorType, SymbolEqualityComparer.Default)
.Select(i => i.First());

foreach (var accumulator in uniqueAccumulators)
{
code.AppendLine($"var {accumulator.Name} = new {accumulator.AccumulatorType}();");
accumulators.Add(accumulator with { IsDeclared = true });
}

if (accumulators.Count > 0)
{
ctx = ctx with { Accumulators = accumulators.ToImmutableArray() };
}

if (toCheckExistence)
{
var checkExpression = variable.InstanceType.IsValueType
Expand All @@ -62,7 +79,14 @@ variable.Node.Lifetime is Lifetime.Singleton or Lifetime.Scoped

foreach (var statement in block.Statements)
{
ctx.StatementBuilder.Build(ctx with { Variable = statement.Current, Code = code }, statement);
if (block.Current != statement.Current)
{
ctx.StatementBuilder.Build(ctx with { Variable = statement.Current, Code = code }, statement);
}
else
{
ctx.StatementBuilder.Build(ctx with { Variable = statement.Current, Code = code }, statement);
}
}

if (variable.Node.Lifetime is Lifetime.Singleton)
Expand Down
3 changes: 2 additions & 1 deletion src/Pure.DI.Core/Core/Code/BuildContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ internal record BuildContext(
LinesBuilder Code,
LinesBuilder LocalFunctionsCode,
object? ContextTag,
bool? LockIsRequired);
bool? LockIsRequired,
ImmutableArray<Accumulator> Accumulators);
23 changes: 19 additions & 4 deletions src/Pure.DI.Core/Core/Code/BuildTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
// ReSharper disable ClassNeverInstantiated.Global
namespace Pure.DI.Core.Code;

internal class BuildTools(IFilter filter, ITypeResolver typeResolver) : IBuildTools
internal class BuildTools(
IFilter filter,
ITypeResolver typeResolver,
IBaseSymbolsProvider baseSymbolsProvider)
: IBuildTools
{
public void AddPureHeader(LinesBuilder code)
{
Expand Down Expand Up @@ -42,9 +46,19 @@ public IEnumerable<Line> OnCreated(BuildContext ctx, Variable variable)
return Array.Empty<Line>();
}

var baseTypes =
baseSymbolsProvider.GetBaseSymbols(variable.InstanceType)
.Concat(Enumerable.Repeat(variable.InstanceType, 1))
.ToImmutableHashSet(SymbolEqualityComparer.Default);

var lines = ctx.Accumulators
.Where(i => baseTypes.Contains(i.Type))
.Select(i => new Line(0, $"{i.Name}.Add({variable.VariableName});"))
.ToList();

if (!ctx.DependencyGraph.Source.Hints.IsOnNewInstanceEnabled)
{
return Array.Empty<Line>();
return lines;
}

if (!filter.IsMeetRegularExpression(
Expand All @@ -53,11 +67,12 @@ public IEnumerable<Line> OnCreated(BuildContext ctx, Variable variable)
(Hint.OnNewInstanceTagRegularExpression, variable.Injection.Tag.ValueToString()),
(Hint.OnNewInstanceLifetimeRegularExpression, variable.Node.Lifetime.ValueToString())))
{
return Array.Empty<Line>();
return lines;
}

var tag = GetTag(ctx, variable);
return [new Line(0, $"{Names.OnNewInstanceMethodName}<{typeResolver.Resolve(variable.InstanceType)}>(ref {variable.VariableName}, {tag.ValueToString()}, {variable.Node.Lifetime.ValueToString()})" + ";")];
lines.Insert(0, new Line(0, $"{Names.OnNewInstanceMethodName}<{typeResolver.Resolve(variable.InstanceType)}>(ref {variable.VariableName}, {tag.ValueToString()}, {variable.Node.Lifetime.ValueToString()});"));
return lines;
}

private static object? GetTag(BuildContext ctx, Variable variable)
Expand Down
3 changes: 2 additions & 1 deletion src/Pure.DI.Core/Core/Code/CompositionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public CompositionCode Build(DependencyGraph graph)
new LinesBuilder(),
new LinesBuilder(),
root.Injection.Tag != MdTag.ContextTag ? root.Injection.Tag : default,
default);
default,
root.Node.Accumulators.ToImmutableArray());

foreach (var perResolveVar in map.GetPerResolves())
{
Expand Down
5 changes: 4 additions & 1 deletion src/Pure.DI.Core/Core/Code/ConstructCodeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,15 @@ public void Build(BuildContext ctx, in DpConstruct construct)
case MdConstructKind.AsyncEnumerable:
BuildEnumerable(ctx, construct,"async ");
break;

case MdConstructKind.Accumulator:
break;

default:
throw new ArgumentOutOfRangeException();
}
}

private void BuildEnumerable(BuildContext ctx, in DpConstruct enumerable, string methodPrefix = "")
{
var variable = ctx.Variable;
Expand Down
14 changes: 13 additions & 1 deletion src/Pure.DI.Core/Core/Code/FactoryCodeBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// ReSharper disable ClassNeverInstantiated.Global
// ReSharper disable ForeachCanBePartlyConvertedToQueryUsingAnotherGetEnumerator
// ReSharper disable MergeIntoPattern
namespace Pure.DI.Core.Code;

internal class FactoryCodeBuilder(
Expand Down Expand Up @@ -54,6 +55,17 @@ public void Build(BuildContext ctx, in DpFactory factory)
using var resolvers = injections
.Zip(variable.Args, (injection, argument) => (injection, argument))
.GetEnumerator();

var injectionsCtx = ctx;
if (variable.IsLazy && variable.Node.Accumulators.Count > 0)
{
injectionsCtx = injectionsCtx with
{
Accumulators = injectionsCtx.Accumulators.AddRange(
variable.Node.Accumulators
.Select(accumulator => accumulator with { IsDeclared = false }))
};
}

var indent = new Indent(0);
var text = syntaxNode.GetText();
Expand All @@ -66,7 +78,7 @@ public void Build(BuildContext ctx, in DpFactory factory)
var (injection, argument) = resolvers.Current;
using (code.Indent(indent.Value))
{
ctx.StatementBuilder.Build(ctx with { Level = level, Variable = argument.Current, LockIsRequired = lockIsRequired }, argument);
ctx.StatementBuilder.Build(injectionsCtx with { Level = level, Variable = argument.Current, LockIsRequired = lockIsRequired }, argument);
code.AppendLine($"{(injection.DeclarationRequired ? "var " : "")}{injection.VariableName} = {ctx.BuildTools.OnInjected(ctx, argument.Current)};");
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/Pure.DI.Core/Core/Code/ImplementationCodeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ public void Build(BuildContext ctx, in DpImplementation implementation)
}

var onCreatedStatements = ctx.BuildTools.OnCreated(ctx, ctx.Variable).ToArray();
var hasOnCreatedHandler = ctx.BuildTools.OnCreated(ctx, variable).Any();
var hasOnCreatedStatements = ctx.BuildTools.OnCreated(ctx, variable).Any();
var hasAlternativeInjections = visits.Any();
var tempVariableInit =
ctx.DependencyGraph.Source.Hints.IsThreadSafeEnabled
&& ctx.Variable.Node.Lifetime is not Lifetime.Transient and not Lifetime.PerBlock
&& (hasAlternativeInjections || hasOnCreatedHandler);
&& (hasAlternativeInjections || hasOnCreatedStatements);

if (tempVariableInit)
{
Expand All @@ -80,7 +80,7 @@ public void Build(BuildContext ctx, in DpImplementation implementation)
if (variable.Node.Lifetime is not Lifetime.Transient
|| hasAlternativeInjections
|| tempVariableInit
|| hasOnCreatedHandler)
|| hasOnCreatedStatements)
{
ctx.Code.Append($"{ctx.BuildTools.GetDeclaration(variable)}{ctx.Variable.VariableName} = ");
ctx.Code.Append(instantiation);
Expand Down
Loading

0 comments on commit 03ee3d8

Please sign in to comment.