Skip to content

Method symbols #1032

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 3 commits into from
Aug 13, 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
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,19 @@ Parent Symbols (Unlisted, abstract):
| Symbol | |
| TypeReference | [here](../type-references.md) |
| TypeSymbol | |

| Name | Symbol Layer File | Symbol Layer Tests | Emitter Tests |
| ----------------------- | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| 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) |
| 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) |
| 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) |
| 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) |
| 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) |
| 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) |
| 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) |
| UnresolvedTypeReference | [here](src/generators/Silk.NET.SilkTouch.Symbols/UnresolvedTypeReference.cs) | [here](tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/UnresolvedTypeReferenceTests.cs) | - |
| MethodSymbol |

| Name | Symbol Layer File | Symbol Layer Tests | Emitter Tests |
| -------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| 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) |
| 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) |
| 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) |
| 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) |
| 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) |
| 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) |
| 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) |
| 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) |
| UnresolvedTypeReference | [here](src/generators/Silk.NET.SilkTouch.Symbols/UnresolvedTypeReference.cs) | [here](tests/Silk.NET.SilkTouch.Symbols.Tests/SymbolVisitorTests/UnresolvedTypeReferenceTests.cs) | - |

## How to create a symbol

Expand Down
49 changes: 49 additions & 0 deletions src/generators/Silk.NET.SilkTouch.Emitter/CSharpEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -228,6 +229,54 @@ protected override ExternalTypeReference VisitExternalTypeReference(ExternalType
return typeReference;
}

protected override StaticExternalMethodSymbol VisitStaticExternalMethod(StaticExternalMethodSymbol staticExternalMethodSymbol)
{
AssertClearState();

VisitTypeReference(staticExternalMethodSymbol.ReturnType);
if (_syntax is not TypeSyntax returnSyntaxToken)
throw new InvalidOperationException("Type Reference not correctly visited");
ClearState();

VisitIdentifier(staticExternalMethodSymbol.Identifier);
if (_syntaxToken is not {} identifierToken)
throw new InvalidOperationException("Type Reference not correctly visited");
ClearState();

var parameters = staticExternalMethodSymbol.Parameters.Select
(
x =>
{
VisitTypeReference(x.TypeReference);
if (_syntax is not TypeSyntax resultToken)
throw new InvalidOperationException("Type Reference not correctly visited");
ClearState();

VisitIdentifier(x.Identifier);
if (_syntaxToken is not {} identifierToken2)
throw new InvalidOperationException("Syntax Token was not correctly visited");
ClearState();

return Parameter(identifierToken2).WithType(resultToken);
}
).ToImmutableArray();

_syntax = MethodDeclaration(returnSyntaxToken.WithLeadingTrivia(Space), identifierToken.WithLeadingTrivia(Space))
.WithModifiers
(
TokenList
(
Token(SyntaxKind.PublicKeyword),
Token(SyntaxKind.StaticKeyword).WithLeadingTrivia(Space),
Token(SyntaxKind.ExternKeyword).WithLeadingTrivia(Space)
)
)
.WithParameterList(ParameterList(SeparatedList(parameters)))
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));

return staticExternalMethodSymbol;
}

