Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion src/Core/Silk.NET.Core/Native/NativeApiAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public static string GetEntryPoint(NativeApiAttribute attr, NativeApiAttribute p

public static CallingConvention GetCallingConvention(NativeApiAttribute attr, NativeApiAttribute parent)
{
return attr?._actualConvention ?? parent?._actualConvention ?? CallingConvention.Cdecl;
return attr?._actualConvention ?? parent?._actualConvention ?? CallingConvention.Winapi;
}
}
}
21 changes: 19 additions & 2 deletions src/Core/Silk.NET.Core/Native/SilkMarshal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Silk.NET.Core.Loader;

namespace Silk.NET.Core.Native
{
Expand All @@ -19,6 +17,25 @@ namespace Silk.NET.Core.Native
/// </summary>
public static class SilkMarshal
{
/// <summary>
/// Gets a value indicating whether <see cref="CallingConvention.Winapi"/> is equivalent to
/// <see cref="CallingConvention.StdCall"/>.
/// </summary>
/// <remarks>
/// If false, <see cref="CallingConvention.Winapi"/> can be generally assumed to be equivalent to
/// <see cref="CallingConvention.Cdecl"/>.
/// </remarks>
public static readonly bool IsWinapiStdcall;

static SilkMarshal()
{
#if NET5_0
IsWinapiStdcall = OperatingSystem.IsWindows();
#else
IsWinapiStdcall = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
#endif
}

/// <summary>
/// Allocate a new BStr pointer.
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions src/Core/Silk.NET.SilkTouch/MarshalContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ public void DeclareExtraRef(int id, int amount = 1)

public BlockSyntax BuildFinalBlock()
{

// add return
if (!ReturnsVoid)
{
Expand Down
227 changes: 183 additions & 44 deletions src/Core/Silk.NET.SilkTouch/NativeApiGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -392,40 +392,66 @@ private static void ProcessMethod
SyntaxList<UsingDirectiveSyntax> generationusings
)
{
void BuildLoadInvoke(ref IMarshalContext ctx, Action next)
{
ctx.TransitionTo(SilkTouchStage.PreLoad);

// this is terminal, we never call next

var parameters = ctx.ResolveAllLoadParameters();

var fPtrType = FunctionPointerType
const string invocationShimName = "StCall";
static FunctionPointerTypeSyntax GetFuncPtrType
(
CallingConvention callingConvention,
ITypeSymbol[] loadTypes
) => FunctionPointerType
(
callingConvention == CallingConvention.Winapi ? FunctionPointerCallingConvention
(
Token(SyntaxKind.UnmanagedKeyword)
) : FunctionPointerCallingConvention
(
FunctionPointerCallingConvention
Token(SyntaxKind.UnmanagedKeyword),
FunctionPointerUnmanagedCallingConventionList
(
Token(SyntaxKind.UnmanagedKeyword),
FunctionPointerUnmanagedCallingConventionList
SingletonSeparatedList
(
SingletonSeparatedList
(
FunctionPointerUnmanagedCallingConvention
(Identifier(GetCallingConvention(callingConvention)))
)
FunctionPointerUnmanagedCallingConvention
(Identifier(GetCallingConvention(callingConvention)))
)
),
FunctionPointerParameterList
)
),
FunctionPointerParameterList
(
SeparatedList
(
SeparatedList
loadTypes.Select
(
ctx.LoadTypes.Select
(
x => FunctionPointerParameter
(IdentifierName(x.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)))
)
x => FunctionPointerParameter
(IdentifierName(x.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)))
)
)
);
)
);

static MemberAccessExpressionSyntax GetFuncPtrExpr
(
string generatedVTableName,
string entryPoint
) => MemberAccessExpression
(
SyntaxKind.SimpleMemberAccessExpression,
ParenthesizedExpression
(
BinaryExpression
(
SyntaxKind.AsExpression, IdentifierName("CurrentVTable"),
IdentifierName(generatedVTableName)
)
), IdentifierName(FirstLetterToUpper(entryPoint))
);

void BuildLoadInvoke(ref IMarshalContext ctx, Action next)
{
ctx.TransitionTo(SilkTouchStage.PreLoad);

// this is terminal, we never call next

var parameters = ctx.ResolveAllLoadParameters();


entryPoints.Add(entryPoint);
processedEntrypoints.Add
Expand All @@ -445,34 +471,45 @@ void BuildLoadInvoke(ref IMarshalContext ctx, Action next)

Func<IMarshalContext, ExpressionSyntax> expression;

var defs = declaration.SyntaxTree.Options.PreprocessorSymbolNames;

// ReSharper disable PossibleMultipleEnumeration - just not an issue
var hasFastWinapi = defs.Contains("NET5_0") ||
defs.Contains("NET6_0") ||
defs.Contains("NET5_0_OR_GREATER"); // newer SDKs (circa .NET 6) have _OR_GREATER
// ReSharper restore PossibleMultipleEnumeration

var needsInvocationShim = callingConvention == CallingConvention.Winapi && !hasFastWinapi;

if ((classIsSealed || generateSeal) && generateVTable)
{
// build load + invocation
expression = ctx => InvocationExpression
(
ParenthesizedExpression
expression = ctx =>
{
var fPtrType = GetFuncPtrType(callingConvention, ctx.LoadTypes);
return InvocationExpression
(
CastExpression
needsInvocationShim ? IdentifierName(invocationShimName) : ParenthesizedExpression
(
fPtrType, MemberAccessExpression
CastExpression
(
SyntaxKind.SimpleMemberAccessExpression,
ParenthesizedExpression
(
BinaryExpression
(
SyntaxKind.AsExpression, IdentifierName("CurrentVTable"), IdentifierName(generatedVTableName)
)
), IdentifierName(FirstLetterToUpper(entryPoint))
fPtrType,
GetFuncPtrExpr(generatedVTableName, entryPoint)
)
)
), ArgumentList(SeparatedList(parameters.Select(x => Argument(x.Value))))
);
), ArgumentList(SeparatedList(parameters.Select(x => Argument(x.Value))))
);
};
}
else
{
throw new Exception("FORCE-USE-VTABLE");
}


