@ -188,17 +188,18 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
@@ -188,17 +188,18 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
var builder = new StringBuilder ( ) ;
builder . AppendLine ( "// <auto-generated/>" ) ;
builder . AppendLine ( "namespace ICSharpCode.Decompiler.CSharp.Syntax; " ) ;
builder . AppendLine ( "#nullable enable " ) ;
builder . AppendLine ( ) ;
bool hasSlots = source . Slots is not null ;
if ( hasSlots )
builder . AppendLine ( "using System.Collections.Generic;" ) ;
if ( source . NeedsPatternPlaceholder )
{
builder . AppendLine ( "using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;" ) ;
}
builder . AppendLine ( ) ;
if ( hasSlots | | source . NeedsPatternPlaceholder )
builder . AppendLine ( ) ;
builder . AppendLine ( "#nullable enable " ) ;
builder . AppendLine ( "namespace ICSharpCode.Decompiler.CSharp.Syntax; " ) ;
builder . AppendLine ( ) ;
builder . AppendLine ( $"partial class {source.NodeName}" ) ;
@ -387,7 +388,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
@@ -387,7 +388,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
return SyntaxFacts . GetKeywordKind ( p ) ! = SyntaxKind . None ? "@" + p : p ;
}
string ParamType ( int i ) = > cp [ i ] . IsCollection
? $"global::System.Collections.Generic. IEnumerable<{cp[i].ElementType}>"
? $"IEnumerable<{cp[i].ElementType}>"
: cp [ i ] . ParamType ;
builder . AppendLine ( $"\tpublic {source.NodeName}()" ) ;
@ -421,7 +422,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
@@ -421,7 +422,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
var args = new List < string > ( ) ;
for ( int i = 0 ; i < len ; i + + )
args . Add ( i = = len - 1
? $"(global::System.Collections.Generic. IEnumerable<{cp[i].ElementType}>){ParamName(cp[i].PropertyName)}"
? $"(IEnumerable<{cp[i].ElementType}>){ParamName(cp[i].PropertyName)}"
: ParamName ( cp [ i ] . PropertyName ) ) ;
builder . AppendLine ( $"\t\t: this({string.Join(" , ", args)})" ) ;
builder . AppendLine ( "\t{" ) ;
@ -465,46 +466,119 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
@@ -465,46 +466,119 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
// Flattened child-index space: slots in declaration order, a single slot occupying one index
// (even when empty), a collection slot a contiguous run of its current length.
builder . Append ( "\tinternal override int GetChildCount() => " ) ;
builder . Append ( string . Join ( " + " , slots . Select ( s = > s . IsCollection ? $"({FieldName(s.PropertyName)}?.Count ?? 0)" : "1" ) ) ) ;
builder . AppendLine ( ";" ) ;
var countTerms = new List < string > ( ) ;
int singleSlotCount = slots . Count ( s = > ! s . IsCollection ) ;
if ( singleSlotCount > 0 )
countTerms . Add ( singleSlotCount . ToString ( ) ) ;
foreach ( var s in slots . Where ( s = > s . IsCollection ) )
countTerms . Add ( $"({FieldName(s.PropertyName)}?.Count ?? 0)" ) ;
builder . AppendLine ( $"\tinternal override int GetChildCount() => {string.Join(" + ", countTerms)};" ) ;
builder . AppendLine ( ) ;
builder . AppendLine ( "\tinternal override AstNode? GetChild(int index)" ) ;
builder . AppendLine ( "\t{" ) ;
builder . AppendLine ( "\t\tint i = index;" ) ;
foreach ( var s in slots )
bool anyCollection = slots . Any ( s = > s . IsCollection ) ;
// Emits a method body that maps a flat child index to a slot and returns an expression for it.
// With only single slots the index is a constant offset, so a switch reads best; once a
// collection slot is present the widths are dynamic, so walk the slots subtracting each one's
// length from a running index.
void EmitReturnDispatch ( Func < int , string > singleExpr , Func < int , string > collectionExpr )
{
if ( s . IsCollection )
if ( ! any Collection)
{
string field = FieldName ( s . PropertyName ) ;
builder . AppendLine ( $"\t\t{{ int n = {field}?.Count ?? 0; if (i < n) return {field}![i]; i -= n; }}" ) ;
builder . AppendLine ( "\t\tswitch (index)" ) ;
builder . AppendLine ( "\t\t{" ) ;
for ( int k = 0 ; k < slots . Count ; k + + )
{
builder . AppendLine ( $"\t\t\tcase {k}:" ) ;
builder . AppendLine ( $"\t\t\t\treturn {singleExpr(k)};" ) ;
}
builder . AppendLine ( "\t\t\tdefault:" ) ;
builder . AppendLine ( "\t\t\t\tthrow new System.ArgumentOutOfRangeException(nameof(index));" ) ;
builder . AppendLine ( "\t\t}" ) ;
return ;
}
else
builder . AppendLine ( "\t\tint i = index;" ) ;
for ( int k = 0 ; k < slots . Count ; k + + )
{
builder . AppendLine ( $"\t\tif (i == 0) return {FieldName(s.PropertyName)}; i -= 1;" ) ;
bool last = k = = slots . Count - 1 ;
if ( slots [ k ] . IsCollection )
{
string field = FieldName ( slots [ k ] . PropertyName ) ;
builder . AppendLine ( "\t\t{" ) ;
builder . AppendLine ( $"\t\t\tint n = {field}?.Count ?? 0;" ) ;
builder . AppendLine ( "\t\t\tif (i < n)" ) ;
builder . AppendLine ( $"\t\t\t\treturn {collectionExpr(k)};" ) ;
if ( ! last )
builder . AppendLine ( "\t\t\ti -= n;" ) ;
builder . AppendLine ( "\t\t}" ) ;
}
else
{
builder . AppendLine ( "\t\tif (i == 0)" ) ;
builder . AppendLine ( $"\t\t\treturn {singleExpr(k)};" ) ;
if ( ! last )
builder . AppendLine ( "\t\ti--;" ) ;
}
}
builder . AppendLine ( "\t\tthrow new System.ArgumentOutOfRangeException(nameof(index));" ) ;
}
builder . AppendLine ( "\t\tthrow new System.ArgumentOutOfRangeException(nameof(index));" ) ;
builder . AppendLine ( "\tinternal override AstNode? GetChild(int index)" ) ;
builder . AppendLine ( "\t{" ) ;
EmitReturnDispatch ( k = > FieldName ( slots [ k ] . PropertyName ) , k = > $"{FieldName(slots[k].PropertyName)}![i]" ) ;
builder . AppendLine ( "\t}" ) ;
builder . AppendLine ( ) ;
builder . AppendLine ( "\tinternal override void SetChild(int index, AstNode? value)" ) ;
builder . AppendLine ( "\t{" ) ;
builder . AppendLine ( "\t\tint i = index;" ) ;
foreach ( var s in slots )
if ( ! anyCollection )
{
if ( s . IsCollection )
builder . AppendLine ( "\t\tswitch (index)" ) ;
builder . AppendLine ( "\t\t{" ) ;
for ( int k = 0 ; k < slots . Count ; k + + )
{
string field = FieldName ( s . PropertyName ) ;
builder . AppendLine ( $"\t\t{{ int n = {field}?.Count ?? 0; if (i < n) {{ {field}![i] = ({s.ElementType})value!; return; }} i -= n; }}" ) ;
builder . AppendLine ( $"\t\t\tcase {k}:" ) ;
builder . AppendLine ( $"\t\t\t\tSetChildNode(ref {FieldName(slots[k].PropertyName)}, ({slots[k].PropertyType}?)value);" ) ;
builder . AppendLine ( "\t\t\t\treturn;" ) ;
}
else
builder . AppendLine ( "\t\t\tdefault:" ) ;
builder . AppendLine ( "\t\t\t\tthrow new System.ArgumentOutOfRangeException(nameof(index));" ) ;
builder . AppendLine ( "\t\t}" ) ;
}
else
{
builder . AppendLine ( "\t\tint i = index;" ) ;
for ( int k = 0 ; k < slots . Count ; k + + )
{
builder . AppendLine ( $"\t\tif (i == 0) {{ SetChildNode(ref {FieldName(s.PropertyName)}, ({s.PropertyType}?)value); return; }} i -= 1;" ) ;
bool last = k = = slots . Count - 1 ;
var s = slots [ k ] ;
string field = FieldName ( s . PropertyName ) ;
if ( s . IsCollection )
{
builder . AppendLine ( "\t\t{" ) ;
builder . AppendLine ( $"\t\t\tint n = {field}?.Count ?? 0;" ) ;
builder . AppendLine ( "\t\t\tif (i < n)" ) ;
builder . AppendLine ( "\t\t\t{" ) ;
builder . AppendLine ( $"\t\t\t\t{field}![i] = ({s.ElementType})value!;" ) ;
builder . AppendLine ( "\t\t\t\treturn;" ) ;
builder . AppendLine ( "\t\t\t}" ) ;
if ( ! last )
builder . AppendLine ( "\t\t\ti -= n;" ) ;
builder . AppendLine ( "\t\t}" ) ;
}
else
{
builder . AppendLine ( "\t\tif (i == 0)" ) ;
builder . AppendLine ( "\t\t{" ) ;
builder . AppendLine ( $"\t\t\tSetChildNode(ref {field}, ({s.PropertyType}?)value);" ) ;
builder . AppendLine ( "\t\t\treturn;" ) ;
builder . AppendLine ( "\t\t}" ) ;
if ( ! last )
builder . AppendLine ( "\t\ti--;" ) ;
}
}
builder . AppendLine ( "\t\tthrow new System.ArgumentOutOfRangeException(nameof(index));" ) ;
}
builder . AppendLine ( "\t\tthrow new System.ArgumentOutOfRangeException(nameof(index));" ) ;
builder . AppendLine ( "\t}" ) ;
builder . AppendLine ( ) ;
@ -515,15 +589,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
@@ -515,15 +589,7 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
builder . AppendLine ( "\tinternal override CSharpSlotInfo GetChildSlotInfo(int index)" ) ;
builder . AppendLine ( "\t{" ) ;
builder . AppendLine ( "\t\tint i = index;" ) ;
foreach ( var s in slots )
{
if ( s . IsCollection )
builder . AppendLine ( $"\t\t{{ int n = {FieldName(s.PropertyName)}?.Count ?? 0; if (i < n) return {s.PropertyName}Slot; i -= n; }}" ) ;
else
builder . AppendLine ( $"\t\tif (i == 0) return {s.PropertyName}Slot; i -= 1;" ) ;
}
builder . AppendLine ( "\t\tthrow new System.ArgumentOutOfRangeException(nameof(index));" ) ;
EmitReturnDispatch ( k = > $"{slots[k].PropertyName}Slot" , k = > $"{slots[k].PropertyName}Slot" ) ;
builder . AppendLine ( "\t}" ) ;
builder . AppendLine ( ) ;
@ -547,7 +613,11 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
@@ -547,7 +613,11 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
{
string field = FieldName ( s . PropertyName ) ;
if ( s . IsCollection )
builder . AppendLine ( $"\t\tif ({field} != null) foreach (var c in {field}) copy.{s.PropertyName}.Add(({s.ElementType})c.Clone());" ) ;
{
builder . AppendLine ( $"\t\tif ({field} != null)" ) ;
builder . AppendLine ( $"\t\t\tforeach (var c in {field})" ) ;
builder . AppendLine ( $"\t\t\t\tcopy.{s.PropertyName}.Add(({s.ElementType})c.Clone());" ) ;
}
else
builder . AppendLine ( $"\t\tif ({field} != null) copy.{s.PropertyName} = ({s.PropertyType}){field}.Clone();" ) ;
}
@ -558,9 +628,9 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
@@ -558,9 +628,9 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
// and the visitors' VisitChildren, instead of the per-step O(slots) index scan (NextSibling).
// Collection slots iterate via their own enumerator, which tolerates removing/replacing the
// current child during traversal.
builder . AppendLine ( "\tinternal override System.Collections.Generic. IEnumerable<AstNode> GetChildNodes()" ) ;
builder . AppendLine ( "\tinternal override IEnumerable<AstNode> GetChildNodes()" ) ;
builder . AppendLine ( "\t{" ) ;
builder . AppendLine ( "\t\tvar children = new System.Collections.Generic. List<AstNode>(GetChildCount());" ) ;
builder . AppendLine ( "\t\tvar children = new List<AstNode>(GetChildCount());" ) ;
foreach ( var s in slots )
{
string field = FieldName ( s . PropertyName ) ;
@ -574,32 +644,38 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
@@ -574,32 +644,38 @@ internal class DecompilerSyntaxTreeGenerator : IIncrementalGenerator
builder . AppendLine ( ) ;
}
builder . AppendLine ( "}" ) ;
// Close the class, trimming the blank line the per-member spacer leaves before the brace.
string body = builder . ToString ( ) . TrimEnd ( ) + "\n}\n" ;
context . AddSource ( source . NodeName + ".g.cs" , SourceText . From ( builder . ToString ( ) . Replace ( "\r\n" , "\n" ) , Encoding . UTF8 ) ) ;
context . AddSource ( source . NodeName + ".g.cs" , SourceText . From ( body . Replace ( "\r\n" , "\n" ) , Encoding . UTF8 ) ) ;
}
void WriteVisitors ( SourceProductionContext context , ImmutableArray < AstNodeAdditions > source )
{
var builder = new StringBuilder ( ) ;
builder . AppendLine ( "// <auto-generated/>" ) ;
builder . AppendLine ( "#nullable enable" ) ;
builder . AppendLine ( ) ;
builder . AppendLine ( "namespace ICSharpCode.Decompiler.CSharp.Syntax;" ) ;
builder . AppendLine ( ) ;
source = source
. Concat ( [ new ( "PatternPlaceholder" , true , false , true , false , "PatternPlaceholder" , "AstNode" , null , null , null , null ) ] )
. ToImmutableArray ( ) ;
WriteInterface ( "IAstVisitor" , "void" , "" ) ;
builder . AppendLine ( ) ;
WriteInterface ( "IAstVisitor<out S>" , "S" , "" ) ;
builder . AppendLine ( ) ;
WriteInterface ( "IAstVisitor<in T, out S>" , "S" , ", T data" ) ;
context . AddSource ( "IAstVisitor.g.cs" , SourceText . From ( builder . ToString ( ) , Encoding . UTF8 ) ) ;
context . AddSource ( "IAstVisitor.g.cs" , SourceText . From ( builder . ToString ( ) . Replace ( "\r\n" , "\n" ) , Encoding . UTF8 ) ) ;
void WriteInterface ( string name , string ret , string param )
{
builder . AppendLine ( $"public interface {name}" ) ;
builder . AppendLine ( "{" ) ;
foreach ( var type in source . OrderBy ( t = > t . VisitMethodName ) )
{
if ( ! type . NeedsVisitor )
@ -696,6 +772,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
@@ -696,6 +772,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
}
var builder = new StringBuilder ( ) ;
builder . AppendLine ( "// <auto-generated/>" ) ;
builder . AppendLine ( ) ;
builder . AppendLine ( "namespace ICSharpCode.Decompiler.CSharp.Syntax;" ) ;
builder . AppendLine ( ) ;
builder . AppendLine ( "/// <summary>Identifies the kind of an AST child slot, shared across node types.</summary>" ) ;
@ -706,7 +784,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
@@ -706,7 +784,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
builder . AppendLine ( $"\t{k}," ) ;
builder . AppendLine ( "}" ) ;
context . AddSource ( "SlotKind.g.cs" , SourceText . From ( builder . ToString ( ) , Encoding . UTF8 ) ) ;
context . AddSource ( "SlotKind.g.cs" , SourceText . From ( builder . ToString ( ) . Replace ( "\r\n" , "\n" ) , Encoding . UTF8 ) ) ;
}
}