Skip to content

Scrape fields from XML #942

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 3, 2022
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
66 changes: 8 additions & 58 deletions src/generators/Silk.NET.SilkTouch.Emitter/CSharpEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,75 +70,25 @@ protected override StructSymbol VisitStruct(StructSymbol structSymbol)
throw new InvalidOperationException("Field Identifier was not visited correctly");
ClearState();

var memberList = new List<MemberDeclarationSyntax>(structSymbol.Layout.Entries.Length);
foreach (var entry in structSymbol.Layout.Entries)
var fields = new List<MemberDeclarationSyntax>(structSymbol.Fields.Length);
foreach (var field in structSymbol.Fields)
{
VisitMember(entry.Member);
VisitField(field);
if (_syntax is not MemberDeclarationSyntax memberDeclarationSyntax)
throw new InvalidOperationException("Member was not visited correctly");
ClearState();
memberDeclarationSyntax = memberDeclarationSyntax.WithLeadingTrivia(LineFeed, _indentation);
memberDeclarationSyntax = memberDeclarationSyntax.WithAttributeLists
(
SingletonList
(
AttributeList
(
SingletonSeparatedList
(
Attribute
(IdentifierName("FieldOffset"))
.WithArgumentList
(
AttributeArgumentList
(
SingletonSeparatedList
(
AttributeArgument
(LiteralExpression(SyntaxKind.NumericLiteralExpression, Literal(entry.ByteOffset)))
)
)
)
)
)
)
).WithLeadingTrivia(LineFeed, _indentation);
memberList.Add(memberDeclarationSyntax);
memberDeclarationSyntax = memberDeclarationSyntax.WithLeadingTrivia(LineFeed, _indentation);
fields.Add(memberDeclarationSyntax);
}

var members = List(memberList);
var members = List(fields);

var modifiers = TokenList(Token(SyntaxTriviaList.Empty, SyntaxKind.PublicKeyword, TriviaList(Space)));
_syntax = StructDeclaration
(
SingletonList
(
AttributeList
(
SingletonSeparatedList
(
Attribute(IdentifierName("StructLayout"))
.WithArgumentList
(
AttributeArgumentList
(
SingletonSeparatedList
(
AttributeArgument
(
MemberAccessExpression
(
SyntaxKind.SimpleMemberAccessExpression,
IdentifierName("LayoutKind"), IdentifierName("Explicit")
)
)
)
)
)
)
)
.WithTrailingTrivia(LineFeed)
), modifiers, identifierToken, null, null, List<TypeParameterConstraintClauseSyntax>(), members
default, modifiers, identifierToken, null, null, List<TypeParameterConstraintClauseSyntax>(),
members
)
.WithKeyword(Token(SyntaxTriviaList.Empty, SyntaxKind.StructKeyword, TriviaList(Space)))
.WithOpenBraceToken(Token(TriviaList(LineFeed), SyntaxKind.OpenBraceToken, SyntaxTriviaList.Empty))
Expand Down
17 changes: 15 additions & 2 deletions src/generators/Silk.NET.SilkTouch.Scraper/XmlVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ private IEnumerable<Symbol> VisitField(XmlElement field)
new IdentifierSymbol(
field.ChildNodes.Cast<XmlNode>().SingleOrDefault(x => x.Name == "type")?.InnerText ??
throw new InvalidOperationException("Could not decode Field Type")),
StructLayout.Empty
ImmutableArray<FieldSymbol>.Empty
);

return new[]
Expand All @@ -56,12 +56,25 @@ private IEnumerable<Symbol> VisitField(XmlElement field)

private IEnumerable<Symbol> VisitStruct(XmlElement @struct)
{
var fields = new List<FieldSymbol>();
foreach (var node in @struct.ChildNodes.Cast<XmlNode>())
{
var symbols = Visit(node);
foreach (var v in symbols)
{
if (v is FieldSymbol fieldSymbol)
{
fields.Add(fieldSymbol);
}
}
}

return new[]
{
new StructSymbol
(
new IdentifierSymbol(@struct.Attributes?["name"]?.Value ?? throw new InvalidOperationException()),
StructLayout.Empty
fields.ToImmutableArray()
)
};
}
Expand Down
31 changes: 6 additions & 25 deletions src/generators/Silk.NET.SilkTouch.Symbols/StructSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,12 @@ namespace Silk.NET.SilkTouch.Symbols;
/// A <see cref="TypeSymbol"/> representing a <c>struct</c>.
/// </summary>
/// <param name="Identifier">The Identifier of this struct</param>
/// <param name="Layout">The layout of this struct</param>
/// <param name="Fields">The fields of this struct</param>
/// <remarks>
/// In this context, a Struct means a type that represents the layout of a continuous block of memory.
/// </remarks>
// /// Each meaningful place in this memory called a field (see <see cref="FieldSymbol"/>) is accessible via this type.
// /// Fields are allowed to overlap.
// /// Additionally it may contain one or multiple <see cref="MethodSymbol"/> that are called with an instance of this type as their first argument.
public sealed record StructSymbol(IdentifierSymbol Identifier, StructLayout Layout) : TypeSymbol(Identifier);

