Skip to content
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

Type Resolution #1008

Merged
merged 8 commits into from
Aug 6, 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
30 changes: 30 additions & 0 deletions Silk.NET.sln
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.SilkTouch.Integrat
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.SilkTouch.TestFramework", "tests\Silk.NET.SilkTouch.TestFramework\Silk.NET.SilkTouch.TestFramework.csproj", "{381D1039-3259-488F-BB25-D90EE63A3E82}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.SilkTouch.TypeResolution", "src\generators\Silk.NET.SilkTouch.TypeResolution\Silk.NET.SilkTouch.TypeResolution.csproj", "{7D181E77-CAD4-4288-AC0A-9C4D55ED1EC2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.SilkTouch.TypeResolution.Tests", "tests\Silk.NET.SilkTouch.TypeResolution.Tests\Silk.NET.SilkTouch.TypeResolution.Tests.csproj", "{89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -330,6 +334,30 @@ Global
{381D1039-3259-488F-BB25-D90EE63A3E82}.Release|x64.Build.0 = Release|Any CPU
{381D1039-3259-488F-BB25-D90EE63A3E82}.Release|x86.ActiveCfg = Release|Any CPU
{381D1039-3259-488F-BB25-D90EE63A3E82}.Release|x86.Build.0 = Release|Any CPU
{7D181E77-CAD4-4288-AC0A-9C4D55ED1EC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7D181E77-CAD4-4288-AC0A-9C4D55ED1EC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D181E77-CAD4-4288-AC0A-9C4D55ED1EC2}.Debug|x64.ActiveCfg = Debug|Any CPU
{7D181E77-CAD4-4288-AC0A-9C4D55ED1EC2}.Debug|x64.Build.0 = Debug|Any CPU
{7D181E77-CAD4-4288-AC0A-9C4D55ED1EC2}.Debug|x86.ActiveCfg = Debug|Any CPU
{7D181E77-CAD4-4288-AC0A-9C4D55ED1EC2}.Debug|x86.Build.0 = Debug|Any CPU
{7D181E77-CAD4-4288-AC0A-9C4D55ED1EC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D181E77-CAD4-4288-AC0A-9C4D55ED1EC2}.Release|Any CPU.Build.0 = Release|Any CPU
{7D181E77-CAD4-4288-AC0A-9C4D55ED1EC2}.Release|x64.ActiveCfg = Release|Any CPU
{7D181E77-CAD4-4288-AC0A-9C4D55ED1EC2}.Release|x64.Build.0 = Release|Any CPU
{7D181E77-CAD4-4288-AC0A-9C4D55ED1EC2}.Release|x86.ActiveCfg = Release|Any CPU
{7D181E77-CAD4-4288-AC0A-9C4D55ED1EC2}.Release|x86.Build.0 = Release|Any CPU
{89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}.Debug|x64.ActiveCfg = Debug|Any CPU
{89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}.Debug|x64.Build.0 = Debug|Any CPU
{89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}.Debug|x86.ActiveCfg = Debug|Any CPU
{89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}.Debug|x86.Build.0 = Debug|Any CPU
{89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}.Release|Any CPU.Build.0 = Release|Any CPU
{89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}.Release|x64.ActiveCfg = Release|Any CPU
{89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}.Release|x64.Build.0 = Release|Any CPU
{89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}.Release|x86.ActiveCfg = Release|Any CPU
{89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -361,6 +389,8 @@ Global
{5329AC43-7177-4953-AFAB-A9FA7B9A4C7C} = {94D5D1E1-B998-4CB1-9D04-DA138A2B0F3C}
{66FE736C-C407-44C3-A94E-4345E22AA95E} = {94D5D1E1-B998-4CB1-9D04-DA138A2B0F3C}
{381D1039-3259-488F-BB25-D90EE63A3E82} = {94D5D1E1-B998-4CB1-9D04-DA138A2B0F3C}
{7D181E77-CAD4-4288-AC0A-9C4D55ED1EC2} = {8238D9F3-E158-4633-8017-B29AA3AD61F7}
{89E8EDA4-EB19-45FC-AFA1-B6A16211A9EE} = {94D5D1E1-B998-4CB1-9D04-DA138A2B0F3C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F5273D7F-3334-48DF-94E3-41AE6816CD4D}
Expand Down
9 changes: 5 additions & 4 deletions src/generators/Silk.NET.SilkTouch.Emitter/CSharpEmitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ public CSharpEmitter()
{

}

/// <summary>
/// Transforms the given <see cref="Silk.NET.SilkTouch.Symbols.Symbol"/> into a <see cref="CSharpSyntaxNode"/>
/// </summary>
/// <param name="symbol">The symbol to transform</param>
/// <param name="typeStore">The <see cref="TypeStore"/> used</param>
/// <returns>A syntax node, containing syntax depending on the symbol. The syntax node should produce valid C# code.</returns>
/// <remarks>
/// The returned syntax node may not be ideal and is not optimized for code size.
Expand All @@ -39,9 +40,9 @@ public CSharpEmitter()
/// The returned node will never contain line comments, but other C# language feature may still rely on whitespace and/or newlines.
/// Note that (block) comments will never be used to replace such whitespace, even if this is valid to allow a potential comment stripping to be a simple as possible.
/// </remarks>
public CSharpSyntaxNode Transform(Symbol symbol)
public CSharpSyntaxNode Transform(Symbol symbol, TypeStore typeStore)
{
var visitor = new Visitor(Whitespace(" "));
var visitor = new Visitor(Whitespace(" "), typeStore);
visitor.Visit(symbol); // the result is ignored. This allows us to optimize the visitor in some cases.
var syntax = visitor.Syntax;
if (syntax is null)
Expand All @@ -61,7 +62,7 @@ private class Visitor : Silk.NET.SilkTouch.Symbols.SymbolVisitor
private IEnumerable<SyntaxTrivia> Indentation => Enumerable.Repeat(_indentation, _indentationCount);
private IEnumerable<SyntaxTrivia> NewLine => Indentation.Prepend(LineFeed);

public Visitor(SyntaxTrivia indentation) : base()
public Visitor(SyntaxTrivia indentation, TypeStore typeStore) : base(typeStore)
{
_indentation = indentation;
}
Expand Down
5 changes: 3 additions & 2 deletions src/generators/Silk.NET.SilkTouch.Scraper/ClangScraper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ public ClangScraper(ILoggerFactory loggerFactory, IOptions<ClangScraperConfigura
/// Scrapes the given XML document for symbols
/// </summary>
/// <param name="document">A XML Document, the format is assumed to be similar to what ClangSharp would output.</param>
/// <param name="typeStore">A <see cref="TypeStore"/> used when creating symbols</param>
/// <returns>Any number of symbols scraped from the given xml</returns>
public IEnumerable<Symbol> ScrapeXML(XmlDocument document)
public IEnumerable<Symbol> ScrapeXML(XmlDocument document, TypeStore typeStore)
{
var bindings = document.ChildNodes.Cast<XmlNode>().OfType<XmlElement>().FirstOrDefault();

Expand All @@ -59,7 +60,7 @@ public IEnumerable<Symbol> ScrapeXML(XmlDocument document)
return Enumerable.Empty<Symbol>();
}

var visitor = new XmlVisitor(_loggerFactory.CreateLogger<XmlVisitor>());
var visitor = new XmlVisitor(_loggerFactory.CreateLogger<XmlVisitor>(), typeStore);
return visitor.Visit(bindings);
}

Expand Down
23 changes: 18 additions & 5 deletions src/generators/Silk.NET.SilkTouch.Scraper/XmlVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ namespace Silk.NET.SilkTouch.Scraper;
internal sealed class XmlVisitor
{
private readonly ILogger _logger;
public XmlVisitor(ILogger logger) {
private readonly TypeStore _typeStore;
public XmlVisitor(ILogger logger, TypeStore typeStore)
{
_logger = logger;
_typeStore = typeStore;
}

public IEnumerable<Symbol> Visit(XmlNode node)
Expand Down Expand Up @@ -134,13 +137,17 @@ private IEnumerable<Symbol> VisitStruct(XmlElement @struct)
}
}
}

return new[]
{
new StructSymbol
StoreType
(
new IdentifierSymbol(@struct.Attributes?["name"]?.Value ?? throw new InvalidOperationException()),
fields.ToImmutableArray()
new StructSymbol
(
TypeId.CreateNew(),
new IdentifierSymbol(@struct.Attributes?["name"]?.Value ?? throw new InvalidOperationException()),
fields.ToImmutableArray()
)
)
};
}
Expand Down Expand Up @@ -171,4 +178,10 @@ private IEnumerable<Symbol> VisitNamespace(XmlElement @namespace)
)
};
}

