Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
JasperFx.Events, JasperFx.Events.SourceGenerator) set
<Version>$(JasperFxVersion)</Version> so they always release together. Bump this one
value to release the whole set. -->
<JasperFxVersion>2.2.0</JasperFxVersion>
<JasperFxVersion>2.2.1</JasperFxVersion>
<LangVersion>13</LangVersion>
<NoWarn>1570;1571;1572;1573;1574;1587;1591;1701;1702;1711;1735;0618</NoWarn>
<Authors>Jeremy D. Miller;Jaedyn Tonee</Authors>
Expand Down
2 changes: 1 addition & 1 deletion src/JasperFx.SourceGenerator/InputParserGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ private static string GetConverterExpression(MemberParseInfo member)
"global::System.Guid" => "s => global::System.Guid.Parse(s)",
"global::System.DateTime" => "s => global::JasperFx.CommandLine.Internal.Conversion.DateTimeConverter.GetDateTime(s)",
"global::System.TimeSpan" => "s => global::JasperFx.CommandLine.Internal.Conversion.TimeSpanConverter.GetTimeSpan(s)",
_ => $"new global::JasperFx.CommandLine.Internal.Conversion.Conversions().FindConverter(typeof({type}))!",
_ => $"s => ({type})new global::JasperFx.CommandLine.Internal.Conversion.Conversions().FindConverter(typeof({type}))!(s)",
};
}

Expand Down
96 changes: 72 additions & 24 deletions src/JasperFx/CodeGeneration/DependencyGatherer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,39 +14,87 @@ internal class DependencyGatherer
public DependencyGatherer(IMethodVariables methodVariables, IList<Frame> frames)
{
_methodVariables = methodVariables;
Dependencies.OnMissing = frame => new List<Frame>(findDependencies(frame).Distinct());
Variables.OnMissing = v => new List<Frame>(findDependencies(v).Distinct());
Dependencies.OnMissing = frame =>
{
// Iterative BFS replaces the original co-recursive yield iterators that lacked cycle
// protection and stack-overflowed on deep Frame/Variable graphs (e.g. Lamar/Wolverine's
// resolver code-gen). Variables encountered along the way are pinned into the Variables
// cache via Fill so MethodFrameArranger.findInjectedFields (which reads Variables.Keys())
// observes the same population the original recursive walk produced as a side effect.
var result = new List<Frame>();
var seenFrames = new HashSet<Frame> { frame };
var seenVariables = new HashSet<Variable>();
Walk(frame, null, result, seenFrames, seenVariables, top: frame);
return result;
};
Variables.OnMissing = v =>
{
var result = new List<Frame>();
var seenFrames = new HashSet<Frame>();
var seenVariables = new HashSet<Variable> { v };
Walk(null, v, result, seenFrames, seenVariables, top: null);
return result;
};

foreach (var frame in frames) Dependencies.FillDefault(frame);
}


private IEnumerable<Frame> findDependencies(Frame frame)
private void Walk(Frame? startFrame, Variable? startVariable, List<Frame> result,
HashSet<Frame> seenFrames, HashSet<Variable> seenVariables, Frame? top)
{
frame.ResolveVariables(_methodVariables);
var frameQueue = new Queue<Frame>();
var variableQueue = new Queue<Variable>();
if (startFrame != null) frameQueue.Enqueue(startFrame);
if (startVariable != null) variableQueue.Enqueue(startVariable);

foreach (var dependency in frame.Dependencies)
while (frameQueue.Count > 0 || variableQueue.Count > 0)
{
yield return dependency;
while (frameQueue.Count > 0)
{
var f = frameQueue.Dequeue();
f.ResolveVariables(_methodVariables);

foreach (var child in Dependencies[dependency]) yield return child;
}
foreach (var dep in f.Dependencies)
{
if (seenFrames.Add(dep))
{
if (!ReferenceEquals(dep, top)) result.Add(dep);
frameQueue.Enqueue(dep);
}
}

foreach (var variable in frame.Uses)
foreach (var dependency in Variables[variable])
yield return dependency;
}
foreach (var v in f.Uses)
{
if (seenVariables.Add(v))
{
// Pin the key so findInjectedFields' Variables.Keys() lookup sees it. The
// value here is a placeholder; the next caller to read Variables[v]
// through the indexer triggers a fresh BFS via OnMissing.
Variables.Fill(v, EmptyList);
variableQueue.Enqueue(v);
}
}
}

private IEnumerable<Frame> findDependencies(Variable variable)
{
if (variable.Creator != null)
{
yield return variable.Creator;
foreach (var frame in Dependencies[variable.Creator]) yield return frame;
}
if (variableQueue.Count == 0) break;
var variable = variableQueue.Dequeue();

if (variable.Creator != null && seenFrames.Add(variable.Creator))
{
if (!ReferenceEquals(variable.Creator, top)) result.Add(variable.Creator);
frameQueue.Enqueue(variable.Creator);
}

foreach (var dependency in variable.Dependencies)
foreach (var frame in Variables[dependency])
yield return frame;
foreach (var d in variable.Dependencies)
{
if (seenVariables.Add(d))
{
Variables.Fill(d, EmptyList);
variableQueue.Enqueue(d);
}
}
}
}
}

private static readonly List<Frame> EmptyList = new(0);
}
Loading