/// <summary>
/// A <see cref="StructSymbol"/> representing the layout of a <see cref="StructSymbol"/>
/// </summary>
/// <param name="Entries">The entries of this layout</param>
public sealed record StructLayout(ImmutableArray<LayoutEntry> Entries)
{
/// <summary>
/// An empty layout with no members
/// </summary>
public static readonly StructLayout Empty = new StructLayout(ImmutableArray<LayoutEntry>.Empty);
}

/// <summary>
/// Represents an entry in a <see cref="StructLayout"/>
/// </summary>
/// <param name="Member">The member symbol associated with this layout entry</param>
/// <param name="ByteOffset">The offset of this entry in bytes</param>
/// <seealso cref="StructLayout"/>
public sealed record LayoutEntry(MemberSymbol Member, int ByteOffset);
/// <remarks>
/// Structs are implicitly sequential in layout. There is no way to provide an offset at which to place a struct.
/// For types that would require such behavior there are separate symbols that may be defined.
/// </remarks>
public sealed record StructSymbol(IdentifierSymbol Identifier, ImmutableArray<FieldSymbol> Fields) : TypeSymbol(Identifier);
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,7 @@ protected virtual StructSymbol VisitStruct(StructSymbol structSymbol)
return new StructSymbol
(
VisitIdentifier(structSymbol.Identifier),
new StructLayout
(
structSymbol.Layout.Entries.Select
(x => new LayoutEntry(VisitMember(x.Member), x.ByteOffset))
.ToImmutableArray()
)
structSymbol.Fields.Select(VisitField).ToImmutableArray()
);
}

Expand Down
8 changes: 4 additions & 4 deletions tests/Silk.NET.SilkTouch.Emitter.Tests/EmitterFieldTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public sealed class EmitterFieldIntegrationTests : EmitterTest
[Fact]
public void FieldIntegration()
{
var syntax = Transform(new FieldSymbol(new StructSymbol(new IdentifierSymbol("int"),StructLayout.Empty), new IdentifierSymbol("Test")));
var syntax = Transform(new FieldSymbol(new StructSymbol(new IdentifierSymbol("int"),ImmutableArray<FieldSymbol>.Empty), new IdentifierSymbol("Test")));

var result = syntax.ToFullString();
Assert.Equal("public int Test;", result);
Expand All @@ -29,7 +29,7 @@ public void FieldIsPublic()
(
new FieldSymbol
(
new StructSymbol(new IdentifierSymbol("int"), StructLayout.Empty),
new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty),
new IdentifierSymbol("Test")
)
) as FieldDeclarationSyntax;
Expand All @@ -45,7 +45,7 @@ public void CorrectTypeIdentifier()
(
new FieldSymbol
(
new StructSymbol(new IdentifierSymbol("int"), StructLayout.Empty),
new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty),
new IdentifierSymbol("Test")
)
) as FieldDeclarationSyntax;
Expand All @@ -63,7 +63,7 @@ public void CorrectIdentifier()
(
new FieldSymbol
(
new StructSymbol(new IdentifierSymbol("int"), StructLayout.Empty),
new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty),
new IdentifierSymbol("Test")
)
) as FieldDeclarationSyntax;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,25 @@ public class EmitterNamespaceMemberTests : EmitterTest
[Fact]
public void SingleMemberIntegration()
{
var syntax = Transform(new NamespaceSymbol(new IdentifierSymbol("Test"), (new []
var syntax = Transform(new NamespaceSymbol(new IdentifierSymbol("Test"), new []
{
(TypeSymbol)new StructSymbol(new IdentifierSymbol("Test2"), StructLayout.Empty)
}).ToImmutableArray()));
(TypeSymbol)new StructSymbol(new IdentifierSymbol("Test2"), ImmutableArray<FieldSymbol>.Empty)
}.ToImmutableArray()));

var result = syntax.ToFullString();
Assert.Equal("namespace Test\n{\n[StructLayout(LayoutKind.Explicit)]\npublic struct Test2\n{\n}\n}\n", result);
Assert.Equal("namespace Test\n{\npublic struct Test2\n{\n}\n}\n", result);
}

[Fact]
public void MultipleMembersIntegration()
{
var syntax = Transform(new NamespaceSymbol(new IdentifierSymbol("Test"), (new []
var syntax = Transform(new NamespaceSymbol(new IdentifierSymbol("Test"), new []
{
(TypeSymbol)new StructSymbol(new IdentifierSymbol("Test2"), StructLayout.Empty),
(TypeSymbol)new StructSymbol(new IdentifierSymbol("Test3"), StructLayout.Empty)
}).ToImmutableArray()));
(TypeSymbol)new StructSymbol(new IdentifierSymbol("Test2"), ImmutableArray<FieldSymbol>.Empty),
(TypeSymbol)new StructSymbol(new IdentifierSymbol("Test3"), ImmutableArray<FieldSymbol>.Empty)
}.ToImmutableArray()));