protected override IdentifierSymbol VisitIdentifier(IdentifierSymbol identifierSymbol)
{
AssertClearState();
Expand Down
18 changes: 18 additions & 0 deletions src/generators/Silk.NET.SilkTouch.Symbols/MethodSymbol.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Immutable;

namespace Silk.NET.SilkTouch.Symbols;

/// <summary>
/// A <see cref="MemberSymbol"/> representing a method with a signature.
/// </summary>
public abstract record MethodSymbol(TypeReference ReturnType, ImmutableArray<Parameter> Parameters, IdentifierSymbol Identifier) : MemberSymbol;

/// <summary>
/// Represents a Parameter to a <see cref="MethodSymbol"/>
/// </summary>
/// <param name="TypeReference">The type of this parameter</param>
/// <param name="Identifier">The identifier of this parameter</param>
public sealed record Parameter(TypeReference TypeReference, IdentifierSymbol Identifier);
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Immutable;

namespace Silk.NET.SilkTouch.Symbols;

/// <summary>
/// A static external method symbol.
/// External meaning it must be loaded
/// </summary>
public sealed record StaticExternalMethodSymbol
(TypeReference ReturnType, ImmutableArray<Parameter> Parameters, IdentifierSymbol Identifier)
: MethodSymbol(ReturnType, Parameters, Identifier)
{
}
38 changes: 37 additions & 1 deletion src/generators/Silk.NET.SilkTouch.Symbols/SymbolVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public virtual Symbol Visit(Symbol symbol)
protected virtual MemberSymbol VisitMember(MemberSymbol memberSymbol)
{
if (memberSymbol is FieldSymbol fs) return VisitField(fs);
if (memberSymbol is MethodSymbol ms) return VisitMethod(ms);

return ThrowUnknownSymbol<MemberSymbol>(memberSymbol);
}
Expand All @@ -66,7 +67,42 @@ protected virtual FieldSymbol VisitField(FieldSymbol fieldSymbol)
{
return new FieldSymbol(VisitTypeReference(fieldSymbol.Type), VisitIdentifier(fieldSymbol.Identifier));
}


/// <summary>
/// Visit a <see cref="MethodSymbol"/>. Will call the appropriate methods to visit the different parts of the symbol.
/// </summary>
/// <param name="methodSymbol">The method to visit</param>
/// <returns>The rewritten symbol</returns>
/// <remarks>
/// The order in which the parts are visited is kept as an implementation detail. Do not rely on this order.
/// </remarks>
protected virtual MethodSymbol VisitMethod(MethodSymbol methodSymbol)
{
if (methodSymbol is StaticExternalMethodSymbol stms) return VisitStaticExternalMethod(stms);
return ThrowUnknownSymbol<MethodSymbol>(methodSymbol);
}

/// <summary>
/// Visit a <see cref="StaticExternalMethodSymbol"/>. Will call the appropriate methods to visit the different parts of the symbol.
/// </summary>
/// <param name="staticExternalMethodSymbol">The method to visit</param>
/// <returns>The rewritten symbol</returns>
/// <remarks>
/// The order in which the parts are visited is kept as an implementation detail. Do not rely on this order.
/// </remarks>
protected virtual StaticExternalMethodSymbol VisitStaticExternalMethod
(StaticExternalMethodSymbol staticExternalMethodSymbol)
{
return new StaticExternalMethodSymbol
(
VisitTypeReference(staticExternalMethodSymbol.ReturnType),
staticExternalMethodSymbol.Parameters.Select
(x => new Parameter(VisitTypeReference(x.TypeReference), VisitIdentifier(x.Identifier)))
.ToImmutableArray(),
VisitIdentifier(staticExternalMethodSymbol.Identifier)
);
}

/// <summary>
/// Visit a <see cref="TypeReference"/>. Will call the appropriate methods to visit the different parts of the symbol.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Immutable;
using Silk.NET.SilkTouch.Symbols;
using Xunit;

namespace Silk.NET.SilkTouch.Emitter.Tests;

public class StaticExternalMethodSymbolTests : EmitterTest
{
[Fact, Trait("Category", "Symbols")]
public void StringTestNoParameters()
{
var symbol = new StaticExternalMethodSymbol
(
new ExternalTypeReference(null, new IdentifierSymbol("int")),
ImmutableArray<Parameter>.Empty,
new IdentifierSymbol("M")
);

var transformed = Transform(symbol);

Assert.Equal("public static extern int M();", transformed.ToFullString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Immutable;
using Moq;
using Moq.Protected;
using Xunit;

namespace Silk.NET.SilkTouch.Symbols.Tests.SymbolVisitorTests;

public class StaticExternalMethodSymbolTests
{
[Fact, Trait("Category", "Symbols")]
public void VisitedAsSelf()
{
var symbol = new StaticExternalMethodSymbol
(new InternalTypeReference(TypeId.CreateNew()), ImmutableArray<Parameter>.Empty, new IdentifierSymbol(""));

var visitor = new Mock<MockSymbolVisitor> { CallBase = true };

visitor.Object.Visit(symbol);

visitor.Protected()
.Verify<StaticExternalMethodSymbol>
("VisitStaticExternalMethod", Times.Once(), ItExpr.IsAny<StaticExternalMethodSymbol>());
}

[Fact, Trait("Category", "Symbols")]
public void VisitedAsMethod()
{
var symbol = new StaticExternalMethodSymbol
(new InternalTypeReference(TypeId.CreateNew()), ImmutableArray<Parameter>.Empty, new IdentifierSymbol(""));

var visitor = new Mock<MockSymbolVisitor> { CallBase = true };

visitor.Object.Visit(symbol);

visitor.Protected()
.Verify<MethodSymbol>
("VisitMethod", Times.Once(), ItExpr.IsAny<MethodSymbol>());
}
}