private T StoreType<T>(T instance) where T : TypeSymbol
{
_typeStore.Store(instance);
return instance;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Silk.NET.SilkTouch.Symbols;
/// <summary>
/// Represents a reference to a type that is also defined as part of this symbol tree.
/// </summary>
/// <param name="Referenced">The Type referenced</param>
public sealed record InternalTypeReference(TypeSymbol Referenced) : TypeReference()
/// <param name="ReferencedTypeId">The <see cref="TypeSymbol.Id"/> of the <see cref="TypeSymbol"/> referenced.</param>
public sealed record InternalTypeReference(TypeId ReferencedTypeId) : TypeReference()
{
}
4 changes: 3 additions & 1 deletion src/generators/Silk.NET.SilkTouch.Symbols/StructSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace Silk.NET.SilkTouch.Symbols;
/// <summary>
/// A <see cref="TypeSymbol"/> representing a <c>struct</c>.
/// </summary>
/// <param name="Id">An Identifier used for referencing types globally</param>
/// <param name="Identifier">The Identifier of this struct</param>
/// <param name="Fields">The fields of this struct</param>
/// <remarks>
Expand All @@ -17,4 +18,5 @@ namespace Silk.NET.SilkTouch.Symbols;
/// 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);
public sealed record StructSymbol
(TypeId Id, IdentifierSymbol Identifier, ImmutableArray<FieldSymbol> Fields) : TypeSymbol(Id, Identifier);
40 changes: 37 additions & 3 deletions src/generators/Silk.NET.SilkTouch.Symbols/SymbolVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ namespace Silk.NET.SilkTouch.Symbols;
/// </summary>
public abstract class SymbolVisitor
{
/// <summary>
/// The <see cref="Silk.NET.SilkTouch.Symbols.TypeStore"/> used by this <see cref="SymbolVisitor"/>
/// </summary>
protected TypeStore TypeStore { get; }

/// <summary>
/// Creates a <see cref="SymbolVisitor"/> with it's dependencies.
/// </summary>
/// <param name="typeStore">The <see cref="Silk.NET.SilkTouch.Symbols.TypeStore"/> to use</param>
public SymbolVisitor(TypeStore typeStore)
{
TypeStore = typeStore;
}

/// <summary>
/// Visit a <see cref="Symbol"/>. This will call the appropriate method based on the actual type of the <paramref name="symbol"/>
/// </summary>
Expand Down Expand Up @@ -82,7 +96,7 @@ protected virtual TypeReference VisitTypeReference(TypeReference typeReference)
/// </remarks>
protected virtual InternalTypeReference VisitInternalTypeReference(InternalTypeReference typeReference)
{
return new InternalTypeReference(VisitType(typeReference.Referenced));
return new InternalTypeReference(VisitTypeId(typeReference.ReferencedTypeId));
}

/// <summary>
Expand All @@ -103,14 +117,33 @@ protected virtual ExternalTypeReference VisitExternalTypeReference(ExternalTypeR
}

/// <summary>
/// Visit a <see cref="TypeSymbol"/>. This will call the appropriate method based on the actual type of the <paramref name="typeSymbol"/>
/// Called when a type is referenced by it's Id and should be visited. Implementers may resolve the Id if they to do so.
/// The default implementation does not resolve the Id or call any further into the tree.
/// </summary>
/// <param name="id">The Id of the type</param>
/// <returns>The new Id that should be used to reference this type</returns>
protected virtual TypeId VisitTypeId(TypeId id)
{
return id;
}

/// <summary>
/// Visit a <see cref="TypeSymbol"/>. This will call the appropriate method based on the actual type of the <paramref name="typeSymbol"/>.
/// This will update the <see cref="TypeStore"/> with it's return value by default. Implementors overriding this should update the store themselves.
/// </summary>
/// <param name="typeSymbol">The type symbol to visit</param>
/// <returns>The rewritten symbol</returns>
/// <seealso cref="VisitStruct"/>
protected virtual TypeSymbol VisitType(TypeSymbol typeSymbol)
{
if (typeSymbol is StructSymbol @struct) return VisitStruct(@struct);
TypeSymbol? result = null;
if (typeSymbol is StructSymbol @struct) result = VisitStruct(@struct);

if (result is not null)
{
TypeStore.Store(result);
return result;
}

return ThrowUnknownSymbol<TypeSymbol>(typeSymbol);
}
Expand All @@ -128,6 +161,7 @@ protected virtual StructSymbol VisitStruct(StructSymbol structSymbol)
{
return new StructSymbol
(
structSymbol.Id,
VisitIdentifier(structSymbol.Identifier),
structSymbol.Fields.Select(VisitField).ToImmutableArray()
);
Expand Down
37 changes: 37 additions & 0 deletions src/generators/Silk.NET.SilkTouch.Symbols/TypeId.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Silk.NET.SilkTouch.Symbols;

/// <summary>
/// The Id of a <see cref="TypeSymbol"/>
/// </summary>
public readonly struct TypeId : IEquatable<TypeId>
{
private readonly Guid _guid;
private TypeId(Guid guid)
{
_guid = guid;
}

/// <summary>
/// Creates a new, unique, instance
/// </summary>
/// <returns>The new instance</returns>
public static TypeId CreateNew() => new TypeId(Guid.NewGuid());

/// <inheritdoc />
public bool Equals(TypeId other) => _guid.Equals(other._guid);

/// <inheritdoc />
public override bool Equals(object? obj) => obj is TypeId other && Equals(other);

/// <inheritdoc />
public override int GetHashCode() => _guid.GetHashCode();

/// <inheritdoc />
public static bool operator ==(TypeId left, TypeId right) => left.Equals(right);

/// <inheritdoc />
public static bool operator !=(TypeId left, TypeId right) => !left.Equals(right);
}
34 changes: 34 additions & 0 deletions src/generators/Silk.NET.SilkTouch.Symbols/TypeStore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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.Concurrent;

namespace Silk.NET.SilkTouch.Symbols;

/// <summary>
/// A simple type store to store and reference <see cref="TypeSymbol"/>s by Id
/// </summary>
public sealed class TypeStore
{
private ConcurrentDictionary<TypeId, TypeSymbol> _dictionary = new();

/// <summary>
/// Stores the given <see cref="TypeSymbol"/> for later resolution
/// </summary>
/// <param name="typeSymbol">The <see cref="TypeSymbol"/> to store</param>
public void Store(TypeSymbol typeSymbol)
{
_dictionary[typeSymbol.Id] = typeSymbol;
}

/// <summary>
/// Resolves a <see cref="TypeSymbol"/> by it's <see cref="TypeSymbol.Id"/>
/// </summary>
/// <param name="id">The Id to use for resolution</param>
/// <param name="typeSymbol">The <see cref="TypeSymbol"/> found or null</param>
/// <returns>Whether a type could be resolved</returns>
public bool TryResolve(TypeId id, out TypeSymbol? typeSymbol)
{
return _dictionary.TryGetValue(id, out typeSymbol);
}
}
3 changes: 2 additions & 1 deletion src/generators/Silk.NET.SilkTouch.Symbols/TypeSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Silk.NET.SilkTouch.Symbols;
/// <summary>
/// A generic <see cref="Symbol"/> representing a named type.
/// </summary>
/// <param name="Id">An Identifier used for referencing types globally</param>
/// <param name="Identifier">The identifier of this type</param>
/// <seealso cref="StructSymbol"/>
public abstract record TypeSymbol(IdentifierSymbol Identifier) : Symbol;
public abstract record TypeSymbol(TypeId Id, IdentifierSymbol Identifier) : Symbol;
Loading