Skip to content

Commit b486b41

Browse files
authored
Function pointer support (#1037)
1 parent 736c57e commit b486b41

File tree

10 files changed

+270
-14
lines changed

10 files changed

+270
-14
lines changed

documentation/for-contributors/generators/symbol-layer/symbols/README.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,19 @@ Parent Symbols (Unlisted, abstract):
1919
| TypeSymbol | |
2020
| MethodSymbol |
2121

22-
| Name | Symbol Layer File | Symbol Layer Tests | Emitter Tests |
23-
| -------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
24-
| ClassSymbol | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/ClassSymbol.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/ClassSymbolTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/ClassSymbolTests.cs) |
25-
| ExternalTypeReference | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/ExternalTypeReference.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/ExternalTypeReferenceTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/ExternalTypeReferenceTests.cs) |
26-
| FieldSymbol | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/FieldSymbol.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/FieldTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/EmitterFieldTests.cs) |
27-
| IdentifierSymbol | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/IdentifierSymbol.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/IdentifierTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/IdentifierSymbolTests.cs) |
28-
| InternalTypeReference | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/InternalTypeReference.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/InternalTypeReferenceTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/InternalTypeReferenceTests.cs) |
29-
| NamespaceSymbol | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/NamespaceSymbol.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/NamespaceTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/EmitterNamespaceTests.cs) |
30-
| PointerTypeReference | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/PointerTypeReference.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/PointerTypeReferenceTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/PointerTypeReferenceTests.cs) |
31-
| StaticExternalMethodSymbol | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/StaticExternalMethodSymbol.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/StaticExternalMethodSymbolTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/StaticExternalMethodSymbolTests.cs) |
32-
| StructSymbol | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/StructSymbol.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/StructTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/EmitterStructTests.cs) |
33-
| UnresolvedTypeReference | [here](src/generators/Silk.NET.SilkTouch.Symbols/UnresolvedTypeReference.cs) | [here](tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/UnresolvedTypeReferenceTests.cs) | - |
22+
| Name | Symbol Layer File | Symbol Layer Tests | Emitter Tests |
23+
| ---------------------------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
24+
| ClassSymbol | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/ClassSymbol.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/ClassSymbolTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/ClassSymbolTests.cs) |
25+
| ExternalTypeReference | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/ExternalTypeReference.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/ExternalTypeReferenceTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/ExternalTypeReferenceTests.cs) |
26+
| FunctionPointerTypeReference | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/FunctionPointerTypeReference.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/FunctionPointerTypeReferenceTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/FunctionPointerTypeReferenceTests.cs) |
27+
| FieldSymbol | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/FieldSymbol.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/FieldTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/EmitterFieldTests.cs) |
28+
| IdentifierSymbol | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/IdentifierSymbol.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/IdentifierTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/IdentifierSymbolTests.cs) |
29+
| InternalTypeReference | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/InternalTypeReference.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/InternalTypeReferenceTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/InternalTypeReferenceTests.cs) |
30+
| NamespaceSymbol | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/NamespaceSymbol.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/NamespaceTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/EmitterNamespaceTests.cs) |
31+
| PointerTypeReference | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/PointerTypeReference.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/PointerTypeReferenceTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/PointerTypeReferenceTests.cs) |
32+
| StaticExternalMethodSymbol | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/StaticExternalMethodSymbol.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/StaticExternalMethodSymbolTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/StaticExternalMethodSymbolTests.cs) |
33+
| StructSymbol | [here](../../../../../src/generators/Silk.NET.SilkTouch.Symbols/StructSymbol.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/StructTests.cs) | [here](../../../../../tests/Silk.NET.SilkTouch.Emitter.Tests/EmitterStructTests.cs) |
34+
| UnresolvedTypeReference | [here](src/generators/Silk.NET.SilkTouch.Symbols/UnresolvedTypeReference.cs) | [here](tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/UnresolvedTypeReferenceTests.cs) | - |
3435

3536
## How to create a symbol
3637

src/generators/Silk.NET.SilkTouch.DotnetTool/Program.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,11 @@ private static IEnumerable<Symbol> ProcessSymbols
342342
visitors.Add(ActivatorUtilities.CreateInstance<PointerTypeResolver>(serviceProvider, typeStore));
343343
}
344344

345+
if ((usedSymbolVisitors & AvailableSymbolVisitors.FunctionPointerTypeResolver) != 0)
346+
{
347+
visitors.Add(ActivatorUtilities.CreateInstance<FunctionPointerTypeResolver>(serviceProvider, typeStore));
348+
}
349+
345350
if ((usedSymbolVisitors & AvailableSymbolVisitors.InternalTypeResolver) != 0)
346351
{
347352
var typeScopeSymbolVisitor = ActivatorUtilities.CreateInstance<TypeScopeSymbolVisitor>
@@ -518,7 +523,8 @@ private enum AvailableSymbolVisitors
518523
PointerTypeResolver = 1 << 1,
519524
InternalTypeResolver = 1 << 2,
520525
PrimitiveTypeResolver = 1 << 3,
521-
AllTypeResolvers = PointerTypeResolver | InternalTypeResolver | PrimitiveTypeResolver,
526+
FunctionPointerTypeResolver = 1 << 4,
527+
AllTypeResolvers = PointerTypeResolver | InternalTypeResolver | PrimitiveTypeResolver | FunctionPointerTypeResolver,
522528
}
523529
}
524530
}

