Skip to content

Commit

Permalink
Function pointers (#20)
Browse files Browse the repository at this point in the history
* Function pointers

* Fix tests

* Fix tests
  • Loading branch information
lithiumtoast authored Apr 1, 2024
1 parent cd68a72 commit 1aed7a7
Show file tree
Hide file tree
Showing 36 changed files with 462 additions and 56 deletions.
24 changes: 24 additions & 0 deletions src/c/tests/function_pointers/function_pointer_void/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"inputFilePath": "./main.c",
"userIncludeDirectories": [
"../../../production/ffi_helper/include"
],
"ignoredIncludeFiles": [
"../../../production/ffi_helper/include/ffi_helper.h"
],
"targetPlatforms": {
"windows": {
"x86_64-pc-windows-msvc": {},
"aarch64-pc-windows-msvc": {}
},
"macos": {
"aarch64-apple-darwin": {},
"x86_64-apple-darwin": {},
"aarch64-apple-ios": {}
},
"linux": {
"x86_64-unknown-linux-gnu": {},
"aarch64-unknown-linux-gnu": {}
}
}
}
9 changes: 9 additions & 0 deletions src/c/tests/function_pointers/function_pointer_void/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <stdio.h>
#include "ffi_helper.h"

typedef void (*function_pointer_void)();

FFI_API_DECL void function(function_pointer_void fnptr)
{
(*fnptr)();
}
27 changes: 27 additions & 0 deletions src/c/tests/structs/struct_int/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"inputFilePath": "./main.c",
"userIncludeDirectories": [
"../../../production/ffi_helper/include"
],
"ignoredIncludeFiles": [
"../../../production/ffi_helper/include/ffi_helper.h"
],
"targetPlatforms": {
"windows": {
"i686-pc-windows-msvc": {},
"x86_64-pc-windows-msvc": {},
"aarch64-pc-windows-msvc": {}
},
"macos": {
"i686-apple-darwin": {},
"aarch64-apple-darwin": {},
"x86_64-apple-darwin": {},
"aarch64-apple-ios": {}
},
"linux": {
"i686-unknown-linux-gnu": {},
"x86_64-unknown-linux-gnu": {},
"aarch64-unknown-linux-gnu": {}
}
}
}
9 changes: 9 additions & 0 deletions src/c/tests/structs/struct_int/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <stdio.h>
#include "ffi_helper.h"

struct struct_int
{
int a;
}

FFI_API_DECL struct struct_int struct_int;
27 changes: 27 additions & 0 deletions src/c/tests/unions/union_int_int/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"inputFilePath": "./main.c",
"userIncludeDirectories": [
"../../../production/ffi_helper/include"
],
"ignoredIncludeFiles": [
"../../../production/ffi_helper/include/ffi_helper.h"
],
"targetPlatforms": {
"windows": {
"i686-pc-windows-msvc": {},
"x86_64-pc-windows-msvc": {},
"aarch64-pc-windows-msvc": {}
},
"macos": {
"i686-apple-darwin": {},
"aarch64-apple-darwin": {},
"x86_64-apple-darwin": {},
"aarch64-apple-ios": {}
},
"linux": {
"i686-unknown-linux-gnu": {},
"x86_64-unknown-linux-gnu": {},
"aarch64-unknown-linux-gnu": {}
}
}
}
10 changes: 10 additions & 0 deletions src/c/tests/unions/union_int_int/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include <stdio.h>
#include "ffi_helper.h"

union union_int_int
{
int a;
int b;
}

FFI_API_DECL union union_int_int union_int_int;
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public partial class JsonSerializerContextCFfiCrossPlatform

