Skip to content

Commit

Permalink
#74 Add the ability to build up an object
Browse files Browse the repository at this point in the history
  • Loading branch information
NikolayPianikov committed Nov 13, 2024
1 parent 9487a03 commit f83f669
Show file tree
Hide file tree
Showing 11 changed files with 169 additions and 102 deletions.
6 changes: 3 additions & 3 deletions readme/async-root.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,13 @@ partial class Composition
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Task<IService> GetMyServiceAsync(CancellationToken cancellationToken)
{
TaskScheduler transientTaskScheduler5 = TaskScheduler.Default;
TaskContinuationOptions transientTaskContinuationOptions4 = TaskContinuationOptions.None;
TaskCreationOptions transientTaskCreationOptions3 = TaskCreationOptions.None;
TaskFactory<IService> perBlockTaskFactory2;
CancellationToken localCancellationToken17 = cancellationToken;
TaskCreationOptions transientTaskCreationOptions3 = TaskCreationOptions.None;
TaskCreationOptions localTaskCreationOptions18 = transientTaskCreationOptions3;
TaskContinuationOptions transientTaskContinuationOptions4 = TaskContinuationOptions.None;
TaskContinuationOptions localTaskContinuationOptions19 = transientTaskContinuationOptions4;
TaskScheduler transientTaskScheduler5 = TaskScheduler.Default;
TaskScheduler localTaskScheduler20 = transientTaskScheduler5;
perBlockTaskFactory2 = new TaskFactory<IService>(localCancellationToken17, localTaskCreationOptions18, localTaskContinuationOptions19, localTaskScheduler20);
Func<IService> perBlockFunc1 = new Func<IService>([MethodImpl(MethodImplOptions.AggressiveInlining)] () =>
Expand Down
12 changes: 6 additions & 6 deletions readme/generic-async-composition-roots-with-constraints.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,13 @@ partial class Composition
public Task<IService<T, bool>> GetOtherServiceAsync<T>(CancellationToken cancellationToken)
where T: IDisposable
{
TaskScheduler transientTaskScheduler5 = TaskScheduler.Default;
TaskContinuationOptions transientTaskContinuationOptions4 = TaskContinuationOptions.None;
TaskCreationOptions transientTaskCreationOptions3 = TaskCreationOptions.None;
TaskFactory<IService<T, bool>> perBlockTaskFactory2;
CancellationToken localCancellationToken47 = cancellationToken;
TaskCreationOptions transientTaskCreationOptions3 = TaskCreationOptions.None;
TaskCreationOptions localTaskCreationOptions48 = transientTaskCreationOptions3;
TaskContinuationOptions transientTaskContinuationOptions4 = TaskContinuationOptions.None;
TaskContinuationOptions localTaskContinuationOptions49 = transientTaskContinuationOptions4;
TaskScheduler transientTaskScheduler5 = TaskScheduler.Default;
TaskScheduler localTaskScheduler50 = transientTaskScheduler5;
perBlockTaskFactory2 = new TaskFactory<IService<T, bool>>(localCancellationToken47, localTaskCreationOptions48, localTaskContinuationOptions49, localTaskScheduler50);
Func<IService<T, bool>> perBlockFunc1 = new Func<IService<T, bool>>([MethodImpl(MethodImplOptions.AggressiveInlining)] () =>
Expand All @@ -116,13 +116,13 @@ partial class Composition
where T: IDisposable
where T1: struct
{
TaskScheduler transientTaskScheduler5 = TaskScheduler.Default;
TaskContinuationOptions transientTaskContinuationOptions4 = TaskContinuationOptions.None;
TaskCreationOptions transientTaskCreationOptions3 = TaskCreationOptions.None;
TaskFactory<IService<T, T1>> perBlockTaskFactory2;
CancellationToken localCancellationToken55 = cancellationToken;
TaskCreationOptions transientTaskCreationOptions3 = TaskCreationOptions.None;
TaskCreationOptions localTaskCreationOptions56 = transientTaskCreationOptions3;
TaskContinuationOptions transientTaskContinuationOptions4 = TaskContinuationOptions.None;
TaskContinuationOptions localTaskContinuationOptions57 = transientTaskContinuationOptions4;
TaskScheduler transientTaskScheduler5 = TaskScheduler.Default;
TaskScheduler localTaskScheduler58 = transientTaskScheduler5;
perBlockTaskFactory2 = new TaskFactory<IService<T, T1>>(localCancellationToken55, localTaskCreationOptions56, localTaskContinuationOptions57, localTaskScheduler58);
Func<IService<T, T1>> perBlockFunc1 = new Func<IService<T, T1>>([MethodImpl(MethodImplOptions.AggressiveInlining)] () =>
Expand Down
2 changes: 1 addition & 1 deletion readme/simplified-factory.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ partial class Composition
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
DateTimeOffset transientDateTimeOffset3 = DateTimeOffset.Now;
Dependency transientDependency1;
Dependency localDependency29 = new Dependency();
DateTimeOffset transientDateTimeOffset3 = DateTimeOffset.Now;
DateTimeOffset localTime30 = transientDateTimeOffset3;
localDependency29.Initialize(localTime30);
transientDependency1 = localDependency29;
Expand Down
6 changes: 3 additions & 3 deletions readme/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,13 @@ partial class Composition
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IService GetRoot(CancellationToken cancellationToken)
{
TaskScheduler transientTaskScheduler6 = TaskScheduler.Current;
TaskContinuationOptions transientTaskContinuationOptions5 = TaskContinuationOptions.None;
TaskCreationOptions transientTaskCreationOptions4 = TaskCreationOptions.None;
TaskFactory<IDependency> perBlockTaskFactory3;
CancellationToken localCancellationToken39 = cancellationToken;
TaskCreationOptions transientTaskCreationOptions4 = TaskCreationOptions.None;
TaskCreationOptions localTaskCreationOptions40 = transientTaskCreationOptions4;
TaskContinuationOptions transientTaskContinuationOptions5 = TaskContinuationOptions.None;
TaskContinuationOptions localTaskContinuationOptions41 = transientTaskContinuationOptions5;
TaskScheduler transientTaskScheduler6 = TaskScheduler.Current;
TaskScheduler localTaskScheduler42 = transientTaskScheduler6;
perBlockTaskFactory3 = new TaskFactory<IDependency>(localCancellationToken39, localTaskCreationOptions40, localTaskContinuationOptions41, localTaskScheduler42);
Func<IDependency> perBlockFunc2 = new Func<IDependency>([MethodImpl(MethodImplOptions.AggressiveInlining)] () =>
Expand Down
6 changes: 5 additions & 1 deletion src/Pure.DI.Core/Core/Code/BlockCodeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ public void Build(BuildContext ctx, in Block block)
var content = new LinesBuilder();
foreach (var statement in block.Statements)
{
ctx.StatementBuilder.Build(ctx with { Variable = statement.Current, Code = content }, statement);
var curVar = statement.Current;
if (ctx.IsFactory || curVar.Injection.Kind != InjectionKind.FactoryInjection || curVar.Node.Lifetime != Lifetime.Transient)
{
ctx.StatementBuilder.Build(ctx with { Variable = curVar, Code = content, IsFactory = false }, statement);
}
}

if (content.Count == 0)
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 @@ -10,4 +10,5 @@ internal record BuildContext(
LinesBuilder LocalFunctionsCode,
object? ContextTag,
bool? LockIsRequired,
ImmutableArray<Accumulator> Accumulators);
ImmutableArray<Accumulator> Accumulators,
bool IsFactory = false);
6 changes: 3 additions & 3 deletions src/Pure.DI.Core/Core/Code/FactoryCodeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,8 @@ public void Build(BuildContext ctx, in DpFactory factory)
lines.AddRange(text.Lines);
}

var injectionArgs = variable.Args.Where(i => i.Current.Injection.Kind == InjectionKind.Injection).ToList();
var initializationArgs = variable.Args.Where(i => i.Current.Injection.Kind != InjectionKind.Injection).ToList();
var injectionArgs = variable.Args.Where(i => i.Current.Injection.Kind is InjectionKind.FactoryInjection).ToList();
var initializationArgs = variable.Args.Where(i => i.Current.Injection.Kind != InjectionKind.FactoryInjection).ToList();

// Replaces injection markers by injection code
if (injectionArgs.Count != injections.Count)
Expand Down Expand Up @@ -276,7 +276,7 @@ public void Build(BuildContext ctx, in DpFactory factory)
var indent = prefixes.Count;
using (code.Indent(indent))
{
ctx.StatementBuilder.Build(injectionsCtx with { Level = level, Variable = argument.Current, LockIsRequired = lockIsRequired }, argument);
ctx.StatementBuilder.Build(injectionsCtx with { Level = level, Variable = argument.Current, LockIsRequired = lockIsRequired, IsFactory = true }, argument);
code.AppendLine($"{(resolver.DeclarationRequired ? $"{typeResolver.Resolve(ctx.DependencyGraph.Source, argument.Current.Injection.Type)} " : "")}{resolver.VariableName} = {ctx.BuildTools.OnInjected(ctx, argument.Current)};");
}

Expand Down
128 changes: 51 additions & 77 deletions src/Pure.DI.Core/Core/Code/FactoryRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,76 +128,61 @@ private ExpressionStatementSyntax CreateAssignmentExpression(SyntaxNode returnBo
SyntaxFactory.IdentifierName(variable.VariableName).WithLeadingTrivia(SyntaxFactory.Space).WithTrailingTrivia(SyntaxFactory.Space),
(ExpressionSyntax)Visit(returnBody).WithLeadingTrivia(SyntaxFactory.Space))),
owner);

public override SyntaxNode? VisitInvocationExpression(InvocationExpressionSyntax invocation)
public override SyntaxNode VisitExpressionStatement(ExpressionStatementSyntax node)
{
if (invocation.ArgumentList.Arguments.Count > 0)
{
if (invocation.Expression is MemberAccessExpressionSyntax
{
Name: GenericNameSyntax
{
Identifier.Text: nameof(IContext.Inject),
TypeArgumentList.Arguments: [not null]
},
Expression: IdentifierNameSyntax ctx
}
&& ctx.Identifier.Text == factory.Source.Context.Identifier.Text
&& TryInject(invocation, out var visitInvocationExpression))
node = (ExpressionStatementSyntax)base.VisitExpressionStatement(node)!;
if (node.Expression is not InvocationExpressionSyntax
{
return visitInvocationExpression;
}
ArgumentList.Arguments.Count: > 0,
Expression: MemberAccessExpressionSyntax { Expression: IdentifierNameSyntax ctx } memberAccessExpression
} invocation
|| ctx.Identifier.Text != factory.Source.Context.Identifier.Text)
{
return node;
}

if (invocation.Expression is MemberAccessExpressionSyntax
{
Name: IdentifierNameSyntax
{
Identifier.Text: nameof(IContext.Inject)
},
Expression: IdentifierNameSyntax ctx2
}
&& ctx2.Identifier.Text == factory.Source.Context.Identifier.Text
&& TryInject(invocation, out visitInvocationExpression))
{
return visitInvocationExpression;
}
var name = "";
switch (memberAccessExpression.Name)
{
case GenericNameSyntax { TypeArgumentList.Arguments.Count: 1 } genericName:
name = genericName.Identifier.Text;
break;

if (invocation.Expression is MemberAccessExpressionSyntax
{
Name: GenericNameSyntax
{
Identifier.Text: nameof(IContext.BuildUp),
TypeArgumentList.Arguments: [not null]
},
Expression: IdentifierNameSyntax ctx3
}
&& ctx3.Identifier.Text == factory.Source.Context.Identifier.Text
&& TryInitialize(invocation, out visitInvocationExpression))
{
return visitInvocationExpression;
}
case IdentifierNameSyntax identifierName:
name = identifierName.Identifier.Text;
break;
}

if (invocation.Expression is MemberAccessExpressionSyntax
{
Name: IdentifierNameSyntax
{
Identifier.Text: nameof(IContext.BuildUp)
},
Expression: IdentifierNameSyntax ctx4
}
&& ctx4.Identifier.Text == factory.Source.Context.Identifier.Text
&& TryInitialize(invocation, out visitInvocationExpression))
{
return visitInvocationExpression;
}
ExpressionSyntax? expressionSyntax = default;
var processed = name switch
{
nameof(IContext.Inject) => TryInject(invocation, out expressionSyntax),
nameof(IContext.BuildUp) => TryInitialize(invocation, out expressionSyntax),
_ => false
};

if (!processed || expressionSyntax is null)
{
return node;
}

SyntaxNode newNode;
if (node.Parent is null or BlockSyntax)
{
newNode = SyntaxFactory.ExpressionStatement(expressionSyntax).WithLeadingTrivia(SyntaxFactory.LineFeed).WithTrailingTrivia(SyntaxFactory.LineFeed);
}
else
{
newNode = SyntaxFactory.Block().AddStatements(SyntaxFactory.ExpressionStatement(expressionSyntax).WithLeadingTrivia(SyntaxFactory.LineFeed).WithTrailingTrivia(SyntaxFactory.LineFeed));
}

return base.VisitInvocationExpression(invocation);
return triviaTools.PreserveTrivia(newNode, node);
}

private bool TryInject(
InvocationExpressionSyntax invocation,
[NotNullWhen(true)] out SyntaxNode? visitInvocationExpression)
[NotNullWhen(true)] out ExpressionSyntax? expressionSyntax)
{
var value = invocation.ArgumentList.Arguments.Count switch
{
Expand All @@ -211,25 +196,25 @@ private bool TryInject(
case IdentifierNameSyntax identifierName:
injections.Add(new Injection(identifierName.Identifier.Text, false));
{
visitInvocationExpression = triviaTools.PreserveTrivia(InjectionMarkerExpression, invocation);
expressionSyntax = triviaTools.PreserveTrivia(InjectionMarkerExpression, invocation);
return true;
}

case DeclarationExpressionSyntax { Designation: SingleVariableDesignationSyntax singleVariableDesignationSyntax }:
injections.Add(new Injection(singleVariableDesignationSyntax.Identifier.Text, true));
{
visitInvocationExpression = triviaTools.PreserveTrivia(InjectionMarkerExpression, invocation);
expressionSyntax = triviaTools.PreserveTrivia(InjectionMarkerExpression, invocation);
return true;
}
}

visitInvocationExpression = default;
expressionSyntax = default;
return false;
}

private bool TryInitialize(
InvocationExpressionSyntax invocation,
[NotNullWhen(true)] out SyntaxNode? visitInvocationExpression)
[NotNullWhen(true)] out ExpressionSyntax? expressionSyntax)
{
var value = invocation.ArgumentList.Arguments.Count switch
{
Expand All @@ -242,33 +227,22 @@ private bool TryInitialize(
case IdentifierNameSyntax identifierName:
initializers.Add(new Initializer(identifierName.Identifier.Text, false));
{
visitInvocationExpression = triviaTools.PreserveTrivia(InitializationMarkerExpression, invocation);
expressionSyntax = triviaTools.PreserveTrivia(InitializationMarkerExpression, invocation);
return true;
}

case DeclarationExpressionSyntax { Designation: SingleVariableDesignationSyntax singleVariableDesignationSyntax }:
initializers.Add(new Initializer(singleVariableDesignationSyntax.Identifier.Text, true));
{
visitInvocationExpression = triviaTools.PreserveTrivia(InitializationMarkerExpression, invocation);
expressionSyntax = triviaTools.PreserveTrivia(InitializationMarkerExpression, invocation);
return true;
}
}

visitInvocationExpression = default;
expressionSyntax = default;
return false;
}

public override SyntaxNode VisitExpressionStatement(ExpressionStatementSyntax node)
{
var newNode = (ExpressionStatementSyntax)base.VisitExpressionStatement(node)!;
if (newNode.Expression.IsEquivalentTo(InjectionMarkerExpression))
{
return triviaTools.PreserveTrivia(newNode, node);
}

return newNode;
}

public override SyntaxNode? VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
{
if (node.IsKind(SyntaxKind.SimpleMemberAccessExpression)
Expand Down
2 changes: 1 addition & 1 deletion src/Pure.DI.Core/Core/FactoryDependencyNodeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public IEnumerable<DependencyNode> Build(MdSetup setup)
foreach (var resolver in factory.Resolvers)
{
var tag = attributes.GetAttribute(resolver.SemanticModel, setup.TagAttributes, resolver.Attributes, default(object?)) ?? resolver.Tag?.Value;
injections.Add(new Injection(InjectionKind.Injection, resolver.ContractType.WithNullableAnnotation(NullableAnnotation.NotAnnotated), tag));
injections.Add(new Injection(InjectionKind.FactoryInjection, resolver.ContractType.WithNullableAnnotation(NullableAnnotation.NotAnnotated), tag));
}

var compilation = binding.SemanticModel.Compilation;
Expand Down
8 changes: 7 additions & 1 deletion src/Pure.DI.Core/Core/Models/InjectionKind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,16 @@
internal enum InjectionKind
{
Field,

Property,

Parameter,

Root,
Injection,

FactoryInjection,

Contract,

Construct
}
Loading

0 comments on commit f83f669

Please sign in to comment.