Skip to content

Commit cc69f43

Browse files
Improved object composition
1 parent f6d746a commit cc69f43

74 files changed

Lines changed: 2674 additions & 152 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

build/Core/Settings.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Build.Core;
99
[SuppressMessage("Performance", "CA1822:Mark members as static")]
1010
class Settings(Properties properties, Versions versions)
1111
{
12-
public static readonly VersionRange VersionRange = VersionRange.Parse("2.1.*");
12+
public static readonly VersionRange VersionRange = VersionRange.Parse("2.2.*");
1313

1414
private readonly Lazy<NuGetVersion> _currentVersion = new(() => GetVersion(versions));
1515

src/Pure.DI.Core/Core/BindingsFactory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public MdBinding CreateAccumulatorBinding(
6363
targetNode.Binding.SemanticModel,
6464
ImmutableArray.Create(new MdContract(targetNode.Binding.SemanticModel, accumulator.Source, accumulator.AccumulatorType, ContractKind.Implicit, ImmutableArray<MdTag>.Empty)),
6565
ImmutableArray<MdTag>.Empty,
66-
new MdLifetime(targetNode.Binding.SemanticModel, accumulator.Source, Lifetime.Transient),
66+
new MdLifetime(targetNode.Binding.SemanticModel, accumulator.Source, Lifetime.PerBlock),
6767
null,
6868
null,
6969
null,

src/Pure.DI.Core/Core/Code/BlockCodeBuilder.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,21 @@ public void Build(BuildContext ctx, in Block block)
3030
var isThreadSafe = ctx.DependencyGraph.Source.Hints.IsThreadSafeEnabled;
3131
var lockIsRequired = ctx.LockIsRequired ?? isThreadSafe;
3232
var toCheckExistence = variable.Node.Lifetime is Lifetime.Singleton or Lifetime.Scoped or Lifetime.PerResolve;
33-
var uniqueAccumulators = ctx.Accumulators
33+
/*var uniqueAccumulators = ctx.Accumulators
3434
.Where(accumulator => !accumulator.IsDeclared)
3535
.GroupBy(i => i.Name)
36-
.Select(i => i.First());
36+
.Select(i => i.First());*/
3737

38-
foreach (var accumulator in uniqueAccumulators)
38+
/*foreach (var accumulator in uniqueAccumulators)
3939
{
4040
code.AppendLine($"var {accumulator.Name} = new {accumulator.AccumulatorType}();");
41-
}
41+
}*/
4242

43-
var accumulators = ctx.Accumulators.Select(accumulator => accumulator with { IsDeclared = true }).ToList();
43+
/*var accumulators = ctx.Accumulators.Select(accumulator => accumulator with { IsDeclared = true }).ToList();
4444
if (accumulators.Count > 0)
4545
{
4646
ctx = ctx with { Accumulators = accumulators.ToImmutableArray(), AvoidLocalFunction = false};
47-
}
47+
}*/
4848

4949
if (toCheckExistence)
5050
{

src/Pure.DI.Core/Core/Code/BuildTools.cs

Lines changed: 152 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
// ReSharper disable MoveLocalFunctionAfterJumpStatement
55
namespace Pure.DI.Core.Code;
66

7+
using v2;
78
using static LinesBuilderExtensions;
8-
using static Tag;
99

1010
sealed class BuildTools(
1111
IFilter filter,
1212
ITypeResolver typeResolver,
1313
IBaseSymbolsProvider baseSymbolsProvider,
14-
[Tag(Injection)] IIdGenerator idGenerator,
14+
[Tag(Tag.Injection)] IIdGenerator idGenerator,
1515
ILocks locks,
1616
ISymbolNames symbolNames,
1717
ICompilations compilations)
@@ -75,10 +75,10 @@ public IEnumerable<Line> OnCreated(BuildContext ctx, Variable variable)
7575
var accLines = ctx.Accumulators
7676
.Where(i => FilterAccumulator(i, variable.Node.Lifetime))
7777
.Where(i => baseTypes.Value.Contains(i.Type))
78-
.GroupBy(i => i.Name)
78+
.GroupBy(i => i.VarInjection.Var.Name)
7979
.Select(i => i.First())
80-
.OrderBy(i => i.Name)
81-
.Select(i => new Line(0, $"{i.Name}.Add({variable.VariableName});"))
80+
.OrderBy(i => i.VarInjection.Var.Name)
81+
.Select(i => new Line(0, $"{i.VarInjection.Var.Name}.Add({variable.VariableName});"))
8282
.ToList();
8383

8484
if (lockIsRequired && accLines.Count > 0)
@@ -129,6 +129,11 @@ public void AddAggressiveInlining(LinesBuilder code)
129129
code.AppendLine($"[{Names.MethodImplAttributeName}(({Names.MethodImplOptionsName})256)]");
130130
}
131131

132+
public string GetDeclaration(CodeContext ctx, VarDeclaration varDeclaration, string separator = " ")
133+
{
134+
return varDeclaration.IsDeclared ? "" : $"{typeResolver.Resolve(ctx.RootContext.Graph.Source, varDeclaration.InstanceType)}{separator}";
135+
}
136+
132137
private string OnInjectedInternal(BuildContext ctx, Variable variable)
133138
{
134139
var variableCode = variable.VariableCode;
@@ -176,10 +181,10 @@ private static bool FilterAccumulator(Accumulator accumulator, Lifetime lifetime
176181
return false;
177182
}
178183

179-
if (accumulator.IsRoot)
184+
/*if (accumulator.IsRoot)
180185
{
181186
return true;
182-
}
187+
}*/
183188

184189
return lifetime is not (Lifetime.Singleton or Lifetime.Scoped or Lifetime.PerResolve);
185190
}
@@ -194,4 +199,144 @@ private static bool FilterAccumulator(Accumulator accumulator, Lifetime lifetime
194199

195200
return tag;
196201
}
202+
203+
public IEnumerable<Line> OnCreated(CodeContext ctx, VarInjection varInjection)
204+
{
205+
if (varInjection.Var.AbstractNode.Arg is not null)
206+
{
207+
return [];
208+
}
209+
210+
var baseTypes = new Lazy<ImmutableHashSet<ISymbol?>>(() =>
211+
baseSymbolsProvider.GetBaseSymbols(varInjection.Var.InstanceType, (_, _) => true)
212+
.ToImmutableHashSet(SymbolEqualityComparer.Default));
213+
214+
var accLines = ctx.Accumulators
215+
.Where(acc => acc.Lifetime == varInjection.Var.AbstractNode.Lifetime)
216+
.Where(acc => baseTypes.Value.Contains(acc.Type))
217+
.GroupBy(acc => acc.VarInjection.Var.Name)
218+
.Select(grouping => grouping.First())
219+
.OrderBy(i => i.VarInjection.Var.Name)
220+
.Select(i => new Line(0, $"{i.VarInjection.Var.Name}.Add({varInjection.Var.Name});"))
221+
.ToList();
222+
223+
var code = new LinesBuilder();
224+
if (ctx.IsLockRequired && accLines.Count > 0)
225+
{
226+
locks.AddLockStatements(ctx.RootContext.Graph, code, false);
227+
code.AppendLine(BlockStart);
228+
code.IncIndent();
229+
}
230+
231+
code.AppendLines(accLines);
232+
233+
if (ctx.IsLockRequired && accLines.Count > 0)
234+
{
235+
code.DecIndent();
236+
code.AppendLine(BlockFinish);
237+
locks.AddUnlockStatements(ctx.RootContext.Graph, code, false);
238+
}
239+
240+
if (!ctx.RootContext.Graph.Source.Hints.IsOnNewInstanceEnabled)
241+
{
242+
return code.Lines;
243+
}
244+
245+
var tag = GetTag(ctx, varInjection);
246+
string GetTypeName() => symbolNames.GetName(varInjection.Var.InstanceType);
247+
string GetTagName() => tag.ValueToString();
248+
string GetLifetimeName() => varInjection.Var.AbstractNode.Lifetime.ValueToString();
249+
if (!filter.IsMeet(
250+
ctx.RootContext.Graph.Source,
251+
(Hint.OnNewInstanceImplementationTypeNameRegularExpression, Hint.OnNewInstanceImplementationTypeNameWildcard, GetTypeName),
252+
(Hint.OnNewInstanceTagRegularExpression, Hint.OnNewInstanceTagWildcard, GetTagName),
253+
(Hint.OnNewInstanceLifetimeRegularExpression, Hint.OnNewInstanceLifetimeWildcard, GetLifetimeName)))
254+
{
255+
return code.Lines;
256+
}
257+
258+
var lines = new List<Line>
259+
{
260+
new(0, $"{Names.OnNewInstanceMethodName}<{typeResolver.Resolve(ctx.RootContext.Graph.Source, varInjection.Var.InstanceType)}>(ref {varInjection.Var.Name}, {tag.ValueToString()}, {varInjection.Var.AbstractNode.Lifetime.ValueToString()});")
261+
};
262+
263+
lines.AddRange(code.Lines);
264+
return lines;
265+
}
266+
267+
public string OnInjected(CodeContext ctx, VarInjection varInjection)
268+
{
269+
var injection = OnInjectedInternal(ctx, varInjection);
270+
var refKind = varInjection.Var.Declaration.RefKind switch
271+
{
272+
RefKind.Ref
273+
#if ROSLYN4_8_OR_GREATER
274+
or RefKind.RefReadOnlyParameter
275+
#endif
276+
=> "ref",
277+
RefKind.Out => "out",
278+
_ => ""
279+
};
280+
281+
if (!string.IsNullOrEmpty(refKind))
282+
{
283+
var localVarName = $"{varInjection.Var.Name}_{refKind}{idGenerator.Generate()}";
284+
ctx.Lines.AppendLine($"{varInjection.Var.InstanceType} {localVarName} = {injection};");
285+
injection = $"{refKind} {localVarName}";
286+
}
287+
288+
return injection;
289+
}
290+
291+
private string OnInjectedInternal(CodeContext ctx, VarInjection varInjection)
292+
{
293+
var variableCode = varInjection.Var.CodeExpression;
294+
if (variableCode == varInjection.Var.Name)
295+
{
296+
var hasCycle = varInjection.Var.HasCycle ?? false;
297+
var skipNotNullCheck =
298+
varInjection.Var.InstanceType.IsReferenceType
299+
&& ctx.RootContext.Graph.Source.SemanticModel.Compilation.Options.NullableContextOptions != NullableContextOptions.Disable
300+
&& (hasCycle || varInjection.Var.AbstractNode.Lifetime is Lifetime.Singleton or Lifetime.Scoped or Lifetime.PerResolve);
301+
302+
if (skipNotNullCheck && (hasCycle || varInjection.Var.AbstractNode.Lifetime is Lifetime.Singleton or Lifetime.Scoped or Lifetime.PerResolve))
303+
{
304+
variableCode = $"{variableCode}";
305+
}
306+
}
307+
308+
if (!ctx.RootContext.Graph.Source.Hints.IsOnDependencyInjectionEnabled)
309+
{
310+
return variableCode;
311+
}
312+
313+
var tag = GetTag(ctx, varInjection);
314+
string GetTypeName() => typeResolver.Resolve(ctx.RootContext.Graph.Source, varInjection.Var.InstanceType).Name;
315+
string GetContractName() => symbolNames.GetName(varInjection.ContractType);
316+
string GetTagName() => tag.ValueToString();
317+
string GetLifetimeName() => varInjection.Var.AbstractNode.Lifetime.ValueToString();
318+
// ReSharper disable once ConvertIfStatementToReturnStatement
319+
if (!filter.IsMeet(
320+
ctx.RootContext.Graph.Source,
321+
(Hint.OnDependencyInjectionImplementationTypeNameRegularExpression, Hint.OnDependencyInjectionImplementationTypeNameWildcard, GetTypeName),
322+
(Hint.OnDependencyInjectionContractTypeNameRegularExpression, Hint.OnDependencyInjectionContractTypeNameWildcard, GetContractName),
323+
(Hint.OnDependencyInjectionTagRegularExpression, Hint.OnDependencyInjectionTagWildcard, GetTagName),
324+
(Hint.OnDependencyInjectionLifetimeRegularExpression, Hint.OnDependencyInjectionLifetimeWildcard, GetLifetimeName)))
325+
{
326+
return variableCode;
327+
}
328+
329+
return $"{Names.OnDependencyInjectionMethodName}<{typeResolver.Resolve(ctx.RootContext.Graph.Source, varInjection.ContractType)}>({variableCode}, {tag.ValueToString()}, {varInjection.Var.AbstractNode.Lifetime.ValueToString()})";
330+
}
331+
332+
private static object? GetTag(CodeContext ctx, VarInjection varInjection)
333+
{
334+
var tag = varInjection.Injection.Tag;
335+
if (ReferenceEquals(tag, MdTag.ContextTag))
336+
{
337+
tag = ctx.ContextTag;
338+
}
339+
340+
return tag;
341+
}
197342
}

src/Pure.DI.Core/Core/Code/ClassCommenter.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public void AddComments(CompositionCode composition, Unit unit)
2020

2121
var classComments = composition.Source.Source.Comments;
2222
var code = composition.Code;
23-
if (classComments.Count <= 0 && composition.Roots.Length <= 0)
23+
if (classComments.Count <= 0 && composition.PublicRoots.Length <= 0)
2424
{
2525
return;
2626
}
@@ -47,7 +47,7 @@ public void AddComments(CompositionCode composition, Unit unit)
4747
code.AppendLine("/// </para>");
4848
}
4949