var result = syntax.ToFullString();
Assert.Equal("namespace Test\n{\n[StructLayout(LayoutKind.Explicit)]\npublic struct Test2\n{\n}\n[StructLayout(LayoutKind.Explicit)]\npublic struct Test3\n{\n}\n}\n", result);
Assert.Equal("namespace Test\n{\npublic struct Test2\n{\n}\npublic struct Test3\n{\n}\n}\n", result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,21 @@ public void SingleFieldIntegration()
(
new StructSymbol
(
new IdentifierSymbol("Test"), new StructLayout((new[]
{
new LayoutEntry(new FieldSymbol
new IdentifierSymbol("Test"), new[]
{
new FieldSymbol
(
new StructSymbol(new IdentifierSymbol("int"), StructLayout.Empty),
new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty),
new IdentifierSymbol("F1")
), 0)
}).ToImmutableArray())
)
}.ToImmutableArray()
)
);

Assert.Equal
(
@"[StructLayout(LayoutKind.Explicit)]
public struct Test
@"public struct Test
{
[FieldOffset(0)]
public int F1;
}
", node.ToFullString()
Expand All @@ -46,37 +44,33 @@ public void MultipleFieldsIntegration()
(
new StructSymbol
(
new IdentifierSymbol("Test"), new StructLayout((new[]
new IdentifierSymbol("Test"), new[]
{
new LayoutEntry(new FieldSymbol
new FieldSymbol
(
new StructSymbol(new IdentifierSymbol("int"), StructLayout.Empty),
new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty),
new IdentifierSymbol("F1")
), 0),
new LayoutEntry(new FieldSymbol
),
new FieldSymbol
(
new StructSymbol(new IdentifierSymbol("int"), StructLayout.Empty),
new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty),
new IdentifierSymbol("F2")
), 20),
new LayoutEntry(new FieldSymbol
),
new FieldSymbol
(
new StructSymbol(new IdentifierSymbol("int"), StructLayout.Empty),
new StructSymbol(new IdentifierSymbol("int"), ImmutableArray<FieldSymbol>.Empty),
new IdentifierSymbol("F3")
), 12)
}).ToImmutableArray())
)
}.ToImmutableArray()
)
);

Assert.Equal
(
@"[StructLayout(LayoutKind.Explicit)]
public struct Test
@"public struct Test
{
[FieldOffset(0)]
public int F1;
[FieldOffset(20)]
public int F2;
[FieldOffset(12)]
public int F3;
}
", node.ToFullString()
Expand Down
13 changes: 6 additions & 7 deletions tests/Silk.NET.SilkTouch.Emitter.Tests/EmitterStructTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,28 @@ public sealed class EmitterStructTests : EmitterTest
[Fact]
public void StructSyntax()
{
var syntax = Transform(new StructSymbol(new IdentifierSymbol("Test"), StructLayout.Empty));
var syntax = Transform(new StructSymbol(new IdentifierSymbol("Test"), ImmutableArray<FieldSymbol>.Empty));
Assert.IsType<StructDeclarationSyntax>(syntax);
}

[Fact]
public void StructKeyword()
{
var syntax = Transform(new StructSymbol(new IdentifierSymbol("Test"), StructLayout.Empty)) as StructDeclarationSyntax;
var syntax = Transform(new StructSymbol(new IdentifierSymbol("Test"), ImmutableArray<FieldSymbol>.Empty)) as StructDeclarationSyntax;
Assert.Equal("struct", syntax!.Keyword.Text);
}

[Fact]
public void CorrectIdentifier()
{
var syntax = Transform(new StructSymbol(new IdentifierSymbol("Test"), StructLayout.Empty)) as StructDeclarationSyntax;
var syntax = Transform(new StructSymbol(new IdentifierSymbol("Test"), ImmutableArray<FieldSymbol>.Empty)) as StructDeclarationSyntax;
Assert.Equal("Test", syntax!.Identifier.Text);
}

[Fact]
public void IsOnlyPublic()
{
var syntax = Transform(new StructSymbol(new IdentifierSymbol("Test"), StructLayout.Empty)) as StructDeclarationSyntax;
var syntax = Transform(new StructSymbol(new IdentifierSymbol("Test"), ImmutableArray<FieldSymbol>.Empty)) as StructDeclarationSyntax;
var @public = Assert.Single(syntax!.Modifiers);
Assert.Equal("public", @public.Text);
}
Expand All @@ -43,10 +43,9 @@ public void IsOnlyPublic()
public void IntegrationEmptyStruct()
{
// Note that this test also covers trivia, which is not checked otherwise.
Assert.Equal(@"[StructLayout(LayoutKind.Explicit)]
public struct Test
Assert.Equal(@"public struct Test
{
}
", Transform(new StructSymbol(new IdentifierSymbol("Test"), StructLayout.Empty)).ToFullString());
", Transform(new StructSymbol(new IdentifierSymbol("Test"), ImmutableArray<FieldSymbol>.Empty)).ToFullString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

namespace LIBRARY_NAMESPACE
{
[StructLayout(LayoutKind.Explicit)]
public struct Test
{
public int f1;
public int f2;
}
}
Loading