Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experiment: Use proper fn pointers for C# headers #167

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
132 changes: 18 additions & 114 deletions ffi_tests/generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,6 @@ public enum Bar_t : sbyte {
B = 42,
}

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe /* static */ delegate
void *
void_ptr_bool_fptr (
[MarshalAs(UnmanagedType.U1)]
bool _0);

/// <summary>
/// Hello, <c>World</c>!
/// </summary>
Expand All @@ -62,8 +55,7 @@ public unsafe struct next_generation_t {
/// <summary>
/// with function pointers and everything!
/// </summary>
[MarshalAs(UnmanagedType.FunctionPtr)]
public void_ptr_bool_fptr cb;
public delegate* unmanaged<bool, void *> cb;
}

/// <summary>
Expand All @@ -89,27 +81,18 @@ public unsafe partial class Ffi {
Int32 async_get_ft ();
}

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe /* static */ delegate
void
void_void_ptr_fptr (
void * _0);

/// <summary>
/// <c>Arc<dyn Send + Sync + Fn() -> Ret></c>
/// </summary>
[StructLayout(LayoutKind.Sequential, Size = 32)]
public unsafe struct ArcDynFn0_void_t {
public void * env_ptr;

[MarshalAs(UnmanagedType.FunctionPtr)]
public void_void_ptr_fptr call;
public delegate* unmanaged<void *, void> call;

[MarshalAs(UnmanagedType.FunctionPtr)]
public void_void_ptr_fptr release;
public delegate* unmanaged<void *, void> release;

[MarshalAs(UnmanagedType.FunctionPtr)]
public void_void_ptr_fptr retain;
public delegate* unmanaged<void *, void> retain;
}

public unsafe partial class Ffi {
Expand Down Expand Up @@ -226,16 +209,9 @@ Int32 read_foo (
foo_t /*const*/ * foo);
}

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe /* static */ delegate
UInt16
uint16_uint8_fptr (
byte _0);

public unsafe partial class Ffi {
[return: MarshalAs(UnmanagedType.FunctionPtr)]
[DllImport(RustLib, ExactSpelling = true)] public static unsafe extern
uint16_uint8_fptr returns_a_fn_ptr ();
delegate* unmanaged<byte, UInt16> returns_a_fn_ptr ();
}

/// <summary>
Expand Down Expand Up @@ -265,18 +241,6 @@ public struct Erased_t {
#pragma warning restore 0169
}

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe /* static */ delegate
void
void_Erased_ptr_fptr (
Erased_t * _0);

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe /* static */ delegate
Erased_t *
Erased_ptr_Erased_const_ptr_fptr (
Erased_t /*const*/ * _0);

/// <summary>
/// An FFI-safe <c>Poll<()></c>.
/// </summary>
Expand All @@ -285,20 +249,11 @@ public enum PollFuture_t : sbyte {
Pending = -1,
}

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe /* static */ delegate
PollFuture_t
PollFuture_Erased_ptr_Opaque_Context_ptr_fptr (
Erased_t * _0,
Opaque_Context_t * _1);

[StructLayout(LayoutKind.Sequential, Size = 16)]
public unsafe struct FfiFutureVTable_t {
[MarshalAs(UnmanagedType.FunctionPtr)]
public void_Erased_ptr_fptr release_vptr;
public delegate* unmanaged<Erased_t *, void> release_vptr;

[MarshalAs(UnmanagedType.FunctionPtr)]
public PollFuture_Erased_ptr_Opaque_Context_ptr_fptr dyn_poll;
public delegate* unmanaged<Erased_t *, Opaque_Context_t *, PollFuture_t> dyn_poll;
}

[StructLayout(LayoutKind.Sequential, Size = 24)]
Expand All @@ -308,45 +263,21 @@ public unsafe struct VirtualPtr__Erased_ptr_FfiFutureVTable_t {
public FfiFutureVTable_t vtable;
}

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe /* static */ delegate
VirtualPtr__Erased_ptr_FfiFutureVTable_t
VirtualPtr__Erased_ptr_FfiFutureVTable_Erased_const_ptr_VirtualPtr__Erased_ptr_FfiFutureVTable_fptr (
Erased_t /*const*/ * _0,
VirtualPtr__Erased_ptr_FfiFutureVTable_t _1);

/// <summary>
/// <c>Box<dyn 'static + Send + FnMut() -> Ret></c>
/// </summary>
[StructLayout(LayoutKind.Sequential, Size = 24)]
public unsafe struct BoxDynFnMut0_void_t {
public void * env_ptr;

[MarshalAs(UnmanagedType.FunctionPtr)]
public void_void_ptr_fptr call;
public delegate* unmanaged<void *, void> call;

[MarshalAs(UnmanagedType.FunctionPtr)]
public void_void_ptr_fptr free;
public delegate* unmanaged<void *, void> free;
}

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe /* static */ delegate
VirtualPtr__Erased_ptr_FfiFutureVTable_t
VirtualPtr__Erased_ptr_FfiFutureVTable_Erased_const_ptr_BoxDynFnMut0_void_fptr (
Erased_t /*const*/ * _0,
BoxDynFnMut0_void_t _1);

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe /* static */ delegate
void
void_Erased_const_ptr_VirtualPtr__Erased_ptr_FfiFutureVTable_fptr (
Erased_t /*const*/ * _0,
VirtualPtr__Erased_ptr_FfiFutureVTable_t _1);

