Skip to content
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
19 changes: 17 additions & 2 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.VisitDecl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,12 @@ private void VisitDecl(Decl decl)
// case CX_DeclKind.CX_DeclKind_ClassTemplatePartialSpecialization:
// case CX_DeclKind.CX_DeclKind_TemplateTypeParm:
// case CX_DeclKind.CX_DeclKind_ObjCTypeParam:
// case CX_DeclKind.CX_DeclKind_TypeAlias:

case CX_DeclKind.CX_DeclKind_TypeAlias:
{
VisitTypeAliasDecl((TypeAliasDecl)decl);
break;
}

case CX_DeclKind.CX_DeclKind_Typedef:
{
Expand Down Expand Up @@ -2411,6 +2416,11 @@ private void VisitTranslationUnitDecl(TranslationUnitDecl translationUnitDecl)
Visit(translationUnitDecl.CursorChildren, translationUnitDecl.Decls);
}

private void VisitTypeAliasDecl(TypeAliasDecl typeAliasDecl)
{
// Nothing to generate for type alias declarations
}

private void VisitTypedefDecl(TypedefDecl typedefDecl)
{
ForUnderlyingType(typedefDecl, typedefDecl.UnderlyingType);
Expand Down Expand Up @@ -3083,7 +3093,12 @@ private bool IsConstant(Expr initExpr)
// case CX_StmtClass.CX_StmtClass_ObjCSelectorExpr:
// case CX_StmtClass.CX_StmtClass_ObjCStringLiteral:
// case CX_StmtClass.CX_StmtClass_ObjCSubscriptRefExpr:
// case CX_StmtClass.CX_StmtClass_OffsetOfExpr:

case CX_StmtClass.CX_StmtClass_OffsetOfExpr:
{
return false;
}

// case CX_StmtClass.CX_StmtClass_OpaqueValueExpr:
// case CX_StmtClass.CX_StmtClass_UnresolvedLookupExpr:
// case CX_StmtClass.CX_StmtClass_UnresolvedMemberExpr:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ private void VisitCallExpr(CallExpr callExpr)
Visit(callExpr.Callee);
VisitArgs(callExpr);
}
else if (calleeDecl is VarDecl)
{
Visit(callExpr.Callee);
VisitArgs(callExpr);
}
else
{
AddDiagnostic(DiagnosticLevel.Error, $"Unsupported callee declaration: '{calleeDecl?.DeclKindName}'. Generated bindings may be incomplete.", calleeDecl);
Expand Down Expand Up @@ -1642,7 +1647,13 @@ private void VisitStmt(Stmt stmt)
// case CX_StmtClass.CX_StmtClass_ObjCSelectorExpr:
// case CX_StmtClass.CX_StmtClass_ObjCStringLiteral:
// case CX_StmtClass.CX_StmtClass_ObjCSubscriptRefExpr:
// case CX_StmtClass.CX_StmtClass_OffsetOfExpr:

case CX_StmtClass.CX_StmtClass_OffsetOfExpr:
{
VisitOffsetOfExpr((OffsetOfExpr)stmt);
break;
}

// case CX_StmtClass.CX_StmtClass_OpaqueValueExpr:

case CX_StmtClass.CX_StmtClass_UnresolvedLookupExpr:
Expand Down Expand Up @@ -2055,6 +2066,20 @@ private void VisitUnaryOperator(UnaryOperator unaryOperator)
StopCSharpCode();
}

private void VisitOffsetOfExpr(OffsetOfExpr offsetOfExpr)
{
var outputBuilder = StartCSharpCode();

outputBuilder.AddUsingDirective("System.Runtime.InteropServices");
outputBuilder.Write("Marshal.OffsetOf<");
outputBuilder.Write(GetRemappedTypeName(offsetOfExpr, context: null, offsetOfExpr.TypeSourceInfoType, out var _));
outputBuilder.Write(">(\"");
Visit(offsetOfExpr.Referenced);
outputBuilder.Write("\")");

StopCSharpCode();
}