private static global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] CFunctionPointerPropInit(global::System.Text.Json.JsonSerializerOptions options)
{
var properties = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[6];
var properties = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[7];

var info0 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::c2ffi.Data.CTypeInfo>
{
Expand Down Expand Up @@ -102,7 +102,26 @@ public partial class JsonSerializerContextCFfiCrossPlatform

properties[2] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::System.Collections.Immutable.ImmutableArray<global::c2ffi.Data.Nodes.CFunctionPointerParameter>>(options, info2);

var info3 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::c2ffi.Data.CLocation?>
var info3 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::c2ffi.Data.CFunctionCallingConvention>
{
IsProperty = true,
IsPublic = true,
IsVirtual = false,
DeclaringType = typeof(global::c2ffi.Data.Nodes.CFunctionPointer),
Converter = null,
Getter = static obj => ((global::c2ffi.Data.Nodes.CFunctionPointer)obj).CallingConvention,
Setter = static (obj, value) => ((global::c2ffi.Data.Nodes.CFunctionPointer)obj).CallingConvention = value!,
IgnoreCondition = null,
HasJsonInclude = false,
IsExtensionData = false,
NumberHandling = null,
PropertyName = "CallingConvention",
JsonPropertyName = null
};

properties[3] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::c2ffi.Data.CFunctionCallingConvention>(options, info3);

var info4 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::c2ffi.Data.CLocation?>
{
IsProperty = true,
IsPublic = true,
Expand All @@ -119,9 +138,9 @@ public partial class JsonSerializerContextCFfiCrossPlatform
JsonPropertyName = "location"
};

properties[3] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::c2ffi.Data.CLocation?>(options, info3);
properties[4] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::c2ffi.Data.CLocation?>(options, info4);

var info4 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<string>
var info5 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<string>
{
IsProperty = true,
IsPublic = true,
Expand All @@ -138,9 +157,9 @@ public partial class JsonSerializerContextCFfiCrossPlatform
JsonPropertyName = "comment"
};

properties[4] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<string>(options, info4);
properties[5] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<string>(options, info5);

var info5 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<string>
var info6 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<string>
{
IsProperty = true,
IsPublic = true,
Expand All @@ -157,7 +176,7 @@ public partial class JsonSerializerContextCFfiCrossPlatform
JsonPropertyName = null
};

properties[5] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<string>(options, info5);
properties[6] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<string>(options, info6);

return properties;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public partial class JsonSerializerContextCFfiTargetPlatform

private static global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[] CFunctionPointerPropInit(global::System.Text.Json.JsonSerializerOptions options)
{
var properties = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[6];
var properties = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfo[7];

var info0 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::c2ffi.Data.CTypeInfo>
{
Expand Down Expand Up @@ -102,7 +102,26 @@ public partial class JsonSerializerContextCFfiTargetPlatform

properties[2] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::System.Collections.Immutable.ImmutableArray<global::c2ffi.Data.Nodes.CFunctionPointerParameter>>(options, info2);

var info3 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::c2ffi.Data.CLocation?>
var info3 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::c2ffi.Data.CFunctionCallingConvention>
{
IsProperty = true,
IsPublic = true,
IsVirtual = false,
DeclaringType = typeof(global::c2ffi.Data.Nodes.CFunctionPointer),
Converter = null,
Getter = static obj => ((global::c2ffi.Data.Nodes.CFunctionPointer)obj).CallingConvention,
Setter = static (obj, value) => ((global::c2ffi.Data.Nodes.CFunctionPointer)obj).CallingConvention = value!,
IgnoreCondition = null,
HasJsonInclude = false,
IsExtensionData = false,
NumberHandling = null,
PropertyName = "CallingConvention",
JsonPropertyName = null
};

properties[3] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::c2ffi.Data.CFunctionCallingConvention>(options, info3);

var info4 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<global::c2ffi.Data.CLocation?>
{
IsProperty = true,
IsPublic = true,
Expand All @@ -119,9 +138,9 @@ public partial class JsonSerializerContextCFfiTargetPlatform
JsonPropertyName = "location"
};

properties[3] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::c2ffi.Data.CLocation?>(options, info3);
properties[4] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<global::c2ffi.Data.CLocation?>(options, info4);

var info4 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<string>
var info5 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<string>
{
IsProperty = true,
IsPublic = true,
Expand All @@ -138,9 +157,9 @@ public partial class JsonSerializerContextCFfiTargetPlatform
JsonPropertyName = "comment"
};

properties[4] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<string>(options, info4);
properties[5] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<string>(options, info5);

var info5 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<string>
var info6 = new global::System.Text.Json.Serialization.Metadata.JsonPropertyInfoValues<string>
{
IsProperty = true,
IsPublic = true,
Expand All @@ -157,7 +176,7 @@ public partial class JsonSerializerContextCFfiTargetPlatform
JsonPropertyName = null
};

properties[5] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<string>(options, info5);
properties[6] = global::System.Text.Json.Serialization.Metadata.JsonMetadataServices.CreatePropertyInfo<string>(options, info6);

return properties;
}
Expand Down
2 changes: 1 addition & 1 deletion src/cs/production/c2ffi.Data/Nodes/CFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class CFunction : CNodeWithLocation
/// Gets or sets the function's calling convention.
/// </summary>
[JsonPropertyName("calling_convention")]
public CFunctionCallingConvention CallingConvention { get; set; } = CFunctionCallingConvention.Cdecl;
public CFunctionCallingConvention CallingConvention { get; set; }

/// <summary>
/// Gets or sets the function's return type information.
Expand Down
5 changes: 5 additions & 0 deletions src/cs/production/c2ffi.Data/Nodes/CFunctionPointer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ public class CFunctionPointer : CNodeWithLocation
public ImmutableArray<CFunctionPointerParameter> Parameters { get; set; } =
ImmutableArray<CFunctionPointerParameter>.Empty;

/// <summary>
/// Gets or sets the function pointer's calling convention.
/// </summary>
public CFunctionCallingConvention CallingConvention { get; set; }

/// <inheritdoc />
[ExcludeFromCodeCoverage]
public override string ToString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ private CTypeInfo VisitTypeInternal(
return typeInfo;
}

if (location.IsSystem)
if (location.IsSystem && nodeKind != CNodeKind.FunctionPointer)
{
return typeInfo;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ internal bool CanVisitInternal(ExploreContext context, ExploreNodeInfo info)
return false;
}

if (info.Location.IsSystem)
if (info.Location.IsSystem && info.NodeKind != CNodeKind.FunctionPointer)
{
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ private CFunction Function(ExploreContext context, ExploreNodeInfo info)
{
Name = info.Name,
Location = info.Location,
CallingConvention = callingConvention,
ReturnTypeInfo = returnTypeInfo,
Parameters = parameters,
CallingConvention = callingConvention,
Comment = comment
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,14 @@ private static CFunctionPointer FunctionPointer(ExploreContext context, ExploreN
var typeInfo = context.VisitType(info.Type, info, nodeKind: CNodeKind.FunctionPointer);
var returnTypeInfo = FunctionPointerReturnType(context, info);
var parameters = FunctionPointerParameters(context, info);
var callingConvention = FunctionPointerCallingConvention(info.Type);
var comment = context.Comment(info.Cursor);

var result = new CFunctionPointer
{
Name = info.Name,
Location = info.Location,
CallingConvention = callingConvention,
TypeInfo = typeInfo,
ReturnTypeInfo = returnTypeInfo,
Parameters = parameters,
Expand Down Expand Up @@ -96,4 +98,18 @@ private static CFunctionPointerParameter FunctionPointerParameter(
};
return result;
}

private static CFunctionCallingConvention FunctionPointerCallingConvention(CXType type)
{
var callingConvention = clang_getFunctionTypeCallingConv(type);
var result = callingConvention switch
{
CXCallingConv.CXCallingConv_C => CFunctionCallingConvention.Cdecl,
CXCallingConv.CXCallingConv_X86StdCall => CFunctionCallingConvention.StdCall,
CXCallingConv.CXCallingConv_X86FastCall => CFunctionCallingConvention.FastCall,
_ => CFunctionCallingConvention.Unknown
};

return result;
}
}
1 change: 1 addition & 0 deletions src/cs/production/c2ffi.Tool/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Bottlenose Labs Inc. (https://github.com/bottlenoselabs). All rights reserved.
// Licensed under the MIT license. See LICENSE file in the Git repository root directory for full license information.

using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Hosting;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) Bottlenose Labs Inc. (https://github.com/bottlenoselabs). All rights reserved.
// Licensed under the MIT license. See LICENSE file in the Git repository root directory for full license information.

using c2ffi.Tests.Library.Models;
using FluentAssertions;

#pragma warning disable CA1707

namespace c2ffi.Tests.EndToEnd.Extract.FunctionPointers.function_pointer_void;

public class Test : ExtractFfiTest
{
private const string FunctionPointerName = "function_pointer_void";

[Fact]
public void FunctionPointer()
{
var ffis = GetTargetPlatformFfis(
$"src/c/tests/function_pointers/{FunctionPointerName}/config.json");
Assert.True(ffis.Length > 0);

foreach (var ffi in ffis)
{
FfiFunctionPointer(ffi);
}
}

private void FfiFunctionPointer(CTestFfiTargetPlatform ffi)
{
var alias = ffi.GetTypeAlias(FunctionPointerName);
alias.Name.Should().Be(FunctionPointerName);
alias.UnderlyingType.NodeKind.Should().Be("functionpointer");
alias.UnderlyingType.Name.Should().Be("void ()");
alias.UnderlyingType.SizeOf.Should().Be(8);
alias.UnderlyingType.AlignOf.Should().Be(8);
alias.UnderlyingType.InnerType.Should().BeNull();

var functionPointer = ffi.GetFunctionPointer("void ()");
functionPointer.Name.Should().Be("void ()");
functionPointer.CallingConvention.Should().Be("cdecl");
functionPointer.Parameters.Should().BeEmpty();
functionPointer.ReturnType.Name.Should().Be("void");
functionPointer.ReturnType.SizeOf.Should().BeNull();
functionPointer.ReturnType.AlignOf.Should().BeNull();
functionPointer.ReturnType.InnerType.Should().BeNull();
functionPointer.ReturnType.NodeKind.Should().Be("primitive");
}
}
Loading

0 comments on commit 1aed7a7

Please sign in to comment.