diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/ValueDesc.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/ValueDesc.cs index 087db164..6eb9fb8a 100644 --- a/sources/ClangSharp.PInvokeGenerator/Abstractions/ValueDesc.cs +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/ValueDesc.cs @@ -21,6 +21,7 @@ internal struct ValueDesc public readonly bool IsArray => (Flags & ValueFlags.Array) != 0; public readonly bool IsConstant => (Flags & ValueFlags.Constant) != 0; public readonly bool IsCopy => (Flags & ValueFlags.Copy) != 0; + public readonly bool IsReference => (Flags & ValueFlags.Reference) != 0; public Action WriteCustomAttrs { get; set; } public object CustomAttrGeneratorData { get; set; } } diff --git a/sources/ClangSharp.PInvokeGenerator/Abstractions/ValueModifiers.cs b/sources/ClangSharp.PInvokeGenerator/Abstractions/ValueModifiers.cs index a0baec37..56de2b34 100644 --- a/sources/ClangSharp.PInvokeGenerator/Abstractions/ValueModifiers.cs +++ b/sources/ClangSharp.PInvokeGenerator/Abstractions/ValueModifiers.cs @@ -12,4 +12,5 @@ internal enum ValueFlags Constant = 1 << 1, Copy = 1 << 2, Array = 1 << 3, + Reference = 1 << 4, } diff --git a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs index 00afd06a..81a9ce2e 100644 --- a/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/CSharp/CSharpOutputBuilder.VisitDecl.cs @@ -112,7 +112,7 @@ public void BeginValue(in ValueDesc desc) Write(GetAccessSpecifierString(desc.AccessSpecifier, isNested: true)); Write(" static "); - if (_generator.Config.GenerateUnmanagedConstants && desc.IsConstant && !desc.IsCopy) + if (_generator.Config.GenerateUnmanagedConstants && desc.IsConstant && (!desc.IsCopy || desc.IsReference)) { if (desc.IsArray) { diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs index 7bf0d6cf..82ad5b63 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.Naming.cs @@ -502,7 +502,13 @@ private CXXRecordDecl GetRecordDecl(CXXBaseSpecifier cxxBaseSpecifier) if (IsType(cxxBaseSpecifier, baseType, out var recordType)) { - return (CXXRecordDecl)recordType.Decl; + var recordDecl = (CXXRecordDecl)recordType.Decl; + + // The referenced decl can be a redeclaration (e.g. a MIDL forward `typedef interface`) + // rather than the definition. A non-definition carries the base list but no members, so + // resolving to the definition is required to flatten every inherited method into the + // derived vtable. + return (CXXRecordDecl?)recordDecl.Definition ?? recordDecl; } // A dependent base (such as `Base` used within a class template) has no resolved diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.TypeResolution.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.TypeResolution.cs index 0a1a6f6e..472d7291 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.TypeResolution.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.TypeResolution.cs @@ -441,7 +441,10 @@ private string GetTypeName(Cursor? cursor, Cursor? context, Type rootType, Type { case CXTemplateArgumentKind_Type: { - typeName = GetRemappedTypeName(cursor, context: null, arg.AsType, out var nativeAsTypeName, skipUsing: true, isTemplate: true); + // The argument name is emitted as part of the generic type (e.g. + // `IReference`), so its namespace using must be emitted too; + // the enclosing type name is not itself resolved through `--with-namespace`. + typeName = GetRemappedTypeName(cursor, context: null, arg.AsType, out var nativeAsTypeName, skipUsing: false, isTemplate: true); break; } diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs index 8e8a6633..4d5502c1 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs @@ -1360,7 +1360,7 @@ void ForFunctionDecl(ParmVarDecl parmVarDecl, FunctionDecl functionDecl) if (defaultArg is not null) { csharpOutputBuilder.WriteCustomAttribute("Optional, DefaultParameterValue(", () => { - generator.Visit(defaultArg); + generator.VisitTransparentStructDefaultArg(defaultArg); csharpOutputBuilder.Write(')'); }); } @@ -1486,6 +1486,24 @@ bool IsDefaultValue(Expr defaultArg) } } + private void VisitTransparentStructDefaultArg(Expr defaultArg) + { + // A default value cast to a transparent struct (e.g. `S_OK` => `(HRESULT)0`) is not a + // constant expression, so `DefaultParameterValue` must receive the underlying constant. + if (IsStmtAsWritten(defaultArg, out var castExpr, removeParens: true)) + { + var typeName = GetRemappedTypeName(castExpr, context: null, castExpr.Type, out _); + + if (_config.WithTransparentStructs.ContainsKey(typeName)) + { + Visit(castExpr.SubExprAsWritten); + return; + } + } + + Visit(defaultArg); + } + private void VisitTranslationUnitDecl(TranslationUnitDecl translationUnitDecl) { PreprocessUuidNameOverrides(translationUnitDecl.Decls); diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitVarDecl.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitVarDecl.cs index fe6cce57..903b9f74 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitVarDecl.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitVarDecl.cs @@ -124,6 +124,14 @@ private void VisitVarDecl(VarDecl varDecl) flags |= ValueFlags.Constant; } + if (varDecl.HasInit && IsStmtAsWritten(varDecl.Init, out _, removeParens: true)) + { + // A `__uuidof` binding (e.g. WinRT `MIDL_CONST_ID IID& IID_X = __uuidof(X)`) is + // already emitted as a `ref readonly Guid` via _uuidsToGenerate; skip the redundant + // declaration to avoid a duplicate member. + return; + } + if (IsStmtAsWritten(varDecl.Init, out var stringLiteral, removeParens: true)) { kind = ValueKind.String; @@ -195,6 +203,15 @@ private void VisitVarDecl(VarDecl varDecl) // It's easiest just to let _uuidsToGenerate handle it return; } + + // clang wraps an alias to another constant (e.g. `#define IID_X IID_Y`) in a + // copy-constructor. The referenced constant has backing storage, so keep the + // `ref readonly` alias rather than emitting a by-value copy. + if (IsStmtAsWritten(cxxConstructExpr.Args[0], out _, removeParens: true)) + { + flags |= ValueFlags.Reference; + } + flags |= ValueFlags.Copy; } else if (IsStmtAsWritten(varDecl.Init, out _, removeParens: true)) diff --git a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs index 4e2ab4b3..4aa01e25 100644 --- a/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs +++ b/sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs @@ -2511,6 +2511,14 @@ private void UncheckStmt(string targetTypeName, Stmt stmt) needsCast = true; } + // A signed-negative value does not implicitly convert to an unsigned target in C# + // (e.g. `uint x = -1`), so emit the explicit cast to the target type. + if (IsPrevContextDecl(out _, out _) && IsUnsigned(targetTypeName) + && !IsStmtAsWritten(stmt, out _, removeParens: true) && IsNegativeConstant(stmt)) + { + needsCast = true; + } + if (needsCast) { _outputBuilder.BeginInnerValue(); @@ -2545,6 +2553,13 @@ private void UncheckStmt(string targetTypeName, Stmt stmt) } } + private static bool IsNegativeConstant(Stmt stmt) + { + var written = (stmt is Expr expr) ? GetExprAsWritten(expr, removeParens: true) : stmt; + var evaluation = written.Handle.Evaluate; + return (evaluation.Kind == CXEval_Int) && (evaluation.AsLongLong < 0); + } + private bool EnumConstantInitNeedsCast(string targetTypeName, Stmt stmt) { // A C# enum member initializer must be implicitly convertible to the enum's underlying diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationDllImport/OptionalParameterTransparentStructTest.CSharp.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationDllImport/OptionalParameterTransparentStructTest.CSharp.cs new file mode 100644 index 00000000..71e9215f --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationDllImport/OptionalParameterTransparentStructTest.CSharp.cs @@ -0,0 +1,13 @@ +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public static partial class Methods + { + [DllImport("ClangSharpPInvokeGenerator", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern void MyFunction([Optional, DefaultParameterValue(0)] HRESULT value); + + [NativeTypeName("#define S_OK ((HRESULT)0L)")] + public const int S_OK = ((int)(0)); + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationDllImport/OptionalParameterTransparentStructTest.Xml.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationDllImport/OptionalParameterTransparentStructTest.Xml.xml new file mode 100644 index 00000000..172b1e01 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationDllImport/OptionalParameterTransparentStructTest.Xml.xml @@ -0,0 +1,22 @@ + + + + + + void + + HRESULT + + ((HRESULT)(0)) + + + + + int + + ((int)(0)) + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationDllImport/OptionalParameterUnsignedNegativeLiteralTest.CSharp.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationDllImport/OptionalParameterUnsignedNegativeLiteralTest.CSharp.cs new file mode 100644 index 00000000..e616c649 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationDllImport/OptionalParameterUnsignedNegativeLiteralTest.CSharp.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public static partial class Methods + { + [DllImport("ClangSharpPInvokeGenerator", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern void MyFunction([NativeTypeName("unsigned int")] uint value = unchecked((uint)(-1))); + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationDllImport/OptionalParameterUnsignedNegativeLiteralTest.Xml.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationDllImport/OptionalParameterUnsignedNegativeLiteralTest.Xml.xml new file mode 100644 index 00000000..4d56437a --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/FunctionDeclarationDllImport/OptionalParameterUnsignedNegativeLiteralTest.Xml.xml @@ -0,0 +1,23 @@ + + + + + + void + + uint + + + + uint + + -1 + + + + + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidDefineAliasRefReadonlyTest.CSharp.Compatible.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidDefineAliasRefReadonlyTest.CSharp.Compatible.cs new file mode 100644 index 00000000..6761a303 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidDefineAliasRefReadonlyTest.CSharp.Compatible.cs @@ -0,0 +1,58 @@ +using ClangSharp.Test; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + public unsafe partial struct Guid + { + [NativeTypeName("unsigned long")] + public uint Data1; + + [NativeTypeName("unsigned short")] + public ushort Data2; + + [NativeTypeName("unsigned short")] + public ushort Data3; + + [NativeTypeName("unsigned char[8]")] + public fixed byte Data4[8]; + } + + [Guid("79EAC9E0-BAF9-11CE-8C82-00AA004BA90B")] + public partial struct IInternet + { + public int x; + } + + public static partial class Methods + { + [NativeTypeName("#define IID_IOInet IID_IInternet")] + public static ref readonly Guid IID_IOInet => ref IID_IInternet; + + public static ref readonly Guid IID_IInternet + { + get + { + ReadOnlySpan data = new byte[] { + 0xE0, 0xC9, 0xEA, 0x79, + 0xF9, 0xBA, + 0xCE, 0x11, + 0x8C, + 0x82, + 0x00, + 0xAA, + 0x00, + 0x4B, + 0xA9, + 0x0B + }; + + Debug.Assert(data.Length == Unsafe.SizeOf()); + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidDefineAliasRefReadonlyTest.CSharp.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidDefineAliasRefReadonlyTest.CSharp.cs new file mode 100644 index 00000000..036c8e7e --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidDefineAliasRefReadonlyTest.CSharp.cs @@ -0,0 +1,64 @@ +using ClangSharp.Test; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + public partial struct Guid + { + [NativeTypeName("unsigned long")] + public uint Data1; + + [NativeTypeName("unsigned short")] + public ushort Data2; + + [NativeTypeName("unsigned short")] + public ushort Data3; + + [NativeTypeName("unsigned char[8]")] + public _Data4_e__FixedBuffer Data4; + + [InlineArray(8)] + public partial struct _Data4_e__FixedBuffer + { + public byte e0; + } + } + + [Guid("79EAC9E0-BAF9-11CE-8C82-00AA004BA90B")] + public partial struct IInternet + { + public int x; + } + + public static partial class Methods + { + [NativeTypeName("#define IID_IOInet IID_IInternet")] + public static ref readonly Guid IID_IOInet => ref IID_IInternet; + + public static ref readonly Guid IID_IInternet + { + get + { + ReadOnlySpan data = [ + 0xE0, 0xC9, 0xEA, 0x79, + 0xF9, 0xBA, + 0xCE, 0x11, + 0x8C, + 0x82, + 0x00, + 0xAA, + 0x00, + 0x4B, + 0xA9, + 0x0B + ]; + + Debug.Assert(data.Length == Unsafe.SizeOf()); + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidDefineAliasRefReadonlyTest.Xml.Compatible.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidDefineAliasRefReadonlyTest.Xml.Compatible.xml new file mode 100644 index 00000000..2f4b9c96 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidDefineAliasRefReadonlyTest.Xml.Compatible.xml @@ -0,0 +1,33 @@ + + + + + + uint + + + ushort + + + ushort + + + byte + + + + + int + + + + + Guid + + ref IID_IInternet + + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidDefineAliasRefReadonlyTest.Xml.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidDefineAliasRefReadonlyTest.Xml.xml new file mode 100644 index 00000000..1ebab883 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidDefineAliasRefReadonlyTest.Xml.xml @@ -0,0 +1,39 @@ + + + + + + uint + + + ushort + + + ushort + + + byte + + + InlineArray(8) + + byte + + + + + + int + + + + + Guid + + ref IID_IInternet + + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidInterfaceDuplicateIidTest.CSharp.Compatible.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidInterfaceDuplicateIidTest.CSharp.Compatible.cs new file mode 100644 index 00000000..541aa12f --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidInterfaceDuplicateIidTest.CSharp.Compatible.cs @@ -0,0 +1,55 @@ +using ClangSharp.Test; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + public unsafe partial struct Guid + { + [NativeTypeName("unsigned long")] + public uint Data1; + + [NativeTypeName("unsigned short")] + public ushort Data2; + + [NativeTypeName("unsigned short")] + public ushort Data3; + + [NativeTypeName("unsigned char[8]")] + public fixed byte Data4[8]; + } + + [Guid("E504A81C-6B01-4A88-8E1E-000000000000")] + public partial struct ITransferTarget + { + public int x; + } + + public static unsafe partial class Methods + { + public static ref readonly Guid IID_ITransferTarget + { + get + { + ReadOnlySpan data = new byte[] { + 0x1C, 0xA8, 0x04, 0xE5, + 0x01, 0x6B, + 0x88, 0x4A, + 0x8E, + 0x1E, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00 + }; + + Debug.Assert(data.Length == Unsafe.SizeOf()); + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidInterfaceDuplicateIidTest.CSharp.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidInterfaceDuplicateIidTest.CSharp.cs new file mode 100644 index 00000000..ffcbe7df --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidInterfaceDuplicateIidTest.CSharp.cs @@ -0,0 +1,61 @@ +using ClangSharp.Test; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace System +{ + public partial struct Guid + { + [NativeTypeName("unsigned long")] + public uint Data1; + + [NativeTypeName("unsigned short")] + public ushort Data2; + + [NativeTypeName("unsigned short")] + public ushort Data3; + + [NativeTypeName("unsigned char[8]")] + public _Data4_e__FixedBuffer Data4; + + [InlineArray(8)] + public partial struct _Data4_e__FixedBuffer + { + public byte e0; + } + } + + [Guid("E504A81C-6B01-4A88-8E1E-000000000000")] + public partial struct ITransferTarget + { + public int x; + } + + public static unsafe partial class Methods + { + public static ref readonly Guid IID_ITransferTarget + { + get + { + ReadOnlySpan data = [ + 0x1C, 0xA8, 0x04, 0xE5, + 0x01, 0x6B, + 0x88, 0x4A, + 0x8E, + 0x1E, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00 + ]; + + Debug.Assert(data.Length == Unsafe.SizeOf()); + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidInterfaceDuplicateIidTest.Xml.Compatible.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidInterfaceDuplicateIidTest.Xml.Compatible.xml new file mode 100644 index 00000000..60102f45 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidInterfaceDuplicateIidTest.Xml.Compatible.xml @@ -0,0 +1,27 @@ + + + + + + uint + + + ushort + + + ushort + + + byte + + + + + int + + + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidInterfaceDuplicateIidTest.Xml.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidInterfaceDuplicateIidTest.Xml.xml new file mode 100644 index 00000000..047798fc --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/GuidInterfaceDuplicateIidTest.Xml.xml @@ -0,0 +1,33 @@ + + + + + + uint + + + ushort + + + ushort + + + byte + + + InlineArray(8) + + byte + + + + + + int + + + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/MultiLevelComInheritanceTest.CSharp.Compatible.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/MultiLevelComInheritanceTest.CSharp.Compatible.cs new file mode 100644 index 00000000..c341623b --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/MultiLevelComInheritanceTest.CSharp.Compatible.cs @@ -0,0 +1,320 @@ +using System; +using System.Runtime.InteropServices; + +namespace ClangSharp.Test +{ + public unsafe partial struct IUnknown + { + public void** lpVtbl; + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + public delegate int _QueryInterface(IUnknown* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + [return: NativeTypeName("unsigned int")] + public delegate uint _AddRef(IUnknown* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + [return: NativeTypeName("unsigned int")] + public delegate uint _Release(IUnknown* pThis); + + [VtblIndex(0)] + public int QueryInterface() + { + fixed (IUnknown* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_QueryInterface>((IntPtr)(lpVtbl[0]))(pThis); + } + } + + [VtblIndex(1)] + [return: NativeTypeName("unsigned int")] + public uint AddRef() + { + fixed (IUnknown* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_AddRef>((IntPtr)(lpVtbl[1]))(pThis); + } + } + + [VtblIndex(2)] + [return: NativeTypeName("unsigned int")] + public uint Release() + { + fixed (IUnknown* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Release>((IntPtr)(lpVtbl[2]))(pThis); + } + } + } + + [NativeTypeName("struct ISequentialStream : IUnknown")] + public unsafe partial struct ISequentialStream + { + public void** lpVtbl; + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + public delegate int _QueryInterface(ISequentialStream* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + [return: NativeTypeName("unsigned int")] + public delegate uint _AddRef(ISequentialStream* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + [return: NativeTypeName("unsigned int")] + public delegate uint _Release(ISequentialStream* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + public delegate int _Read(ISequentialStream* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + public delegate int _Write(ISequentialStream* pThis); + + [VtblIndex(0)] + public int QueryInterface() + { + fixed (ISequentialStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_QueryInterface>((IntPtr)(lpVtbl[0]))(pThis); + } + } + + [VtblIndex(1)] + [return: NativeTypeName("unsigned int")] + public uint AddRef() + { + fixed (ISequentialStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_AddRef>((IntPtr)(lpVtbl[1]))(pThis); + } + } + + [VtblIndex(2)] + [return: NativeTypeName("unsigned int")] + public uint Release() + { + fixed (ISequentialStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Release>((IntPtr)(lpVtbl[2]))(pThis); + } + } + + [VtblIndex(3)] + public int Read() + { + fixed (ISequentialStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Read>((IntPtr)(lpVtbl[3]))(pThis); + } + } + + [VtblIndex(4)] + public int Write() + { + fixed (ISequentialStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Write>((IntPtr)(lpVtbl[4]))(pThis); + } + } + } + + [NativeTypeName("struct IStream : ISequentialStream")] + public unsafe partial struct IStream + { + public void** lpVtbl; + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + public delegate int _QueryInterface(IStream* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + [return: NativeTypeName("unsigned int")] + public delegate uint _AddRef(IStream* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + [return: NativeTypeName("unsigned int")] + public delegate uint _Release(IStream* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + public delegate int _Read(IStream* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + public delegate int _Write(IStream* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + public delegate int _Seek(IStream* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + public delegate int _Clone(IStream* pThis); + + [VtblIndex(0)] + public int QueryInterface() + { + fixed (IStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_QueryInterface>((IntPtr)(lpVtbl[0]))(pThis); + } + } + + [VtblIndex(1)] + [return: NativeTypeName("unsigned int")] + public uint AddRef() + { + fixed (IStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_AddRef>((IntPtr)(lpVtbl[1]))(pThis); + } + } + + [VtblIndex(2)] + [return: NativeTypeName("unsigned int")] + public uint Release() + { + fixed (IStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Release>((IntPtr)(lpVtbl[2]))(pThis); + } + } + + [VtblIndex(3)] + public int Read() + { + fixed (IStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Read>((IntPtr)(lpVtbl[3]))(pThis); + } + } + + [VtblIndex(4)] + public int Write() + { + fixed (IStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Write>((IntPtr)(lpVtbl[4]))(pThis); + } + } + + [VtblIndex(5)] + public int Seek() + { + fixed (IStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Seek>((IntPtr)(lpVtbl[5]))(pThis); + } + } + + [VtblIndex(6)] + public int Clone() + { + fixed (IStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Clone>((IntPtr)(lpVtbl[6]))(pThis); + } + } + } + + [NativeTypeName("struct IStreamAsync : IStream")] + public unsafe partial struct IStreamAsync + { + public void** lpVtbl; + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + public delegate int _QueryInterface(IStreamAsync* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + [return: NativeTypeName("unsigned int")] + public delegate uint _AddRef(IStreamAsync* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + [return: NativeTypeName("unsigned int")] + public delegate uint _Release(IStreamAsync* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + public delegate int _Read(IStreamAsync* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + public delegate int _Write(IStreamAsync* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + public delegate int _Seek(IStreamAsync* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + public delegate int _Clone(IStreamAsync* pThis); + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + public delegate int _ReadAsync(IStreamAsync* pThis); + + [VtblIndex(0)] + public int QueryInterface() + { + fixed (IStreamAsync* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_QueryInterface>((IntPtr)(lpVtbl[0]))(pThis); + } + } + + [VtblIndex(1)] + [return: NativeTypeName("unsigned int")] + public uint AddRef() + { + fixed (IStreamAsync* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_AddRef>((IntPtr)(lpVtbl[1]))(pThis); + } + } + + [VtblIndex(2)] + [return: NativeTypeName("unsigned int")] + public uint Release() + { + fixed (IStreamAsync* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Release>((IntPtr)(lpVtbl[2]))(pThis); + } + } + + [VtblIndex(3)] + public int Read() + { + fixed (IStreamAsync* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Read>((IntPtr)(lpVtbl[3]))(pThis); + } + } + + [VtblIndex(4)] + public int Write() + { + fixed (IStreamAsync* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Write>((IntPtr)(lpVtbl[4]))(pThis); + } + } + + [VtblIndex(5)] + public int Seek() + { + fixed (IStreamAsync* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Seek>((IntPtr)(lpVtbl[5]))(pThis); + } + } + + [VtblIndex(6)] + public int Clone() + { + fixed (IStreamAsync* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Clone>((IntPtr)(lpVtbl[6]))(pThis); + } + } + + [VtblIndex(7)] + public int ReadAsync() + { + fixed (IStreamAsync* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_ReadAsync>((IntPtr)(lpVtbl[7]))(pThis); + } + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/MultiLevelComInheritanceTest.CSharp.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/MultiLevelComInheritanceTest.CSharp.cs new file mode 100644 index 00000000..b27c6bfc --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/MultiLevelComInheritanceTest.CSharp.cs @@ -0,0 +1,173 @@ +using System.Runtime.CompilerServices; + +namespace ClangSharp.Test +{ + public unsafe partial struct IUnknown + { + public void** lpVtbl; + + [VtblIndex(0)] + public int QueryInterface() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[0]))((IUnknown*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(1)] + [return: NativeTypeName("unsigned int")] + public uint AddRef() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[1]))((IUnknown*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(2)] + [return: NativeTypeName("unsigned int")] + public uint Release() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[2]))((IUnknown*)Unsafe.AsPointer(ref this)); + } + } + + [NativeTypeName("struct ISequentialStream : IUnknown")] + public unsafe partial struct ISequentialStream + { + public void** lpVtbl; + + [VtblIndex(0)] + public int QueryInterface() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[0]))((ISequentialStream*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(1)] + [return: NativeTypeName("unsigned int")] + public uint AddRef() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[1]))((ISequentialStream*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(2)] + [return: NativeTypeName("unsigned int")] + public uint Release() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[2]))((ISequentialStream*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(3)] + public int Read() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[3]))((ISequentialStream*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(4)] + public int Write() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[4]))((ISequentialStream*)Unsafe.AsPointer(ref this)); + } + } + + [NativeTypeName("struct IStream : ISequentialStream")] + public unsafe partial struct IStream + { + public void** lpVtbl; + + [VtblIndex(0)] + public int QueryInterface() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[0]))((IStream*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(1)] + [return: NativeTypeName("unsigned int")] + public uint AddRef() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[1]))((IStream*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(2)] + [return: NativeTypeName("unsigned int")] + public uint Release() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[2]))((IStream*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(3)] + public int Read() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[3]))((IStream*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(4)] + public int Write() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[4]))((IStream*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(5)] + public int Seek() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[5]))((IStream*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(6)] + public int Clone() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[6]))((IStream*)Unsafe.AsPointer(ref this)); + } + } + + [NativeTypeName("struct IStreamAsync : IStream")] + public unsafe partial struct IStreamAsync + { + public void** lpVtbl; + + [VtblIndex(0)] + public int QueryInterface() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[0]))((IStreamAsync*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(1)] + [return: NativeTypeName("unsigned int")] + public uint AddRef() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[1]))((IStreamAsync*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(2)] + [return: NativeTypeName("unsigned int")] + public uint Release() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[2]))((IStreamAsync*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(3)] + public int Read() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[3]))((IStreamAsync*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(4)] + public int Write() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[4]))((IStreamAsync*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(5)] + public int Seek() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[5]))((IStreamAsync*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(6)] + public int Clone() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[6]))((IStreamAsync*)Unsafe.AsPointer(ref this)); + } + + [VtblIndex(7)] + public int ReadAsync() + { + return ((delegate* unmanaged[Thiscall])(lpVtbl[7]))((IStreamAsync*)Unsafe.AsPointer(ref this)); + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/MultiLevelComInheritanceTest.Xml.Compatible.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/MultiLevelComInheritanceTest.Xml.Compatible.xml new file mode 100644 index 00000000..79ab8e47 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/MultiLevelComInheritanceTest.Xml.Compatible.xml @@ -0,0 +1,370 @@ + + + + + + void** + + + int + + IUnknown* + + + + uint + + IUnknown* + + + + uint + + IUnknown* + + + + int + + fixed (IUnknown* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_QueryInterface>((IntPtr)(lpVtbl[0]))(pThis); + } + + + + uint + + fixed (IUnknown* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_AddRef>((IntPtr)(lpVtbl[1]))(pThis); + } + + + + uint + + fixed (IUnknown* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Release>((IntPtr)(lpVtbl[2]))(pThis); + } + + + + + + void** + + + int + + ISequentialStream* + + + + uint + + ISequentialStream* + + + + uint + + ISequentialStream* + + + + int + + ISequentialStream* + + + + int + + ISequentialStream* + + + + int + + fixed (ISequentialStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_QueryInterface>((IntPtr)(lpVtbl[0]))(pThis); + } + + + + uint + + fixed (ISequentialStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_AddRef>((IntPtr)(lpVtbl[1]))(pThis); + } + + + + uint + + fixed (ISequentialStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Release>((IntPtr)(lpVtbl[2]))(pThis); + } + + + + int + + fixed (ISequentialStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Read>((IntPtr)(lpVtbl[3]))(pThis); + } + + + + int + + fixed (ISequentialStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Write>((IntPtr)(lpVtbl[4]))(pThis); + } + + + + + + void** + + + int + + IStream* + + + + uint + + IStream* + + + + uint + + IStream* + + + + int + + IStream* + + + + int + + IStream* + + + + int + + IStream* + + + + int + + IStream* + + + + int + + fixed (IStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_QueryInterface>((IntPtr)(lpVtbl[0]))(pThis); + } + + + + uint + + fixed (IStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_AddRef>((IntPtr)(lpVtbl[1]))(pThis); + } + + + + uint + + fixed (IStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Release>((IntPtr)(lpVtbl[2]))(pThis); + } + + + + int + + fixed (IStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Read>((IntPtr)(lpVtbl[3]))(pThis); + } + + + + int + + fixed (IStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Write>((IntPtr)(lpVtbl[4]))(pThis); + } + + + + int + + fixed (IStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Seek>((IntPtr)(lpVtbl[5]))(pThis); + } + + + + int + + fixed (IStream* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Clone>((IntPtr)(lpVtbl[6]))(pThis); + } + + + + + + void** + + + int + + IStreamAsync* + + + + uint + + IStreamAsync* + + + + uint + + IStreamAsync* + + + + int + + IStreamAsync* + + + + int + + IStreamAsync* + + + + int + + IStreamAsync* + + + + int + + IStreamAsync* + + + + int + + IStreamAsync* + + + + int + + fixed (IStreamAsync* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_QueryInterface>((IntPtr)(lpVtbl[0]))(pThis); + } + + + + uint + + fixed (IStreamAsync* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_AddRef>((IntPtr)(lpVtbl[1]))(pThis); + } + + + + uint + + fixed (IStreamAsync* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Release>((IntPtr)(lpVtbl[2]))(pThis); + } + + + + int + + fixed (IStreamAsync* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Read>((IntPtr)(lpVtbl[3]))(pThis); + } + + + + int + + fixed (IStreamAsync* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Write>((IntPtr)(lpVtbl[4]))(pThis); + } + + + + int + + fixed (IStreamAsync* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Seek>((IntPtr)(lpVtbl[5]))(pThis); + } + + + + int + + fixed (IStreamAsync* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_Clone>((IntPtr)(lpVtbl[6]))(pThis); + } + + + + int + + fixed (IStreamAsync* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_ReadAsync>((IntPtr)(lpVtbl[7]))(pThis); + } + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/MultiLevelComInheritanceTest.Xml.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/MultiLevelComInheritanceTest.Xml.xml new file mode 100644 index 00000000..7b008031 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/MultiLevelComInheritanceTest.Xml.xml @@ -0,0 +1,163 @@ + + + + + + void** + + + int + + return ((delegate* unmanaged[Thiscall]<IUnknown*, int>)(lpVtbl[0]))((IUnknown*)Unsafe.AsPointer(ref this)); + + + + uint + + return ((delegate* unmanaged[Thiscall]<IUnknown*, uint>)(lpVtbl[1]))((IUnknown*)Unsafe.AsPointer(ref this)); + + + + uint + + return ((delegate* unmanaged[Thiscall]<IUnknown*, uint>)(lpVtbl[2]))((IUnknown*)Unsafe.AsPointer(ref this)); + + + + + + void** + + + int + + return ((delegate* unmanaged[Thiscall]<ISequentialStream*, int>)(lpVtbl[0]))((ISequentialStream*)Unsafe.AsPointer(ref this)); + + + + uint + + return ((delegate* unmanaged[Thiscall]<ISequentialStream*, uint>)(lpVtbl[1]))((ISequentialStream*)Unsafe.AsPointer(ref this)); + + + + uint + + return ((delegate* unmanaged[Thiscall]<ISequentialStream*, uint>)(lpVtbl[2]))((ISequentialStream*)Unsafe.AsPointer(ref this)); + + + + int + + return ((delegate* unmanaged[Thiscall]<ISequentialStream*, int>)(lpVtbl[3]))((ISequentialStream*)Unsafe.AsPointer(ref this)); + + + + int + + return ((delegate* unmanaged[Thiscall]<ISequentialStream*, int>)(lpVtbl[4]))((ISequentialStream*)Unsafe.AsPointer(ref this)); + + + + + + void** + + + int + + return ((delegate* unmanaged[Thiscall]<IStream*, int>)(lpVtbl[0]))((IStream*)Unsafe.AsPointer(ref this)); + + + + uint + + return ((delegate* unmanaged[Thiscall]<IStream*, uint>)(lpVtbl[1]))((IStream*)Unsafe.AsPointer(ref this)); + + + + uint + + return ((delegate* unmanaged[Thiscall]<IStream*, uint>)(lpVtbl[2]))((IStream*)Unsafe.AsPointer(ref this)); + + + + int + + return ((delegate* unmanaged[Thiscall]<IStream*, int>)(lpVtbl[3]))((IStream*)Unsafe.AsPointer(ref this)); + + + + int + + return ((delegate* unmanaged[Thiscall]<IStream*, int>)(lpVtbl[4]))((IStream*)Unsafe.AsPointer(ref this)); + + + + int + + return ((delegate* unmanaged[Thiscall]<IStream*, int>)(lpVtbl[5]))((IStream*)Unsafe.AsPointer(ref this)); + + + + int + + return ((delegate* unmanaged[Thiscall]<IStream*, int>)(lpVtbl[6]))((IStream*)Unsafe.AsPointer(ref this)); + + + + + + void** + + + int + + return ((delegate* unmanaged[Thiscall]<IStreamAsync*, int>)(lpVtbl[0]))((IStreamAsync*)Unsafe.AsPointer(ref this)); + + + + uint + + return ((delegate* unmanaged[Thiscall]<IStreamAsync*, uint>)(lpVtbl[1]))((IStreamAsync*)Unsafe.AsPointer(ref this)); + + + + uint + + return ((delegate* unmanaged[Thiscall]<IStreamAsync*, uint>)(lpVtbl[2]))((IStreamAsync*)Unsafe.AsPointer(ref this)); + + + + int + + return ((delegate* unmanaged[Thiscall]<IStreamAsync*, int>)(lpVtbl[3]))((IStreamAsync*)Unsafe.AsPointer(ref this)); + + + + int + + return ((delegate* unmanaged[Thiscall]<IStreamAsync*, int>)(lpVtbl[4]))((IStreamAsync*)Unsafe.AsPointer(ref this)); + + + + int + + return ((delegate* unmanaged[Thiscall]<IStreamAsync*, int>)(lpVtbl[5]))((IStreamAsync*)Unsafe.AsPointer(ref this)); + + + + int + + return ((delegate* unmanaged[Thiscall]<IStreamAsync*, int>)(lpVtbl[6]))((IStreamAsync*)Unsafe.AsPointer(ref this)); + + + + int + + return ((delegate* unmanaged[Thiscall]<IStreamAsync*, int>)(lpVtbl[7]))((IStreamAsync*)Unsafe.AsPointer(ref this)); + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/TemplateArgumentNamespaceUsingTest.CSharp.Compatible.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/TemplateArgumentNamespaceUsingTest.CSharp.Compatible.cs new file mode 100644 index 00000000..921d32a9 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/TemplateArgumentNamespaceUsingTest.CSharp.Compatible.cs @@ -0,0 +1,37 @@ +using ClangSharp.Test; +using System; +using System.Numerics; +using System.Runtime.InteropServices; + +namespace System.Numerics +{ + public unsafe partial struct Matrix4x4 + { + [NativeTypeName("float[16]")] + public fixed float m[16]; + } + + public unsafe partial struct ISpatial : ISpatial.Interface + { + public void** lpVtbl; + + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] + [return: NativeTypeName("ABI::Windows::Foundation::IReference *")] + public delegate IReference* _GetTransform(ISpatial* pThis); + + [return: NativeTypeName("ABI::Windows::Foundation::IReference *")] + public IReference* GetTransform() + { + fixed (ISpatial* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_GetTransform>((IntPtr)(lpVtbl[0]))(pThis); + } + } + + public interface Interface + { + [return: NativeTypeName("ABI::Windows::Foundation::IReference *")] + IReference* GetTransform(); + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/TemplateArgumentNamespaceUsingTest.CSharp.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/TemplateArgumentNamespaceUsingTest.CSharp.cs new file mode 100644 index 00000000..4c1e4bb0 --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/TemplateArgumentNamespaceUsingTest.CSharp.cs @@ -0,0 +1,35 @@ +using ClangSharp.Test; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace System.Numerics +{ + public partial struct Matrix4x4 + { + [NativeTypeName("float[16]")] + public _m_e__FixedBuffer m; + + [InlineArray(16)] + public partial struct _m_e__FixedBuffer + { + public float e0; + } + } + + public unsafe partial struct ISpatial : ISpatial.Interface + { + public void** lpVtbl; + + [return: NativeTypeName("ABI::Windows::Foundation::IReference *")] + public IReference* GetTransform() + { + return ((delegate* unmanaged[Thiscall]*>)(lpVtbl[0]))((ISpatial*)Unsafe.AsPointer(ref this)); + } + + public interface Interface + { + [return: NativeTypeName("ABI::Windows::Foundation::IReference *")] + IReference* GetTransform(); + } + } +} diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/TemplateArgumentNamespaceUsingTest.Xml.Compatible.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/TemplateArgumentNamespaceUsingTest.Xml.Compatible.xml new file mode 100644 index 00000000..fa997d4e --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/TemplateArgumentNamespaceUsingTest.Xml.Compatible.xml @@ -0,0 +1,35 @@ + + + + + + float + + + + + void** + + + IReference<Matrix4x4>* + + ISpatial* + + + + IReference<Matrix4x4>* + + fixed (ISpatial* pThis = &this) + { + return Marshal.GetDelegateForFunctionPointer<_GetTransform>((IntPtr)(lpVtbl[0]))(pThis); + } + + + + + IReference<Matrix4x4>* + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/TemplateArgumentNamespaceUsingTest.Xml.xml b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/TemplateArgumentNamespaceUsingTest.Xml.xml new file mode 100644 index 00000000..ecd02aab --- /dev/null +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/Baselines/StructDeclaration/TemplateArgumentNamespaceUsingTest.Xml.xml @@ -0,0 +1,32 @@ + + + + + + float + + + InlineArray(16) + + float + + + + + + void** + + + IReference<Matrix4x4>* + + return ((delegate* unmanaged[Thiscall]<ISpatial*, IReference<Matrix4x4>*>)(lpVtbl[0]))((ISpatial*)Unsafe.AsPointer(ref this)); + + + + + IReference<Matrix4x4>* + + + + + diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/FunctionDeclarationDllImportTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/FunctionDeclarationDllImportTest.cs index 72921329..52d722a2 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/FunctionDeclarationDllImportTest.cs +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/FunctionDeclarationDllImportTest.cs @@ -140,6 +140,34 @@ public Task OptionalParameterUnsafeTest(string nativeType, string nativeInit, st return ValidateAsync(nameof(OptionalParameterUnsafeTest), inputContents, discriminator: $"{nativeType}_{nativeInit}_{expectedManagedType}_{expectedManagedInit}"); } + [Test] + public Task OptionalParameterUnsignedNegativeLiteralTest() + { + // A bare negative literal default on an unsigned parameter must keep the explicit `(uint)` cast; otherwise + // `unchecked(-1)` stays `int` and fails to bind to the `uint` parameter (CS1750). + var inputContents = @"extern ""C"" void MyFunction(unsigned int value = -1);"; + + return ValidateAsync(nameof(OptionalParameterUnsignedNegativeLiteralTest), inputContents); + } + + [Test] + public Task OptionalParameterTransparentStructTest() + { + // A transparent-struct-typed parameter default must emit the bare constant. Wrapping it in a cast to the + // struct (`((HRESULT)(0))`) is not a constant expression and fails inside `DefaultParameterValue` (CS0182). + var inputContents = @"typedef long HRESULT; +#define S_OK ((HRESULT)0L) + +extern ""C"" void MyFunction(HRESULT value = S_OK);"; + + var withTransparentStructs = new Dictionary + { + ["HRESULT"] = ("int", PInvokeGeneratorTransparentStructKind.Unknown) + }; + var remappedNames = new Dictionary { ["HRESULT"] = "HRESULT" }; + return ValidateAsync(nameof(OptionalParameterTransparentStructTest), inputContents, remappedNames: remappedNames, withTransparentStructs: withTransparentStructs); + } + [Test] public Task WithCallConvTest() { diff --git a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/StructDeclarationTest.cs b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/StructDeclarationTest.cs index 61dfdfb0..97faf36b 100644 --- a/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/StructDeclarationTest.cs +++ b/tests/ClangSharp.PInvokeGenerator.UnitTests/Baseline/StructDeclarationTest.cs @@ -14,6 +14,7 @@ public sealed class StructDeclarationTest : BaselineTest private static readonly string[] ExcludeWildcardTestExcludedNames = ["Foo*", "*Legacy", "B?r"]; private static readonly string[] GuidTestExcludedNames = ["DECLSPEC_UUID"]; private static readonly string[] GuidConstTestExcludedNames = ["DECLSPEC_UUID", "GUID"]; + private static readonly string[] GuidDefineAliasTestExcludedNames = ["DECLSPEC_UUID", "EXTERN_C"]; public StructDeclarationTest(BaselineVariant variant) : base(variant) { @@ -558,6 +559,81 @@ struct ITextHost return ValidateAsync(nameof(GuidInterfaceWithGuidConstTest), inputContents, excludedNames: GuidConstTestExcludedNames, remappedNames: remappedNames, withGuids: withGuids); } + [Test] + public Task GuidInterfaceDuplicateIidTest() + { + if (Variant.Os != BaselineOs.Windows) + { + // Non-Windows doesn't support __declspec(uuid("")) + return Task.CompletedTask; + } + + // A `static const IID&` alias initialized from `__uuidof` names the same `IID_` symbol the interface's uuid + // already emits. Only one definition may be generated; the alias var must not add a second (CS0102). + var inputContents = @"#define DECLSPEC_UUID(x) __declspec(uuid(x)) + +struct _GUID +{ + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; +}; + +typedef struct _GUID GUID; +typedef GUID IID; + +struct DECLSPEC_UUID(""e504a81c-6b01-4a88-8e1e-000000000000"") ITransferTarget +{ + int x; +}; + +static const IID& IID_ITransferTarget = __uuidof(ITransferTarget); +"; + + var remappedNames = new Dictionary { ["_GUID"] = "Guid", ["GUID"] = "Guid" }; + return ValidateAsync(nameof(GuidInterfaceDuplicateIidTest), inputContents, additionalConfigOptions: PInvokeGeneratorConfigurationOptions.GenerateUnmanagedConstants, excludedNames: GuidTestExcludedNames, remappedNames: remappedNames); + } + + [Test] + public Task GuidDefineAliasRefReadonlyTest() + { + if (Variant.Os != BaselineOs.Windows) + { + // Non-Windows doesn't support __declspec(uuid("")) + return Task.CompletedTask; + } + + // A `#define IID_X IID_Y` alias over a `ref readonly Guid` IID must keep the `ref readonly` return so it + // stays a zero-copy alias; dropping it leaves a by-value `Guid` return paired with a `=> ref` body. + var inputContents = @"#define DECLSPEC_UUID(x) __declspec(uuid(x)) +#define EXTERN_C extern ""C"" + +struct _GUID +{ + unsigned long Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; +}; + +typedef struct _GUID GUID; +typedef GUID IID; + +struct DECLSPEC_UUID(""79eac9e0-baf9-11ce-8c82-00aa004ba90b"") IInternet +{ + int x; +}; + +EXTERN_C const IID IID_IInternet; + +#define IID_IOInet IID_IInternet +"; + + var remappedNames = new Dictionary { ["_GUID"] = "Guid", ["GUID"] = "Guid" }; + return ValidateAsync(nameof(GuidDefineAliasRefReadonlyTest), inputContents, additionalConfigOptions: PInvokeGeneratorConfigurationOptions.GenerateUnmanagedConstants | PInvokeGeneratorConfigurationOptions.GenerateMacroBindings, excludedNames: GuidDefineAliasTestExcludedNames, remappedNames: remappedNames); + } + [Test] public Task InheritanceTest() { @@ -608,6 +684,65 @@ struct MyStruct2 : MyStruct1A, MyStruct1B return ValidateAsync(nameof(InheritanceWithNativeInheritanceAttributeTest), inputContents, additionalConfigOptions: PInvokeGeneratorConfigurationOptions.GenerateNativeInheritanceAttribute); } + [Test] + public Task MultiLevelComInheritanceTest() + { + // When an intermediate base is reached through a forward `typedef struct X X;` (as MIDL interfaces are + // declared), clang binds the base to a non-definition redeclaration whose own members would otherwise be + // dropped. All inherited vtbl slots must still flatten in order (CS0535 when they don't). + var inputContents = @"struct IUnknown +{ + virtual int QueryInterface() = 0; + virtual unsigned int AddRef() = 0; + virtual unsigned int Release() = 0; +}; + +struct ISequentialStream : public IUnknown +{ + virtual int Read() = 0; + virtual int Write() = 0; +}; + +typedef struct IStream IStream; + +struct IStream : public ISequentialStream +{ + virtual int Seek() = 0; + virtual int Clone() = 0; +}; + +struct IStream; + +struct IStreamAsync : public IStream +{ + virtual int ReadAsync() = 0; +}; +"; + + return ValidateAsync(nameof(MultiLevelComInheritanceTest), inputContents, additionalConfigOptions: PInvokeGeneratorConfigurationOptions.GenerateVtblIndexAttribute); + } + + [Test] + public Task TemplateArgumentNamespaceUsingTest() + { + // A namespaced type (System.Numerics.Matrix4x4) used only as a generic-pointer-wrapper template argument + // must still emit its `using`; dropping it leaves the `Matrix4x4` reference unresolved (CS0246). + var inputContents = @"namespace ABI { namespace Windows { namespace Foundation { + template struct IReference { T value; }; + namespace Numerics { struct Matrix4x4 { float m[16]; }; } +}}} + +struct ISpatial +{ + virtual ABI::Windows::Foundation::IReference* GetTransform() = 0; +}; +"; + + var remappedNames = new Dictionary { ["ABI.Windows.Foundation.Numerics.Matrix4x4"] = "Matrix4x4" }; + var withNamespaces = new Dictionary { ["Matrix4x4"] = "System.Numerics" }; + return ValidateAsync(nameof(TemplateArgumentNamespaceUsingTest), inputContents, additionalConfigOptions: PInvokeGeneratorConfigurationOptions.GenerateGenericPointerWrapper | PInvokeGeneratorConfigurationOptions.GenerateMarkerInterfaces, remappedNames: remappedNames, withNamespaces: withNamespaces); + } + [TestCase("double", "double", 10, 5)] [TestCase("short", "short", 10, 5)] [TestCase("int", "int", 10, 5)]