Skip to content

Commit

Permalink
Add support for cdecl and thiscall calling conventions (#1201)
Browse files Browse the repository at this point in the history
* Fix cdecl calling convetions

* Add support for ThisCall calling convention on interface methods
  • Loading branch information
sotteson1 authored Aug 31, 2022
1 parent eb0f857 commit 2449377
Show file tree
Hide file tree
Showing 6 changed files with 2,003 additions and 14 deletions.
1,925 changes: 1,925 additions & 0 deletions scripts/ChangesSinceLastRelease.txt

Large diffs are not rendered by default.

61 changes: 54 additions & 7 deletions sources/ClangSharpSourceToWinmd/ClangSharpSourceWinmdGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using System.Reflection.Metadata.Ecma335;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
Expand Down Expand Up @@ -1037,7 +1038,8 @@ private MethodDefinitionHandle AddMethodViaParams(
IEnumerable<Parameter> parameters,
MethodAttributes methodAttrs,
MethodImplAttributes methodImplAttributes,
bool instanceMethod)
bool instanceMethod,
bool thisCall)
{
methodName = FixArchSpecificName(methodName);

Expand Down Expand Up @@ -1066,6 +1068,11 @@ private MethodDefinitionHandle AddMethodViaParams(
parameters = parameters.Take(parameters.Count() - 1);
}

if (thisCall)
{
signatureCallingConvention = SignatureCallingConvention.ThisCall;
}

var methodSignature = new BlobBuilder();
new BlobEncoder(methodSignature)
.MethodSignature(
Expand Down Expand Up @@ -1113,7 +1120,8 @@ private MethodDefinitionHandle AddMethodViaSymbol(
IMethodSymbol methodSymbol,
MethodAttributes methodAttrs,
MethodImplAttributes methodImplAttrs,
bool instanceMethod)
bool instanceMethod,
bool thisCall)
{
var returnType = methodSymbol.ReturnType;
this.RemapToMoreSpecificTypeIfPossible(methodSymbol.Name, "return", methodSymbol.GetReturnTypeAttributes(), ref returnType);
Expand All @@ -1124,7 +1132,7 @@ private MethodDefinitionHandle AddMethodViaSymbol(
parameters.Add(new Parameter(this, methodSymbol, p));
}

return this.AddMethodViaParams(methodSymbol, methodSymbol.Name, returnType, parameters, methodAttrs, methodImplAttrs, instanceMethod);
return this.AddMethodViaParams(methodSymbol, methodSymbol.Name, returnType, parameters, methodAttrs, methodImplAttrs, instanceMethod, thisCall);
}

private INamedTypeSymbol GetTypeByMetadataName(string typeName)
Expand Down Expand Up @@ -1245,6 +1253,7 @@ private MethodDefinitionHandle WriteClassMethods(ClassDeclarationSyntax node)
symbol,
MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig | MethodAttributes.PinvokeImpl,
methodImplAttributes,
false,
false);
if (firstMethod.IsNil)
{
Expand All @@ -1253,7 +1262,8 @@ private MethodDefinitionHandle WriteClassMethods(ClassDeclarationSyntax node)

this.namesToMethodDefHandles[fullName] = methodDef;

MethodImportAttributes methodImportAttributes = MethodImportAttributes.CallingConventionWinApi;
MethodImportAttributes importCallingConvention = MethodImportAttributes.CallingConventionWinApi;
MethodImportAttributes methodImportAttributes = MethodImportAttributes.None;
var dllImportAttr = symbol.GetAttributes().First(a => a.AttributeClass.Name == "DllImportAttribute");
string moduleName = (string)dllImportAttr.ConstructorArguments[0].Value;
StringHandle importedMethodName = this.metadataBuilder.GetOrAddString(methodName);
Expand All @@ -1271,8 +1281,28 @@ private MethodDefinitionHandle WriteClassMethods(ClassDeclarationSyntax node)
{
importedMethodName = this.metadataBuilder.GetOrAddString((string)arg.Value.Value);
}
else if (arg.Key == "CallingConvention")
{
var callingConvention = (CallingConvention)arg.Value.Value;

switch (callingConvention)
{
case CallingConvention.Cdecl:
importCallingConvention = MethodImportAttributes.CallingConventionCDecl;
break;

case CallingConvention.StdCall:
importCallingConvention = MethodImportAttributes.CallingConventionWinApi;
break;

default:
break;
}
}
}

methodImportAttributes |= importCallingConvention;

this.AddCustomAttributes(method, methodDef);

this.metadataBuilder.AddMethodImport(
Expand Down Expand Up @@ -1367,7 +1397,8 @@ private MethodDefinitionHandle WriteDelegateMethods(DelegateDeclarationSyntax no
},
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
MethodImplAttributes.Managed | MethodImplAttributes.Runtime,
true);
true,
false);
firstMethod = ctorMethodDef;

var invokeMethodSymbol = symbol.GetMembers().First(m => m.Name == "Invoke");
Expand All @@ -1376,7 +1407,8 @@ private MethodDefinitionHandle WriteDelegateMethods(DelegateDeclarationSyntax no
(IMethodSymbol)invokeMethodSymbol,
MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.HideBySig | MethodAttributes.Virtual,
MethodImplAttributes.Managed | MethodImplAttributes.Runtime,
true);
true,
false);