private void VisitUnresolvedLookupExpr(UnresolvedLookupExpr unresolvedLookupExpr)
{
var outputBuilder = StartCSharpCode();
Expand Down
40 changes: 38 additions & 2 deletions sources/ClangSharp.PInvokeGenerator/PInvokeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1503,7 +1503,16 @@ private string GetTypeName(Cursor cursor, Cursor context, Type type, out string
}
else
{
// The default name should be correct
// The default name should be correct for C++, but C may have a prefix we need to strip

if (name.StartsWith("enum "))
{
name = name.Substring(5);
}
else if (name.StartsWith("struct "))
{
name = name.Substring(7);
}
}

if (name.Contains("::"))
Expand Down Expand Up @@ -1629,6 +1638,10 @@ private string GetTypeNameForPointeeType(Cursor cursor, Cursor context, Type poi
{
name = GetTypeNameForPointeeType(cursor, context, attributedType.ModifiedType, out var nativeModifiedTypeName);
}
else if (pointeeType is ElaboratedType elaboratedType)
{
name = GetTypeNameForPointeeType(cursor, context, elaboratedType.NamedType, out var nativeNamedTypeName);
}
else if (pointeeType is FunctionType functionType)
{
if (!_config.ExcludeFnptrCodegen && (functionType is FunctionProtoType functionProtoType))
Expand Down Expand Up @@ -1728,6 +1741,24 @@ private string GetTypeNameForPointeeType(Cursor cursor, Cursor context, Type poi
name = "IntPtr";
}
}
else if (pointeeType is TypedefType typedefType)
{
// We check remapped names here so that types that have variable sizes
// can be treated correctly. Otherwise, they will resolve to a particular
// platform size, based on whatever parameters were passed into clang.

var remappedName = GetRemappedName(name, cursor, tryRemapOperatorName: false, out var wasRemapped);

if (wasRemapped)
{
name = remappedName;
name += '*';
}
else
{
name = GetTypeNameForPointeeType(cursor, context, typedefType.Decl.UnderlyingType, out var nativeUnderlyingTypeName);
}
}
else
{
// Otherwise fields that point at anonymous structs get the wrong name
Expand Down Expand Up @@ -2983,7 +3014,12 @@ private bool IsUnchecked(string targetTypeName, Stmt stmt)
// case CX_StmtClass.CX_StmtClass_ObjCSelectorExpr:
// case CX_StmtClass.CX_StmtClass_ObjCStringLiteral:
// case CX_StmtClass.CX_StmtClass_ObjCSubscriptRefExpr:
// case CX_StmtClass.CX_StmtClass_OffsetOfExpr:

case CX_StmtClass.CX_StmtClass_OffsetOfExpr:
{
return false;
}

// case CX_StmtClass.CX_StmtClass_OpaqueValueExpr:
// case CX_StmtClass.CX_StmtClass_UnresolvedLookupExpr:
// case CX_StmtClass.CX_StmtClass_UnresolvedMemberExpr:
Expand Down
2 changes: 1 addition & 1 deletion sources/ClangSharp/Cursors/Decls/Decl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ public bool IsStdNamespace
CX_DeclKind.CX_DeclKind_ObjCCategoryImpl => new ObjCCategoryImplDecl(handle),
CX_DeclKind.CX_DeclKind_ObjCImplementation => new ObjCImplementationDecl(handle),
CX_DeclKind.CX_DeclKind_ObjCInterface => new ObjCInterfaceDecl(handle),
CX_DeclKind.CX_DeclKind_ObjCProtocol => new ObjCPropertyDecl(handle),
CX_DeclKind.CX_DeclKind_ObjCProtocol => new ObjCProtocolDecl(handle),
CX_DeclKind.CX_DeclKind_ObjCMethod => new ObjCMethodDecl(handle),
CX_DeclKind.CX_DeclKind_ObjCProperty => new ObjCPropertyDecl(handle),
CX_DeclKind.CX_DeclKind_BuiltinTemplate => new BuiltinTemplateDecl(handle),
Expand Down
4 changes: 4 additions & 0 deletions sources/ClangSharp/Cursors/Exprs/OffsetOfExpr.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,22 @@ namespace ClangSharp
public sealed class OffsetOfExpr : Expr
{
private readonly Lazy<IReadOnlyList<Expr>> _indexExprs;
private readonly Lazy<Cursor> _referenced;
private readonly Lazy<Type> _typeSourceInfoType;

internal OffsetOfExpr(CXCursor handle) : base(handle, CXCursorKind.CXCursor_UnexposedExpr, CX_StmtClass.CX_StmtClass_OffsetOfExpr)
{
_indexExprs = new Lazy<IReadOnlyList<Expr>>(() => Children.Cast<Expr>().ToList());
_referenced = new Lazy<Cursor>(() => TranslationUnit.GetOrCreate<Cursor>(Handle.Referenced));
_typeSourceInfoType = new Lazy<Type>(() => TranslationUnit.GetOrCreate<Type>(Handle.TypeOperand));
}

public IReadOnlyList<Expr> IndexExprs => _indexExprs.Value;

public uint NumExpressions => NumChildren;

public Cursor Referenced => _referenced.Value;

public Type TypeSourceInfoType => _typeSourceInfoType.Value;
}
}
23 changes: 17 additions & 6 deletions sources/ClangSharpPInvokeGenerator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -440,12 +440,23 @@ public static int Run(InvocationContext context)
return -1;
}

var clangCommandLineArgs = new string[]
string[] clangCommandLineArgs;

if (string.IsNullOrWhiteSpace(std))
{
$"--language={language}", // Treat subsequent input files as having type <language>
$"--std={std}", // Language standard to compile for
"-Wno-pragma-once-outside-header" // We are processing files which may be header files
};
clangCommandLineArgs = new string[] {
$"--language={language}", // Treat subsequent input files as having type <language>
"-Wno-pragma-once-outside-header" // We are processing files which may be header files
};
}
else
{
clangCommandLineArgs = new string[] {
$"--language={language}", // Treat subsequent input files as having type <language>
$"--std={std}", // Language standard to compile for
"-Wno-pragma-once-outside-header" // We are processing files which may be header files
};
}

