Skip to content

Commit

Permalink
Type Resolution & Type IDs (#1008)
Browse files Browse the repository at this point in the history
  • Loading branch information
HurricanKai authored Aug 6, 2022
1 parent b41cf8f commit 290052d
Show file tree
Hide file tree
Showing 38 changed files with 734 additions and 139 deletions.
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

0 comments on commit 290052d

Please sign in to comment.