return firstMethod;
}
Expand Down Expand Up @@ -1889,8 +1921,22 @@ private MethodDefinitionHandle WriteInterfaceMethods(StructDeclarationSyntax nod
{
var methodSymbol = model.GetDeclaredSymbol(method);
var methodName = methodSymbol.Name;
bool thisCall = false;
MethodAttributes methodAttributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.NewSlot | MethodAttributes.Abstract | MethodAttributes.Virtual;

var delegateDef =
node.Members.FirstOrDefault(
m => m is DelegateDeclarationSyntax &&
((DelegateDeclarationSyntax)m).Identifier.Text == $"_{methodName}");
if (delegateDef != null)
{
var attr = SyntaxUtils.GetAttribute(delegateDef.AttributeLists, "UnmanagedFunctionPointer");
if (attr != null && attr.ToString().Contains("ThisCall"))
{
thisCall = true;
}
}

MethodImplAttributes methodImplAttributes = MethodImplAttributes.Managed;

if (methodSymbol.GetAttributes().Any(a => a.AttributeClass.Name == "PreserveSigAttribute"))
Expand All @@ -1903,7 +1949,8 @@ private MethodDefinitionHandle WriteInterfaceMethods(StructDeclarationSyntax nod
methodSymbol,
methodAttributes,
methodImplAttributes,
true);
true,
thisCall);

if (firstMethod.IsNil)
{
Expand Down
7 changes: 4 additions & 3 deletions sources/ClangSharpSourceToWinmd/MetadataSyntaxTreeCleaner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -390,10 +390,11 @@ public override SyntaxNode VisitAttributeList(AttributeListSyntax node)

case "UnmanagedFunctionPointer":
{
// ClangSharp is emitting this attribute with no arguments.
// ClangSharp can emit this attribute with no arguments.
// The typedef we're using of this attribute has no such ctor,
// so emit one that does, using WinApi as the default calling convention
if (firstAttr.ArgumentList == null)
// so emit one that does, using WinApi as the default calling convention.
// Also, convert StdCall to Winapi
if (firstAttr.ArgumentList == null || firstAttr.ArgumentList.ToString() == "(CallingConvention.StdCall)")
{
return
SyntaxFactory.AttributeList(
Expand Down
2 changes: 0 additions & 2 deletions sources/GeneratorSdk/tools/assets/scraper/baseSettings.rsp
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,3 @@ dont-use-using-statics-for-enums
exclude-anonymous-field-helpers
--methodClassName
Apis
--with-callconv
*=Winapi
18 changes: 18 additions & 0 deletions sources/WinmdUtils/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ namespace WinmdUtilsProgram
{
class Program
{
private static readonly GenericSignatureTypeProvider SignatureProvider = new GenericSignatureTypeProvider();

static int Main(string[] args)
{
var showMissingImportsCommand = new Command("showMissingImports", "Show missing imports between two winmd files.")
Expand Down Expand Up @@ -1263,6 +1265,14 @@ private static bool CompareAttributes(string fullName, IEnumerable<IAttribute> a
return writer.DifferencesCount == before;
}

private static SignatureCallingConvention GetCallingConvention(IMethod method)
{
var metadata = method.ParentModule.PEFile.Metadata;
var methodDef = metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken);
var methodSignature = methodDef.DecodeSignature(SignatureProvider, null);
return methodSignature.Header.CallingConvention;
}

private static bool CompareMethods(IMethod method1, IMethod method2, DifferencesWriter writer)
{
int before = writer.DifferencesCount;
Expand All @@ -1271,6 +1281,14 @@ private static bool CompareMethods(IMethod method1, IMethod method2, Differences

CompareAttributes(methodFullName, method1.GetAttributes(), method2.GetAttributes(), writer);

var method1CallConv = GetCallingConvention(method1);
var method2CallConv = GetCallingConvention(method2);

if (method1CallConv != method2CallConv)
{
writer.WriteDifference($"{methodFullName} : calling convention...{method1CallConv} => {method2CallConv}");
}

// Return param
string returnFullName = $"{methodFullName} : return";
CompareAttributes(returnFullName, method1.GetReturnTypeAttributes(), method2.GetReturnTypeAttributes(), writer);
Expand Down
4 changes: 2 additions & 2 deletions sources/WinmdUtils/Properties/launchSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
},
"compare": {
"commandName": "Project",
"commandLineArgs": "compare --first $(USERPROFILE)\\.nuget\\packages\\microsoft.windows.sdk.win32metadata\\28.0.1-preview\\Windows.Win32.winmd --second $(ProjectDir)..\\..\\bin\\Windows.Win32.winmd --knownDiffsFile $(ProjectDir)..\\..\\scripts\\ChangesSinceLastRelease.txt"
"commandLineArgs": "compare --first $(USERPROFILE)\\.nuget\\packages\\microsoft.windows.sdk.win32metadata\\29.0.6-preview\\Windows.Win32.winmd --second $(ProjectDir)..\\..\\bin\\Windows.Win32.winmd --knownDiffsFile $(ProjectDir)..\\..\\scripts\\ChangesSinceLastRelease.txt"
},
"compareWithUpdate": {
"commandName": "Project",
"commandLineArgs": "compare --first $(USERPROFILE)\\.nuget\\packages\\microsoft.windows.sdk.win32metadata\\28.0.1-preview\\Windows.Win32.winmd --second $(ProjectDir)..\\..\\bin\\Windows.Win32.winmd --knownDiffsFile $(ProjectDir)..\\..\\scripts\\ChangesSinceLastRelease.txt --updateKnownDiffsComment \"Fix me!\""
"commandLineArgs": "compare --first $(USERPROFILE)\\.nuget\\packages\\microsoft.windows.sdk.win32metadata\\29.0.6-preview\\Windows.Win32.winmd --second $(ProjectDir)..\\..\\bin\\Windows.Win32.winmd --knownDiffsFile $(ProjectDir)..\\..\\scripts\\ChangesSinceLastRelease.txt --updateKnownDiffsComment \"Fix me!\""
},
"showDuplicateImports": {
"commandName": "Project",
Expand Down

0 comments on commit 2449377

Please sign in to comment.