diff --git a/src/generators/Silk.NET.SilkTouch.DotnetTool/Silk.NET.SilkTouch.DotnetTool.csproj b/src/generators/Silk.NET.SilkTouch.DotnetTool/Silk.NET.SilkTouch.DotnetTool.csproj index 38845f6e12..fa9581a595 100644 --- a/src/generators/Silk.NET.SilkTouch.DotnetTool/Silk.NET.SilkTouch.DotnetTool.csproj +++ b/src/generators/Silk.NET.SilkTouch.DotnetTool/Silk.NET.SilkTouch.DotnetTool.csproj @@ -3,10 +3,8 @@ Exe net6.0 - true silktouch DotnetTool - true $(NETCoreSdkRuntimeIdentifier) diff --git a/src/generators/Silk.NET.SilkTouch.Scraper/XmlVisitor.cs b/src/generators/Silk.NET.SilkTouch.Scraper/XmlVisitor.cs index 809fbebb04..98d292e2c3 100644 --- a/src/generators/Silk.NET.SilkTouch.Scraper/XmlVisitor.cs +++ b/src/generators/Silk.NET.SilkTouch.Scraper/XmlVisitor.cs @@ -27,7 +27,7 @@ public XmlVisitor(ILogger logger, TypeStore typeStore) public IEnumerable Visit(XmlNode node) { - _logger.LogTrace("Visiting XML Node of kind {name}", node.Name); + _logger.LogTrace("Visiting XML Node of kind {name} {inner}", node.Name, node.InnerXml); switch (node) { case XmlElement { Name: "bindings" } bindings: diff --git a/src/generators/Silk.NET.SilkTouch.TypeResolution/FunctionPointerTypeResolver.cs b/src/generators/Silk.NET.SilkTouch.TypeResolution/FunctionPointerTypeResolver.cs index 2784a0fbdf..1a7f4ef33b 100644 --- a/src/generators/Silk.NET.SilkTouch.TypeResolution/FunctionPointerTypeResolver.cs +++ b/src/generators/Silk.NET.SilkTouch.TypeResolution/FunctionPointerTypeResolver.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Immutable; +using System.Diagnostics; using System.Text.RegularExpressions; +using Microsoft.Extensions.Logging; using Silk.NET.SilkTouch.Symbols; namespace Silk.NET.SilkTouch.TypeResolution; @@ -12,34 +14,80 @@ namespace Silk.NET.SilkTouch.TypeResolution; /// public class FunctionPointerTypeResolver : SimpleTypeResolverBase { - private static readonly Regex _regex = new - (/*lang=regex*/ - @"delegate\*\sunmanaged(\[((?'modifier'.(?=,\s?)?)+)*\])?\<((?'parameter'(.(?=,\s?)+))(,\s?))*(?'return_type'(.)+)\>", - RegexOptions.CultureInvariant - ); + private readonly ILogger _logger; /// - public FunctionPointerTypeResolver(TypeStore typeStore) : base(typeStore) + public FunctionPointerTypeResolver(ILogger logger, TypeStore typeStore) : base(typeStore) { + _logger = logger; } /// protected override bool TryResolve(UnresolvedTypeReference utr, out TypeReference? resolved) { - if (utr.Text.StartsWith("delegate*")) + int c = 0; + var text = utr.Text; + if (text.Length > "delegate*".Length && text[text.Length - 1] == '>' && text.Substring(c, "delegate*".Length) == "delegate*") { - var match = _regex.Match(utr.Text); - if (match.Success) + _logger.LogTrace("Attempting to resolve {text} to function pointer after passing preliminary tests", utr.Text); + c += "delegate*".Length; + if (text.Substring(c, " unmanaged".Length) == " unmanaged") { - var parameters = match.Groups["parameter"] - .Captures.OfType() - .Select(x => new UnresolvedTypeReference(x.Value)) - .Cast() - .ToImmutableArray(); - var returnType = new UnresolvedTypeReference(match.Groups["return_type"].Value); + c += " unmanaged".Length; + if (text[c] == '[') + { + var endOffset = text.AsSpan(c).IndexOf(']'); + + var attributeText = text.Substring(c + 1, endOffset - 1); + _logger.LogDebug("{text} may be a function pointer and has unhandled attributes {attributes}", utr.Text, attributeText); + // TODO: parse out function attributes here + + c += endOffset + 1; + } + var types = new List(); + if (text[c] == '<') + { + c += 1; + while (true) + { + var typeTextEndIndex = text.AsSpan(c).IndexOf(','); + if (typeTextEndIndex != -1) + { + var typeText = text.Substring(c, typeTextEndIndex); + c += typeTextEndIndex + 1; + if (text[c] == ' ') c++; + types.Add(new UnresolvedTypeReference(typeText)); + } + else + { + var l = text.Length - c - 1; + if (l > 0) + { + var typeText = text.Substring(c, l); + types.Add(new UnresolvedTypeReference(typeText)); + } + break; + } + } + } + else + { + _logger.LogDebug("{text} may be a function pointer but generic params are somehow scrambled", utr.Text); + } - resolved = new FunctionPointerTypeReference(returnType, parameters); - return true; + + // at least a return type is required + if (types.Count > 0) + { + resolved = new FunctionPointerTypeReference + (types.Last(), types.Take(types.Count - 1).ToImmutableArray()); + _logger.LogTrace("{text} resolved to function pointer {ptr}", utr.Text, resolved); + return true; + } + } + else + { + _logger.LogDebug("Rejecting {text} as it may be a function pointer, but not unmanaged", utr.Text); } } resolved = null; diff --git a/tests/Silk.NET.SilkTouch.TypeResolution.Tests/FunctionPointerTypeResolverTests.cs b/tests/Silk.NET.SilkTouch.TypeResolution.Tests/FunctionPointerTypeResolverTests.cs index 344bbd0706..7abfe54a4f 100644 --- a/tests/Silk.NET.SilkTouch.TypeResolution.Tests/FunctionPointerTypeResolverTests.cs +++ b/tests/Silk.NET.SilkTouch.TypeResolution.Tests/FunctionPointerTypeResolverTests.cs @@ -3,6 +3,8 @@ using System; using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Silk.NET.SilkTouch.Symbols; using Xunit; @@ -17,11 +19,16 @@ public class FunctionPointerTypeResolverTests InlineData("delegate* unmanaged[Cdecl]", "C", new[] { "A", "B"}), InlineData("delegate* unmanaged", "C", new[] { "A", "B"}), InlineData("delegate* unmanaged[Cdecl]", "C", new[] { "A", "B"}), + InlineData("delegate* unmanaged[Cdecl, SupressGCTransition]", "A", new string[0]), + InlineData("delegate* unmanaged[Cdecl, SupressGCTransition]", "C", new[] { "A", "B"}), ] public void ShouldMatch(string text, string returnString, string[] parameters) { - var result = new FunctionPointerTypeResolver(new TypeStore()).Visit(new UnresolvedTypeReference(text)); - + var serviceProvider = Helpers.CreateServiceProvider(); + var result = new FunctionPointerTypeResolver + (serviceProvider.GetRequiredService>(), new TypeStore()).Visit + (new UnresolvedTypeReference(text)); + var fptr = Assert.IsType(result); Assert.Equal(returnString, Assert.IsType(fptr.ReturnType).Text); Assert.Collection @@ -44,11 +51,15 @@ public void ShouldMatch(string text, string returnString, string[] parameters) InlineData("longType"), InlineData("int"), InlineData("using"), - InlineData("delegate*") + InlineData("delegate*"), + InlineData("delegate* unmanaged<>"), ] public void ShouldNotMatch(string text) { - var result = new FunctionPointerTypeResolver(new TypeStore()).Visit(new UnresolvedTypeReference(text)); + var serviceProvider = Helpers.CreateServiceProvider(); + var result = new FunctionPointerTypeResolver + (serviceProvider.GetRequiredService>(), new TypeStore()).Visit + (new UnresolvedTypeReference(text)); Assert.IsNotType(result); }