50-
var orderedRoots = composition.Roots
50+
var orderedRoots = composition.PublicRoots
5151
.OrderByDescending(root => root.IsPublic)
5252
.ThenBy(root => root.DisplayName)
5353
.ThenBy(root => root.Node.Binding.Id)
@@ -134,7 +134,7 @@ IReadOnlyCollection<string> CreateRootDescriptions(Root root) =>
134134
code.AppendLine("/// <example>");
135135
code.AppendLine($"/// This example shows how to get an instance of type {formatter.FormatRef(root.Node.Type)} using the composition root {formatter.FormatRef(composition.Source.Source, root)}:");
136136
code.AppendLine("/// <code>");
137-
code.AppendLine($"/// {(composition.TotalDisposablesCount == 0 ? "" : "using ")}var composition = new {composition.Source.Source.Name.ClassName}({string.Join(", ", composition.Args.Where(i => i.Node.Arg?.Source.Kind == ArgKind.Class).Select(arg => arg.VariableDeclarationName))});");
137+
code.AppendLine($"/// {(composition.TotalDisposablesCount == 0 ? "" : "using ")}var composition = new {composition.Source.Source.Name.ClassName}({string.Join(", ", composition.ClassArgs.Where(i => i.Node.Arg?.Source.Kind == ArgKind.Class).Select(arg => arg.Name))});");
138138
code.AppendLine($"/// var instance = composition.{formatter.Format(root)};");
139139
code.AppendLine("/// </code>");
140140
code.AppendLine("/// See also:");