if (needsInvocationShim)
{
ctx.AddSideEffect(ManualWinapiInvokeShim);
}

if (ctx.ReturnsVoid)
{
Expand Down Expand Up @@ -512,8 +549,7 @@ void BuildLoadInvoke(ref IMarshalContext ctx, Action next)
block = Block(UnsafeStatement(Token(SyntaxKind.UnsafeKeyword), block));
}

var method = declaration.WithBody
(block)
var method = declaration.WithBody(block)
.WithAttributeLists(default)
.WithSemicolonToken(default)
.WithParameterList
Expand Down Expand Up @@ -572,6 +608,109 @@ void BuildLoadInvoke(ref IMarshalContext ctx, Action next)
sourceContext.ReportDiagnostic
(Diagnostic.Create(Diagnostics.MethodClassFailure, declaration.GetLocation(), ex.ToString()));
}

InvocationExpressionSyntax ManualWinapiInvokeInnerExpr
(
IMarshalContext ctx,
CallingConvention callingConvention
) => InvocationExpression
(
ParenthesizedExpression
(
CastExpression
(
GetFuncPtrType(callingConvention, ctx.LoadTypes),
GetFuncPtrExpr(generatedVTableName, entryPoint)
)
),
ArgumentList
(
SeparatedList
(
Enumerable.Range
(0, ctx.LoadTypes.Length - 1)
.Select(x => Argument(IdentifierName($"arg{x}")))
)
)
);

StatementSyntax ManualWinapiInvokeShim(IMarshalContext ctx)
{
var stdCallStmt = ManualWinapiInvokeInnerExpr(ctx, CallingConvention.StdCall);
var cdeclStmt = ManualWinapiInvokeInnerExpr(ctx, CallingConvention.Cdecl);
return LocalFunctionStatement
(
IdentifierName(ctx.ReturnLoadType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)),
invocationShimName
)
.WithParameterList
(
ParameterList
(
SeparatedList
(
new ArraySegment<ITypeSymbol>(ctx.LoadTypes, 0, ctx.LoadTypes.Length - 1).Select
(
(x, i) => Parameter(Identifier($"arg{i}"))
.WithType
(
IdentifierName(x.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
)
)
)
)
)
.WithBody
(
Block
(
IfStatement
(
MemberAccessExpression
(
SyntaxKind.SimpleMemberAccessExpression,
MemberAccessExpression
(
SyntaxKind.SimpleMemberAccessExpression,
MemberAccessExpression
(
SyntaxKind.SimpleMemberAccessExpression,
MemberAccessExpression
(
SyntaxKind.SimpleMemberAccessExpression,
MemberAccessExpression
(
SyntaxKind.SimpleMemberAccessExpression, IdentifierName("Silk"),
IdentifierName("NET")
), IdentifierName("Core")
), IdentifierName("Native")
), IdentifierName("SilkMarshal")
), IdentifierName("IsWinapiStdcall")
),
Block
(
ctx.ReturnsVoid
? ExpressionStatement(stdCallStmt)
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
: ReturnStatement(stdCallStmt)
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
),
ElseClause
(
Block
(
ctx.ReturnsVoid
? ExpressionStatement(cdeclStmt)
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
: ReturnStatement(cdeclStmt)
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken))
)
)
)
)
)
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
}
}

private static string GetCallingConvention(CallingConvention convention)
Expand Down