[StructLayout(LayoutKind.Sequential, Size = 8)]
public unsafe struct DropGlueVTable_t {
[MarshalAs(UnmanagedType.FunctionPtr)]
public void_Erased_ptr_fptr release_vptr;
public delegate* unmanaged<Erased_t *, void> release_vptr;
}

[StructLayout(LayoutKind.Sequential, Size = 16)]
Expand All @@ -356,31 +287,19 @@ public unsafe struct VirtualPtr__Erased_ptr_DropGlueVTable_t {
public DropGlueVTable_t vtable;
}

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe /* static */ delegate
VirtualPtr__Erased_ptr_DropGlueVTable_t
VirtualPtr__Erased_ptr_DropGlueVTable_Erased_const_ptr_fptr (
Erased_t /*const*/ * _0);

[StructLayout(LayoutKind.Sequential, Size = 48)]
public unsafe struct FfiFutureExecutorVTable_t {
[MarshalAs(UnmanagedType.FunctionPtr)]
public void_Erased_ptr_fptr release_vptr;
public delegate* unmanaged<Erased_t *, void> release_vptr;

[MarshalAs(UnmanagedType.FunctionPtr)]
public Erased_ptr_Erased_const_ptr_fptr retain_vptr;
public delegate* unmanaged<Erased_t /*const*/ *, Erased_t *> retain_vptr;

[MarshalAs(UnmanagedType.FunctionPtr)]
public VirtualPtr__Erased_ptr_FfiFutureVTable_Erased_const_ptr_VirtualPtr__Erased_ptr_FfiFutureVTable_fptr dyn_spawn;
public delegate* unmanaged<Erased_t /*const*/ *, VirtualPtr__Erased_ptr_FfiFutureVTable_t, VirtualPtr__Erased_ptr_FfiFutureVTable_t> dyn_spawn;

[MarshalAs(UnmanagedType.FunctionPtr)]
public VirtualPtr__Erased_ptr_FfiFutureVTable_Erased_const_ptr_BoxDynFnMut0_void_fptr dyn_spawn_blocking;
public delegate* unmanaged<Erased_t /*const*/ *, BoxDynFnMut0_void_t, VirtualPtr__Erased_ptr_FfiFutureVTable_t> dyn_spawn_blocking;

[MarshalAs(UnmanagedType.FunctionPtr)]
public void_Erased_const_ptr_VirtualPtr__Erased_ptr_FfiFutureVTable_fptr dyn_block_on;
public delegate* unmanaged<Erased_t /*const*/ *, VirtualPtr__Erased_ptr_FfiFutureVTable_t, void> dyn_block_on;

[MarshalAs(UnmanagedType.FunctionPtr)]
public VirtualPtr__Erased_ptr_DropGlueVTable_Erased_const_ptr_fptr dyn_enter;
public delegate* unmanaged<Erased_t /*const*/ *, VirtualPtr__Erased_ptr_DropGlueVTable_t> dyn_enter;
}

[StructLayout(LayoutKind.Sequential, Size = 56)]
Expand All @@ -396,22 +315,14 @@ Int32 test_spawner (
VirtualPtr__Erased_ptr_FfiFutureExecutorVTable_t executor);
}

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe /* static */ delegate
void
void_void_ptr_char_const_ptr_fptr (
void * _0,
byte /*const*/ * _1);

/// <summary>
/// <c>&'lt mut (dyn 'lt + Send + FnMut(A1) -> Ret)</c>
/// </summary>
[StructLayout(LayoutKind.Sequential, Size = 16)]
public unsafe struct RefDynFnMut1_void_char_const_ptr_t {
public void * env_ptr;

[MarshalAs(UnmanagedType.FunctionPtr)]
public void_void_ptr_char_const_ptr_fptr call;
public delegate* unmanaged<void *, byte /*const*/ *, void> call;
}

public unsafe partial class Ffi {
Expand All @@ -426,18 +337,11 @@ void with_concat (
RefDynFnMut1_void_char_const_ptr_t cb);
}

[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe /* static */ delegate
void
void_foo_ptr_fptr (
foo_t * _0);

public unsafe partial class Ffi {
[return: MarshalAs(UnmanagedType.U1)]
[DllImport(RustLib, ExactSpelling = true)] public static unsafe extern
bool with_foo (
[MarshalAs(UnmanagedType.FunctionPtr)]
void_foo_ptr_fptr cb);
delegate* unmanaged<foo_t *, void> cb);
}


Expand Down
39 changes: 26 additions & 13 deletions ffi_tests/tests/csharp/Tests.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using FfiTests;

