Skip to content

Class method scraping #1034

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 @@ -294,7 +294,7 @@ protected override StaticExternalMethodSymbol VisitStaticExternalMethod(StaticEx
throw new InvalidOperationException("Syntax Token was not correctly visited");
ClearState();

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

Expand Down
93 changes: 74 additions & 19 deletions src/generators/Silk.NET.SilkTouch.Scraper/XmlVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Xml;
using Microsoft.Extensions.Logging;
using Silk.NET.SilkTouch.Symbols;
using Parameter=Silk.NET.SilkTouch.Symbols.Parameter;
using TypeReference=Silk.NET.SilkTouch.Symbols.TypeReference;

namespace Silk.NET.SilkTouch.Scraper;
Expand Down Expand Up @@ -37,6 +38,10 @@ public IEnumerable<Symbol> Visit(XmlNode node)
return VisitStruct(@struct);
case XmlElement { Name: "field" } field:
return VisitField(field);
case XmlElement { Name: "class" } @class:
return VisitClass(@class);
case XmlElement { Name: "function" } function:
return VisitFunction(function);
default:
{
_logger.LogWarning("Skipping unknown XML Node of kind {name}", node.Name);
Expand All @@ -49,6 +54,75 @@ public IEnumerable<Symbol> Visit(XmlNode node)
}
}

private IEnumerable<Symbol> VisitClass(XmlElement @class)
{
var name = @class.Attributes["name"]?.Value;
if (name is null)
throw new InvalidOperationException("Class name cannot be null");

var members = @class.ChildNodes.Cast<XmlNode>()
.SelectMany
(
x =>
{
var results = Visit(x).ToArray();
if (results.Any(x => x is not MethodSymbol))
throw new NotImplementedException("Class only supports method members for now");
return results.OfType<MethodSymbol>();
}
);
return new[]
{
new ClassSymbol(TypeId.CreateNew(), new IdentifierSymbol(name), members.ToImmutableArray())
};
}

private IEnumerable<Symbol> VisitFunction(XmlElement function)
{
var name = function.Attributes["name"]?.Value;
if (name is null)
throw new InvalidOperationException("Function name cannot be null");

var returnTypeNode = function.ChildNodes.Cast<XmlNode>().OfType<XmlElement>().FirstOrDefault(x => x.Name == "type");
if (returnTypeNode is null)
throw new InvalidOperationException("Could not find return type of function");

var returnType = VisitType(returnTypeNode).Single();
if (returnType is not TypeReference rt)
throw new InvalidOperationException("VisitType needs to return single type reference");

var parameters = function.ChildNodes.Cast<XmlNode>()
.OfType<XmlElement>()
.Where(x => x.Name == "param")
.Select
(
x =>
{
var paramName = x.Attributes["name"]?.Value;
if (paramName is null)
throw new InvalidOperationException("Function parameter name cannot be null");

var paramTypeNode = x.ChildNodes.Cast<XmlNode>()
.OfType<XmlElement>()
.SingleOrDefault(x => x.Name == "type");
if (paramTypeNode is null)
throw new InvalidOperationException("Parameter type cannot be null");

var paramType = VisitType(paramTypeNode).Single();
if (paramType is not TypeReference pt)
throw new InvalidOperationException("VisitType needs to return single type reference");

return new Parameter(pt, new IdentifierSymbol(paramName));
}
)
.ToImmutableArray();

return new[]
{
new StaticExternalMethodSymbol(rt, parameters, new IdentifierSymbol(name))
};
}

private IEnumerable<Symbol> VisitField(XmlElement field)
{
var name = field.Attributes["name"]?.Value;
Expand Down Expand Up @@ -100,25 +174,6 @@ private IEnumerable<Symbol> VisitField(XmlElement field)
new FieldSymbol(finalType, new IdentifierSymbol(name))
};
}

// TODO: Configurable Type maps
private static readonly Dictionary<string, TypeReference> _typeMap = new()
{
["int"] = new ExternalTypeReference(null, new IdentifierSymbol("int"))
};

private bool TryResolveTypeRef(string text, [NotNullWhen(true)] out TypeReference? reference)
{
if (_typeMap.TryGetValue(text, out reference))
{
return true;
}
else
{
_logger.LogDebug("Failed to resolve type reference from \"{text}\"", text);
return false;
}
}

// NOTE: This does not visit types as in class/struct, but visits *references* to types. Like from methods or fields.
private IEnumerable<Symbol> VisitType(XmlElement type)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@


/* -------------------------- NEW OUTPUT -------------------------- */

namespace LIBRARY_NAMESPACE
{
public class Methods
{
public static extern System.Int32 test(System.Int32* pointer);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@


/* -------------------------- NEW OUTPUT -------------------------- */

namespace LIBRARY_NAMESPACE
{
public class Methods
{
public static extern System.Int32 test(System.Int32* pointer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,32 @@ struct x *p;
};");
return Verifier.Verify(result);
}

[Fact(Skip = "__declspec is windows only"),
Trait("Category", "Integration"),
Trait("Source Language", "C++"),
Trait("Target Language", "C#"),
Trait("Feature", "Functions")]
public Task Test9()
{
var result = TestHelper.GetCSharpOutputFromCpp(@"
#define GLFWAPI __declspec(dllexport)
GLFWAPI int test(int* pointer);
");
return Verifier.Verify(result);
}

[Fact,
Trait("Category", "Integration"),
Trait("Source Language", "C++"),
Trait("Target Language", "C#"),
Trait("Feature", "Functions")]
public Task Test10()
{
var result = TestHelper.GetCSharpOutputFromCpp(@"
#define GLFWAPI __attribute__((visibility(""default"")))
GLFWAPI int test(int* pointer);
");
return Verifier.Verify(result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class SilkTouchTestFramework : XunitTestFramework
["Nested Types"] = true,
["Structs"] = true,
["Unions"] = false,
["Functions"] = true
};

public SilkTouchTestFramework(IMessageSink messageSink)
Expand Down