diff --git a/src/Assimp/Silk.NET.Assimp/Assimp.cs b/src/Assimp/Silk.NET.Assimp/Assimp.cs index 1a3163b0b2..da9673ac3c 100644 --- a/src/Assimp/Silk.NET.Assimp/Assimp.cs +++ b/src/Assimp/Silk.NET.Assimp/Assimp.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; using Silk.NET.Core.Contexts; using Silk.NET.Core.Loader; using Silk.NET.Core.Native; @@ -7,6 +8,7 @@ namespace Silk.NET.Assimp { + [NativeApi(Convention = CallingConvention.Winapi)] public partial class Assimp { public static Assimp GetApi() diff --git a/src/Core/Silk.NET.Core/Native/SilkMarshal.cs b/src/Core/Silk.NET.Core/Native/SilkMarshal.cs index ef5c7a6007..c84d5d49ee 100644 --- a/src/Core/Silk.NET.Core/Native/SilkMarshal.cs +++ b/src/Core/Silk.NET.Core/Native/SilkMarshal.cs @@ -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 { @@ -19,6 +17,25 @@ namespace Silk.NET.Core.Native /// public static class SilkMarshal { + /// + /// Gets a value indicating whether is equivalent to + /// . + /// + /// + /// If false, can be generally assumed to be equivalent to + /// . + /// + public static readonly bool IsWinapiStdcall; + + static SilkMarshal() + { +#if NET5_0 + IsWinapiStdcall = OperatingSystem.IsWindows(); +#else + IsWinapiStdcall = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); +#endif + } + /// /// Allocate a new BStr pointer. /// diff --git a/src/Core/Silk.NET.SilkTouch/MarshalContext.cs b/src/Core/Silk.NET.SilkTouch/MarshalContext.cs index b5798a40c8..e2b0017624 100644 --- a/src/Core/Silk.NET.SilkTouch/MarshalContext.cs +++ b/src/Core/Silk.NET.SilkTouch/MarshalContext.cs @@ -423,6 +423,7 @@ public void DeclareExtraRef(int id, int amount = 1) public BlockSyntax BuildFinalBlock() { + // add return if (!ReturnsVoid) { diff --git a/src/Core/Silk.NET.SilkTouch/NativeApiGenerator.cs b/src/Core/Silk.NET.SilkTouch/NativeApiGenerator.cs index 379de67bc3..8e6b7ec3df 100644 --- a/src/Core/Silk.NET.SilkTouch/NativeApiGenerator.cs +++ b/src/Core/Silk.NET.SilkTouch/NativeApiGenerator.cs @@ -392,40 +392,66 @@ private static void ProcessMethod SyntaxList 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 @@ -445,34 +471,45 @@ void BuildLoadInvoke(ref IMarshalContext ctx, Action next) Func 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) { @@ -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 @@ -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(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)