static class Tests
{
public unsafe delegate R WithUTF8Continuation<R>(byte * _);
Expand Down Expand Up @@ -63,19 +66,29 @@ static void Main(string[] _)

// test with_concat
unsafe {
string s = null;
var s = new List<string>();
var handle = GCHandle.Alloc(s);

[UnmanagedCallersOnly()]
static unsafe void cb(void * ctx, byte /*const*/ * p) {
GCHandle handle = GCHandle.FromIntPtr((IntPtr) ctx);
var s = (List<string>)handle.Target;
s.Add(Marshal.PtrToStringUTF8((IntPtr)p));
}

s1.WithUTF8(p1 => s2.WithUTF8(p2 => {
Ffi.with_concat(
p1,
p2,
new RefDynFnMut1_void_char_const_ptr_t { env_ptr = (void *) 0xbad00, call = (void * _,
byte * p) => {
s = Marshal.PtrToStringUTF8((IntPtr)p);
},
});
new RefDynFnMut1_void_char_const_ptr_t {
env_ptr = (void *)(IntPtr)handle,
call = &cb,
}
);
return 0;
}));
Trace.Assert(s == s1 + s2);
handle.Free();
Trace.Assert(s[0] == s1 + s2);
}

// test max
Expand Down Expand Up @@ -110,16 +123,16 @@ static void Main(string[] _)
Ffi.free_foo(foo);
Ffi.free_foo(null);

bool called = false;
Ffi.with_foo((foo_t * foo) => {
[UnmanagedCallersOnly()]
static unsafe void cb(foo_t * foo) {
Trace.Assert(
Ffi.read_foo(foo)
==
42
);
called = true;
});
Trace.Assert(called);
}

Ffi.with_foo(&cb);
}

// test constant
Expand Down
70 changes: 17 additions & 53 deletions src/layout/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,64 +298,28 @@ const _: () = { macro_rules! impl_CTypes {
Ret::define_self(&crate::headers::languages::CSharp, definer)?; $(
$An::define_self(&crate::headers::languages::CSharp, definer)?; $(
$Ai::define_self(&crate::headers::languages::CSharp, definer)?; )*)?
let ref me = Self::name(&crate::headers::languages::CSharp).to_string();
let ref mut _arg = {
let mut iter = (0 ..).map(|c| format!("_{}", c));
move || iter.next().unwrap()
};
definer.define_once(me, &mut |definer| writeln!(definer.out(),
concat!(
// IIUC,
// - For 32-bits / x86,
// Rust's extern "C" is the same as C#'s (default) Winapi:
// "cdecl" for Linux, and "stdcall" for Windows.
//
// - For everything else, this is param is ignored.
// I guess because both OSes agree on the calling convention?
"[UnmanagedFunctionPointer(CallingConvention.Winapi)]\n",

"{ret_marshaler}public unsafe /* static */ delegate\n",
" {Ret}\n",
" {me} (", $("\n",
" {}{", stringify!($An), "}", $(",\n",
" {}{", stringify!($Ai), "}", )*)?
");\n"
),$(
$An::csharp_marshaler()
.map(|m| format!("[MarshalAs({})]\n ", m))
.as_deref()
.unwrap_or("")
, $(
$Ai::csharp_marshaler()
.map(|m| format!("[MarshalAs({})]\n ", m))
.as_deref()
.unwrap_or("")
, )*)?
me = me,
ret_marshaler =
Ret::csharp_marshaler()
.map(|m| format!("[return: MarshalAs({})]\n", m))
.as_deref()
.unwrap_or("")
,
Ret = Ret::name(&crate::headers::languages::CSharp), $(
$An = $An::name_wrapping_var(&crate::headers::languages::CSharp, &_arg()), $(
$Ai = $Ai::name_wrapping_var(&crate::headers::languages::CSharp, &_arg()), )*)?
))
Ok(())
}

fn csharp_ty ()
-> rust::String
{
Self::c_short_name().to_string()
}

fn legacy_csharp_marshaler ()
-> Option<rust::String>
{
// This assumes the calling convention from the above
// `UnmanagedFunctionPointer` attribute.
Some("UnmanagedType.FunctionPtr".into())
use ::std::fmt::Write;
let mut ret = String::new();
let fmt = &mut ret;
// FIXME: `managed`, `unmanaged`, or `unmanaged[…]`? For now, let's go with
// `unmanaged`-that-defaults-to-that-of-the-platform.
//
// Note: in order for C# to feed such a function pointer, it has to declare,
// with an annotation of:
// - `[UnmanagedCallersOnly()]` (for the default case), or
// - `[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]` otherwise,
// a `static unsafe … cb(…) { … }` function, and then feed `&cb`.
fmt.push_str("delegate* unmanaged<"); $(
write!(fmt, "{}, ", $An::name(&crate::headers::languages::CSharp)).unwrap(); $(
write!(fmt, "{}, ", $Ai::name(&crate::headers::languages::CSharp)).unwrap(); )*)?
write!(fmt, "{}>", Ret::name(&crate::headers::languages::CSharp)).unwrap();
ret
}
}
} type OPAQUE_KIND = OpaqueKind::Concrete; }
Expand Down