src/Pure.DI.Core/Core/Code/ClassDiagramBuilder.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ public LinesBuilder Build(CompositionCode composition)
3535
{
3636
var classes = new List<Class>();
3737
var hasResolveMethods = composition.Source.Source.Hints.IsResolveEnabled;
38-
var rootProperties = composition.Roots.ToDictionary(i => i.Injection, i => i);
38+
var rootProperties = composition.PublicRoots.ToDictionary(i => i.Injection, i => i);
3939
var compositionLines = new LinesBuilder();
4040
if (hasResolveMethods || rootProperties.Count > 0)
4141
{
4242
compositionLines.AppendLine($"class {composition.Source.Source.Name.ClassName} {BlockStart}");
4343
using (lines.Indent())
4444
{
4545
compositionLines.AppendLine("<<partial>>");
46-
foreach (var root in composition.Roots.OrderByDescending(i => i.IsPublic).ThenBy(i => i.Name))
46+
foreach (var root in composition.PublicRoots.OrderByDescending(i => i.IsPublic).ThenBy(i => i.Name))
4747
{
4848
compositionLines.AppendLine($"{Format(rootAccessModifierResolver.Resolve(root))}{FormatRoot(setup, root)}");
4949
}
@@ -218,7 +218,7 @@ private string FormatRoot(MdSetup setup, Root root)
218218
var rootArgsStr = "";
219219
if (root.IsMethod)
220220
{
221-
rootArgsStr = $"({string.Join(", ", root.Args.Select(arg => $"{ResolveTypeName(setup, arg.InstanceType)} {arg.VariableDeclarationName}"))})";
221+
rootArgsStr = $"({string.Join(", ", root.RootArgs.Select(arg => $"{ResolveTypeName(setup, arg.InstanceType)} {arg.Name}"))})";
222222
}
223223

224224
var displayName = root.IsPublic ? root.DisplayName : "_";

src/Pure.DI.Core/Core/Code/CompositionBuilder.cs

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
namespace Pure.DI.Core.Code;
55

6+
using v2;
7+
68
sealed class CompositionBuilder(
79
IBuildTools buildTools,
810
INodeInfo nodeInfo,
@@ -11,7 +13,7 @@ sealed class CompositionBuilder(
1113
ICodeBuilder<Block> blockBuilder,
1214
ICodeBuilder<IStatement> statementBuilder,
1315
IBuilder<CompositionCode, LinesBuilder> classDiagramBuilder,
14-
Func<IVariablesMap> variablesMapFactory,
16+
Func<IVarsMap> variablesMapFactory,
1517
IVariableNameProvider variableNameProvider,
1618
IOverridesRegistry overridesRegistry,
1719
CancellationToken cancellationToken)
@@ -39,17 +41,7 @@ public CompositionCode Build(DependencyGraph graph)
3941
new LinesBuilder(),
4042
root.Injection.Tag != MdTag.ContextTag ? root.Injection.Tag : null,
4143
null,
42-
root.Node.Accumulators.ToImmutableArray());
43-
44-
foreach (var perResolveVar in map.GetPerResolves())
45-
{
46-
ctx.Code.AppendLine($"var {perResolveVar.VariableName} = default({typeResolver.Resolve(graph.Source, perResolveVar.InstanceType)});");
47-
if (perResolveVar.Info.RefCount > 0
48-
&& perResolveVar.InstanceType.IsValueType)
49-
{
50-
ctx.Code.AppendLine($"var {perResolveVar.VariableName}Created = false;");
51-
}
52-
}
44+
ImmutableArray<Accumulator>.Empty);
5345

5446
var bodyCode = new LinesBuilder();
5547
blockBuilder.Build(ctx with { Code = bodyCode }, rootBlock);
@@ -81,7 +73,7 @@ public CompositionCode Build(DependencyGraph graph)
8173

8274
var typeDescription = typeResolver.Resolve(graph.Source, processedRoot.Injection.Type);
8375
var isMethod = (processedRoot.Kind & RootKinds.Method) == RootKinds.Method
84-
|| processedRoot.Args.Length > 0
76+
|| processedRoot.RootArgs.Length > 0
8577
|| typeDescription.TypeArgs.Count > 0;
8678

8779
processedRoot = processedRoot with
@@ -91,7 +83,7 @@ public CompositionCode Build(DependencyGraph graph)
9183
};
9284

9385
roots.Add(processedRoot);
94-
isThreadSafe |= isThreadSafeEnabled && root.Node.Accumulators.Count > 0;
86+
// isThreadSafe |= isThreadSafeEnabled && root.Node.Accumulators.Count > 0;
9587
isThreadSafe |= isThreadSafeEnabled && map.IsThreadSafe;
9688
map.Reset();
9789
}
@@ -117,7 +109,9 @@ public CompositionCode Build(DependencyGraph graph)
117109
asyncDisposables.Count,
118110
totalDisposables.Count(i => i.Node.Lifetime == Lifetime.Scoped),
119111
isThreadSafe,
120-
ImmutableArray<Line>.Empty);
112+
ImmutableArray<Line>.Empty,
113+
ImmutableArray<VarDeclaration>.Empty,
114+
ImmutableArray<VarDeclaration>.Empty);
121115

122116
var diagram = classDiagramBuilder.Build(composition);
123117
return composition with { Diagram = diagram.Lines.ToImmutableArray() };

src/Pure.DI.Core/Core/Code/FactoryCodeBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ public void Build(BuildContext ctx, in DpFactory factory)
257257
var initializationArgsEnum = initializationArgs.Select(i => i.Current).GetEnumerator();
258258
ctx = ctx with { LockIsRequired = lockIsRequired, Level = level, AvoidLocalFunction = hasOverrides };
259259
var injectionsCtx = ctx;
260-
if (variable.IsLazy && variable.Node.Accumulators.Count > 0)
260+
/*if (variable.IsLazy && variable.Node.Accumulators.Count > 0)
261261
{
262262
injectionsCtx = injectionsCtx with
263263
{
@@ -266,7 +266,7 @@ public void Build(BuildContext ctx, in DpFactory factory)
266266
.Select(i => i with { IsDeclared = i.IsRoot && i.IsDeclared })
267267
.ToImmutableArray()
268268
};
269-
}
269+
}*/
270270

271271
var prefixes = new Stack<string>();
272272
foreach (var textLine in lines)

0 commit comments

Comments
 (0)