src/generators/Silk.NET.SilkTouch.Emitter/CSharpEmitter.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,45 @@ protected override PointerTypeReference VisitPointerTypeReference(PointerTypeRef
236236
return pointerTypeReference;
237237
}
238238

239+
protected override FunctionPointerTypeReference VisitFunctionPointerTypeReference
240+
(FunctionPointerTypeReference functionPointerTypeReference)
241+
{
242+
AssertClearState();
243+
244+
var paramList = functionPointerTypeReference.ParameterTypes
245+
.Append(functionPointerTypeReference.ReturnType)
246+
.Select
247+
(
248+
(x, i) =>
249+
{
250+
VisitTypeReference(x);
251+
if (_syntax is not TypeSyntax typeSyntax)
252+
throw new InvalidOperationException("TypeReference did not return TypeSyntax");
253+
ClearState();
254+
255+
if (i == 0 || i > functionPointerTypeReference.ParameterTypes.Length)
256+
return typeSyntax;
257+
else
258+
return typeSyntax.WithLeadingTrivia(Space); // not ideal, but the easiest way to do this
259+
}
260+
)
261+
.Select(FunctionPointerParameter)
262+
.ToImmutableArray();
263+
264+
_syntax = FunctionPointerType
265+
(
266+
Token(SyntaxKind.DelegateKeyword),
267+
Token(SyntaxKind.AsteriskToken),
268+
FunctionPointerCallingConvention(Token(SyntaxKind.UnmanagedKeyword)).WithLeadingTrivia(Space),
269+
FunctionPointerParameterList
270+
(
271+
SeparatedList(paramList)
272+
)
273+
);
274+
275+
return functionPointerTypeReference;
276+
}
277+
239278
protected override ExternalTypeReference VisitExternalTypeReference(ExternalTypeReference typeReference)
240279
{
241280
AssertClearState();
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Immutable;
5+
6+
namespace Silk.NET.SilkTouch.Symbols;
7+
8+
/// <summary>
9+
/// A <see cref="TypeReference"/> representing a function pointer.
10+
/// </summary>
11+
public sealed record FunctionPointerTypeReference(TypeReference ReturnType, ImmutableArray<TypeReference> ParameterTypes) : TypeReference
12+
{
13+
}

src/generators/Silk.NET.SilkTouch.Symbols/SymbolVisitor.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ protected virtual TypeReference VisitTypeReference(TypeReference typeReference)
120120
if (typeReference is InternalTypeReference itr) return VisitInternalTypeReference(itr);
121121
if (typeReference is UnresolvedTypeReference utr) UnresolvedTypeReference.ThrowInvalidSymbol();
122122
if (typeReference is PointerTypeReference ptr) return VisitPointerTypeReference(ptr);
123+
if (typeReference is FunctionPointerTypeReference fptr) return VisitFunctionPointerTypeReference(fptr);
123124
return ThrowUnknownSymbol<TypeReference>(typeReference);
124125
}
125126

