Skip to content

Commit

Permalink
Port stackoverflow fix from Roslyn to SourceGenerator PolyFill (#76954)
Browse files Browse the repository at this point in the history
Revert

Remove
  • Loading branch information
CyrusNajmabadi authored Oct 13, 2022
1 parent 075e74e commit cd2d837
Showing 1 changed file with 96 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ private static ImmutableArray<SyntaxNode> GetMatchingNodes(

try
{
recurse(compilationUnit, ref localAliases, ref seenNames, ref results, ref attributeTargets);
processCompilationUnit(compilationUnit, ref localAliases, ref seenNames, ref results, ref attributeTargets);

if (results.Length == 0)
return ImmutableArray<SyntaxNode>.Empty;
Expand All @@ -229,7 +229,21 @@ private static ImmutableArray<SyntaxNode> GetMatchingNodes(
seenNames.Dispose();
}

void recurse(
void processCompilationUnit(
SyntaxNode compilationUnit,
ref Aliases localAliases,
ref ValueListBuilder<string> seenNames,
ref ValueListBuilder<SyntaxNode> results,
ref ValueListBuilder<SyntaxNode> attributeTargets)
{
cancellationToken.ThrowIfCancellationRequested();

syntaxHelper.AddAliases(compilationUnit, ref localAliases, global: false);

processCompilationOrNamespaceMembers(compilationUnit, ref localAliases, ref seenNames, ref results, ref attributeTargets);
}

void processCompilationOrNamespaceMembers(
SyntaxNode node,
ref Aliases localAliases,
ref ValueListBuilder<string> seenNames,
Expand All @@ -238,70 +252,100 @@ void recurse(
{
cancellationToken.ThrowIfCancellationRequested();

if (node is ICompilationUnitSyntax)
foreach (var child in node.ChildNodesAndTokens())
{
syntaxHelper.AddAliases(node, ref localAliases, global: false);

recurseChildren(node, ref localAliases, ref seenNames, ref results, ref attributeTargets);
if (child.IsNode)
{
var childNode = child.AsNode()!;
if (syntaxHelper.IsAnyNamespaceBlock(childNode))
processNamespaceBlock(childNode, ref localAliases, ref seenNames, ref results, ref attributeTargets);
else
processMember(childNode, ref localAliases, ref seenNames, ref results, ref attributeTargets);
}
}
else if (syntaxHelper.IsAnyNamespaceBlock(node))
{
var localAliasCount = localAliases.Length;
syntaxHelper.AddAliases(node, ref localAliases, global: false);
}

void processNamespaceBlock(
SyntaxNode namespaceBlock,
ref Aliases localAliases,
ref ValueListBuilder<string> seenNames,
ref ValueListBuilder<SyntaxNode> results,
ref ValueListBuilder<SyntaxNode> attributeTargets)
{
cancellationToken.ThrowIfCancellationRequested();

recurseChildren(node, ref localAliases, ref seenNames, ref results, ref attributeTargets);
var localAliasCount = localAliases.Length;
syntaxHelper.AddAliases(namespaceBlock, ref localAliases, global: false);

// after recursing into this namespace, dump any local aliases we added from this namespace decl itself.
localAliases.Length = localAliasCount;
}
else if (syntaxHelper.IsAttributeList(node))
processCompilationOrNamespaceMembers(
namespaceBlock, ref localAliases, ref seenNames, ref results, ref attributeTargets);

// after recursing into this namespace, dump any local aliases we added from this namespace decl itself.
localAliases.Length = localAliasCount;
}

void processMember(
SyntaxNode member,
ref Aliases localAliases,
ref ValueListBuilder<string> seenNames,
ref ValueListBuilder<SyntaxNode> results,
ref ValueListBuilder<SyntaxNode> attributeTargets)
{
cancellationToken.ThrowIfCancellationRequested();

// nodes can be arbitrarily deep. Use an explicit stack over recursion to prevent a stack-overflow.
var nodeStack = new ValueListBuilder<SyntaxNode>(Span<SyntaxNode>.Empty);
nodeStack.Append(member);

try
{
foreach (var attribute in syntaxHelper.GetAttributesOfAttributeList(node))
while (nodeStack.Length > 0)
{
// Have to lookup both with the name in the attribute, as well as adding the 'Attribute' suffix.
// e.g. if there is [X] then we have to lookup with X and with XAttribute.
var simpleAttributeName = syntaxHelper.GetUnqualifiedIdentifierOfName(
syntaxHelper.GetNameOfAttribute(attribute)).ValueText;
if (matchesAttributeName(ref localAliases, ref seenNames, simpleAttributeName, withAttributeSuffix: false) ||
matchesAttributeName(ref localAliases, ref seenNames, simpleAttributeName, withAttributeSuffix: true))
{
attributeTargets.Length = 0;
syntaxHelper.AddAttributeTargets(node, ref attributeTargets);
var node = nodeStack.Pop();

foreach (var target in attributeTargets.AsSpan())
if (syntaxHelper.IsAttributeList(node))
{
foreach (var attribute in syntaxHelper.GetAttributesOfAttributeList(node))
{
if (predicate(target, cancellationToken))
results.Append(target);
// Have to lookup both with the name in the attribute, as well as adding the 'Attribute' suffix.
// e.g. if there is [X] then we have to lookup with X and with XAttribute.
var simpleAttributeName = syntaxHelper.GetUnqualifiedIdentifierOfName(
syntaxHelper.GetNameOfAttribute(attribute)).ValueText;
if (matchesAttributeName(ref localAliases, ref seenNames, simpleAttributeName, withAttributeSuffix: false) ||
matchesAttributeName(ref localAliases, ref seenNames, simpleAttributeName, withAttributeSuffix: true))
{
attributeTargets.Length = 0;
syntaxHelper.AddAttributeTargets(node, ref attributeTargets);

foreach (var target in attributeTargets.AsSpan())
{
if (predicate(target, cancellationToken))
results.Append(target);
}

break;
}
}

return;
// attributes can't have attributes inside of them. so no need to recurse when we're done.
}
else
{
// For any other node, just keep recursing deeper to see if we can find an attribute. Note: we cannot
// terminate the search anywhere as attributes may be found on things like local functions, and that
// means having to dive deep into statements and expressions.
foreach (var child in node.ChildNodesAndTokens().Reverse())
{
if (child.IsNode)
nodeStack.Append(child.AsNode()!);
}
}
}

// attributes can't have attributes inside of them. so no need to recurse when we're done.
}
else
{
// For any other node, just keep recursing deeper to see if we can find an attribute. Note: we cannot
// terminate the search anywhere as attributes may be found on things like local functions, and that
// means having to dive deep into statements and expressions.
recurseChildren(node, ref localAliases, ref seenNames, ref results, ref attributeTargets);
}
}

return;

void recurseChildren(
SyntaxNode node,
ref Aliases localAliases,
ref ValueListBuilder<string> seenNames,
ref ValueListBuilder<SyntaxNode> results,
ref ValueListBuilder<SyntaxNode> attributeTargets)
finally
{
foreach (var child in node.ChildNodesAndTokens())
{
if (child.IsNode)
recurse(child.AsNode()!, ref localAliases, ref seenNames, ref results, ref attributeTargets);
}
nodeStack.Dispose();
}
}

Expand Down

0 comments on commit cd2d837

Please sign in to comment.