clangCommandLineArgs = clangCommandLineArgs.Concat(includeDirectories.Select(x => "--include-directory=" + x)).ToArray();
clangCommandLineArgs = clangCommandLineArgs.Concat(defineMacros.Select(x => "--define-macro=" + x)).ToArray();
Expand Down Expand Up @@ -806,7 +817,7 @@ private static void AddStdOption(RootCommand rootCommand)
aliases: new string[] { "--std", "-std" },
description: "Language standard to compile for.",
argumentType: typeof(string),
getDefaultValue: () => "c++17",
getDefaultValue: () => "",
arity: ArgumentArity.ExactlyOne
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@ public abstract class FunctionPointerDeclarationTest : PInvokeGeneratorTest
{
[Fact]
public abstract Task BasicTest();

[Fact]
public abstract Task PointerlessTypedefTest();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,54 @@ public sealed class CSharpCompatibleUnix_FunctionPointerDeclarationTest : Functi
{
public override Task BasicTest()
{
var inputContents = @"typedef void (*Callback)();";
var inputContents = @"typedef void (*Callback)();

var expectedOutputContents = @"using System.Runtime.InteropServices;
struct MyStruct {
Callback _callback;
};
";

var expectedOutputContents = @"using System;
using System.Runtime.InteropServices;

namespace ClangSharp.Test
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void Callback();

public partial struct MyStruct
{
[NativeTypeName(""Callback"")]
public IntPtr _callback;
}
}
";

return ValidateGeneratedCSharpCompatibleUnixBindingsAsync(inputContents, expectedOutputContents);
}

public override Task PointerlessTypedefTest()
{
var inputContents = @"typedef void (Callback)();

struct MyStruct {
Callback* _callback;
};
";

var expectedOutputContents = @"using System;
using System.Runtime.InteropServices;

namespace ClangSharp.Test
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void Callback();

public partial struct MyStruct
{
[NativeTypeName(""Callback *"")]
public IntPtr _callback;
}
}
";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,54 @@ public sealed class CSharpCompatibleWindows_FunctionPointerDeclarationTest : Fun
{
public override Task BasicTest()
{
var inputContents = @"typedef void (*Callback)();";
var inputContents = @"typedef void (*Callback)();

var expectedOutputContents = @"using System.Runtime.InteropServices;
struct MyStruct {
Callback _callback;
};
";

var expectedOutputContents = @"using System;
using System.Runtime.InteropServices;

namespace ClangSharp.Test
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void Callback();

public partial struct MyStruct
{
[NativeTypeName(""Callback"")]
public IntPtr _callback;
}
}
";

return ValidateGeneratedCSharpCompatibleWindowsBindingsAsync(inputContents, expectedOutputContents);
}

public override Task PointerlessTypedefTest()
{
var inputContents = @"typedef void (Callback)();

struct MyStruct {
Callback* _callback;
};
";

var expectedOutputContents = @"using System;
using System.Runtime.InteropServices;

namespace ClangSharp.Test
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void Callback();

public partial struct MyStruct
{
[NativeTypeName(""Callback *"")]
public IntPtr _callback;
}
}
";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,44 @@ public sealed class CSharpLatestUnix_FunctionPointerDeclarationTest : FunctionPo
{
public override Task BasicTest()
{
var inputContents = @"typedef void (*Callback)();";
var inputContents = @"typedef void (*Callback)();

var expectedOutputContents = "";
struct MyStruct {
Callback _callback;
};
";

var expectedOutputContents = @"namespace ClangSharp.Test
{
public unsafe partial struct MyStruct
{
[NativeTypeName(""Callback"")]
public delegate* unmanaged[Cdecl]<void> _callback;
}
}
";

return ValidateGeneratedCSharpLatestUnixBindingsAsync(inputContents, expectedOutputContents);
}

public override Task PointerlessTypedefTest()
{
var inputContents = @"typedef void (Callback)();

struct MyStruct {
Callback* _callback;
};
";

var expectedOutputContents = @"namespace ClangSharp.Test
{
public unsafe partial struct MyStruct
{
[NativeTypeName(""Callback *"")]
public delegate* unmanaged[Cdecl]<void> _callback;
}
}
";

return ValidateGeneratedCSharpLatestUnixBindingsAsync(inputContents, expectedOutputContents);
}
Expand Down
Loading