@@ -135,7 +136,24 @@ protected virtual PointerTypeReference VisitPointerTypeReference(PointerTypeRefe
135136
{
136137
return new PointerTypeReference(VisitTypeReference(pointerTypeReference.Underlying));
137138
}
138-
139+
/// <summary>
140+
/// Visit a <see cref="FunctionPointerTypeReference"/>. Will call the appropriate methods to visit the different parts of the symbol.
141+
/// </summary>
142+
/// <param name="functionPointerTypeReference">The function pointer type reference to visit</param>
143+
/// <returns>The rewritten symbol</returns>
144+
/// <remarks>
145+
/// The order in which the parts are visited is kept as an implementation detail. Do not rely on this order.
146+
/// </remarks>
147+
protected virtual FunctionPointerTypeReference VisitFunctionPointerTypeReference
148+
(FunctionPointerTypeReference functionPointerTypeReference)
149+
{
150+
return new FunctionPointerTypeReference
151+
(
152+
VisitTypeReference(functionPointerTypeReference.ReturnType),
153+
functionPointerTypeReference.ParameterTypes.Select(VisitTypeReference).ToImmutableArray()
154+
);
155+
}
156+
139157
/// <summary>
140158
/// Visit a <see cref="InternalTypeReference"/>. Will call the appropriate methods to visit the different parts of the symbol.
141159
/// </summary>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Immutable;
5+
using System.Text.RegularExpressions;
6+
using Silk.NET.SilkTouch.Symbols;
7+
8+
namespace Silk.NET.SilkTouch.TypeResolution;
9+
10+
/// <summary>
11+
/// A <see cref="SimpleTypeResolverBase"/> that resolves strings of standard C# form
12+
/// </summary>
13+
public class FunctionPointerTypeResolver : SimpleTypeResolverBase
14+
{
15+
private static readonly Regex _regex = new
16+
(/*lang=regex*/
17+
@"delegate\*\sunmanaged(\[((?'modifier'.(?=,\s?)?)+)*\])?\<((?'parameter'(.(?=,\s?)+))(,\s?))*(?'return_type'(.)+)\>",
18+
RegexOptions.CultureInvariant
19+
);
20+
21+
/// <inheritdoc />
22+
public FunctionPointerTypeResolver(TypeStore typeStore) : base(typeStore)
23+
{
24+
}
25+
26+
/// <inheritdoc />
27+
protected override bool TryResolve(UnresolvedTypeReference utr, out TypeReference? resolved)
28+
{
29+
if (utr.Text.StartsWith("delegate*"))
30+
{
31+
var match = _regex.Match(utr.Text);
32+
if (match.Success)
33+
{
34+
var parameters = match.Groups["parameter"]
35+
.Captures.OfType<Capture>()
36+
.Select(x => new UnresolvedTypeReference(x.Value))
37+
.Cast<TypeReference>()
38+
.ToImmutableArray();
39+
var returnType = new UnresolvedTypeReference(match.Groups["return_type"].Value);
40+
41+
resolved = new FunctionPointerTypeReference(returnType, parameters);
42+
return true;
43+
}
44+
}
45+
resolved = null;
46+
return false;
47+
}
48+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Immutable;
5+
using Silk.NET.SilkTouch.Symbols;
6+
using Xunit;
7+
8+
namespace Silk.NET.SilkTouch.Emitter.Tests;
9+
10+
public class FunctionPointerTypeReferenceTests : EmitterTest
11+
{
12+
[Fact, Trait("Category", "Symbols"),
13+
Trait("Target Language", "C#")]
14+
public void StringTestNoParams()
15+
{
16+
var symbol = new FunctionPointerTypeReference
17+
(new ExternalTypeReference(null, new IdentifierSymbol("Ret")), ImmutableArray<TypeReference>.Empty);
18+
19+
var transformed = Transform(symbol);
20+
21+
Assert.Equal("delegate* unmanaged<Ret>", transformed.ToFullString());
22+
}
23+
24+
[Fact, Trait("Category", "Symbols"),
25+
Trait("Target Language", "C#")]
26+
public void StringTestWithParams()
27+
{
28+
var symbol = new FunctionPointerTypeReference
29+
(new ExternalTypeReference(null, new IdentifierSymbol("Ret")), new TypeReference[]
30+
{
31+
new ExternalTypeReference(null, new IdentifierSymbol("Param1")),
32+
new ExternalTypeReference(null, new IdentifierSymbol("Param2")),
33+
}.ToImmutableArray());
34+
35+
var transformed = Transform(symbol);
36+
37+
Assert.Equal("delegate* unmanaged<Param1, Param2, Ret>", transformed.ToFullString());
38+
}
39+
}

tests/Silk.NET.SilkTouch.IntegrationTests/TestHelper.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public static string GetCSharpOutputFromCpp(string cpp)
5555
var processors = new SymbolVisitor[]
5656
{
5757
ActivatorUtilities.CreateInstance<PointerTypeResolver>(serviceProvider, typeStore),
58+
ActivatorUtilities.CreateInstance<FunctionPointerTypeResolver>(serviceProvider, typeStore),
5859
ActivatorUtilities.CreateInstance<PrimitiveTypeResolver>(serviceProvider, typeStore),
5960

6061
typeScopeSymbolVisitor,
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Immutable;
5+
using Moq;
6+
using Moq.Protected;
7+
using Xunit;
8+
9+
namespace Silk.NET.SilkTouch.Symbols.Tests.SymbolVisitorTests;
10+
11+
public class FunctionPointerTypeReferenceTests
12+
{
13+
[Fact, Trait("Category", "Symbols")]
14+
public void VisitedAsSelf()
15+
{
16+
var symbol = new FunctionPointerTypeReference(new InternalTypeReference(TypeId.CreateNew()), ImmutableArray<TypeReference>.Empty);
17+
var visitor = new Mock<MockSymbolVisitor> { CallBase = true };
18+
visitor.Object.Visit(symbol);
19+
20+
visitor.Protected()
21+
.Verify<FunctionPointerTypeReference>
22+
("VisitFunctionPointerTypeReference", Times.Once(), ItExpr.IsAny<FunctionPointerTypeReference>());
23+
}
24+
25+
[Fact, Trait("Category", "Symbols")]
26+
public void VisitedAsRef()
27+
{
28+
var symbol = new FunctionPointerTypeReference(new InternalTypeReference(TypeId.CreateNew()), ImmutableArray<TypeReference>.Empty);
29+
var visitor = new Mock<MockSymbolVisitor> { CallBase = true };
30+
visitor.Object.Visit(symbol);
31+
32+
visitor.Protected()
33+
.Verify<TypeReference>
34+
("VisitTypeReference", Times.Once(), ItExpr.Is<TypeReference>(x => ReferenceEquals(x, symbol)));
35+
}
36+
}

0 commit comments

Comments
 (0)