diff --git a/csbindgen-tests/src/lib.rs b/csbindgen-tests/src/lib.rs index be42290..2ce0ac4 100644 --- a/csbindgen-tests/src/lib.rs +++ b/csbindgen-tests/src/lib.rs @@ -14,6 +14,28 @@ mod lz4; #[allow(non_camel_case_types)] mod lz4_ffi; +#[no_mangle] +pub extern "C" fn callback_test(cb: extern "C" fn(a: i32) -> i32) -> i32 { + cb(100) +} + +#[no_mangle] +pub extern "C" fn nullable_callback_test(cb: Option i32>) -> i32 { + match cb { + Some(f) => f(100), + None => -1, + } +} + +#[no_mangle] +pub extern "C" fn callback_test2() -> extern "C" fn(a: i32) -> i32 { + callback +} + +extern "C" fn callback(a: i32) -> i32 { + a * a +} + #[no_mangle] #[allow(improper_ctypes_definitions)] pub extern "C" fn ignore_nop() -> (i32, i32) { @@ -31,12 +53,6 @@ pub extern "C" fn my_add(x: i32, y: i32) -> i32 { x + y } -#[no_mangle] -pub extern "C" fn callback_test(cb: extern fn(a: i32) -> i32) -> i32 { - // Fn - cb(100) -} - #[no_mangle] pub extern "C" fn create_counter_context() -> *mut c_void { let ctx = Box::new(CounterContext { diff --git a/csbindgen/src/emitter.rs b/csbindgen/src/emitter.rs index 8c90036..5fe7657 100644 --- a/csbindgen/src/emitter.rs +++ b/csbindgen/src/emitter.rs @@ -25,7 +25,7 @@ pub fn emit_rust_method(list: &Vec, options: &BindgenOptions) -> S format!( " {}: {}", p.name, - p.rust_type.to_string(method_type_path) + p.rust_type.to_rust_string(method_type_path) ) }) .collect::>() @@ -33,7 +33,7 @@ pub fn emit_rust_method(list: &Vec, options: &BindgenOptions) -> S let return_line = match &item.return_type { None => "".to_string(), - Some(v) => format!(" -> {}", v.to_string(method_type_path)), + Some(v) => format!(" -> {}", v.to_rust_string(method_type_path)), }; let parameter_only_names = item diff --git a/csbindgen/src/parser.rs b/csbindgen/src/parser.rs index 163d46f..ca7e67f 100644 --- a/csbindgen/src/parser.rs +++ b/csbindgen/src/parser.rs @@ -1,8 +1,6 @@ use crate::{builder::BindgenOptions, type_meta::*}; -use std::{ - collections::{HashMap, HashSet}, -}; -use syn::{ForeignItem, Item, Pat, ReturnType}; +use std::collections::{HashMap, HashSet}; +use syn::{ForeignItem, Item, Pat, ReturnType}; enum FnItem { ForeignItem(syn::ForeignItemFn), @@ -120,8 +118,8 @@ pub fn collect_type_alias(ast: &syn::File) -> Vec<(String, RustType)> { name, RustType { type_name: alias.to_string(), - type_kind: TypeKind::Normal - } + type_kind: TypeKind::Normal, + }, )); } } @@ -217,22 +215,46 @@ fn parse_type(t: &syn::Type) -> RustType { if let syn::Type::Path(path) = &*t.elem { return RustType { type_name: path.path.segments.last().unwrap().ident.to_string(), - type_kind: TypeKind::Pointer(if has_const { PointerType::ConstPointer } else { PointerType::MutPointer }) + type_kind: TypeKind::Pointer(if has_const { + PointerType::ConstPointer + } else { + PointerType::MutPointer + }), }; } else if let syn::Type::Ptr(t) = &*t.elem { if let syn::Type::Path(path) = &*t.elem { return RustType { type_name: path.path.segments.last().unwrap().ident.to_string(), - type_kind: TypeKind::Pointer(if has_const { PointerType::ConstPointerPointer } else { PointerType::MutPointerPointer }) + type_kind: TypeKind::Pointer(if has_const { + PointerType::ConstPointerPointer + } else { + PointerType::MutPointerPointer + }), }; } - } + } } syn::Type::Path(t) => { - return RustType{ - type_name:t.path.segments.last().unwrap().ident.to_string(), - type_kind: TypeKind::Normal - } + let last_segment = t.path.segments.last().unwrap(); + if let syn::PathArguments::AngleBracketed(x) = &last_segment.arguments { + // generics, only supports Option<> for null function pointer + if last_segment.ident.to_string() == "Option" { + if let Some(x) = x.args.first() { + if let syn::GenericArgument::Type(t) = x { + let rust_type = parse_type(t); + return RustType { + type_name: "Option".to_string(), + type_kind: TypeKind::Option(Box::new(rust_type)), + }; + } + } + } + } else { + return RustType { + type_name: last_segment.ident.to_string(), + type_kind: TypeKind::Normal, + }; + } } syn::Type::Array(t) => { let mut digits = "".to_string(); @@ -256,8 +278,29 @@ fn parse_type(t: &syn::Type) -> RustType { }; }; } - syn::Type::BareFn(_) => { - //todo!(); + syn::Type::BareFn(t) => { + let mut parameters = Vec::new(); + + for arg in t.inputs.iter() { + let rust_type = parse_type(&arg.ty); + + let name = if let Some((ident, _)) = &arg.name { + ident.to_string() + } else { + "".to_string() + }; + parameters.push(Parameter { name, rust_type }); + } + + let ret = match &t.output { + syn::ReturnType::Default => None, + syn::ReturnType::Type(_, t) => Some(Box::new(parse_type(&t))), + }; + + return RustType { + type_name: "extern \"C\" fn".to_string(), + type_kind: TypeKind::Function(parameters, ret), + }; } _ => {} }; diff --git a/csbindgen/src/type_meta.rs b/csbindgen/src/type_meta.rs index 32b13a0..9609e92 100644 --- a/csbindgen/src/type_meta.rs +++ b/csbindgen/src/type_meta.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap}; use crate::builder::BindgenOptions; @@ -51,8 +51,9 @@ pub struct RustType { pub enum TypeKind { Normal, Pointer(PointerType), - FixedArray(String, Option), // digits - Function(Vec, Option>), // parameter, return + FixedArray(String, Option), // digits + Function(Vec, Option>), // parameter, return + Option(Box), } #[derive(Clone, Debug)] @@ -71,7 +72,7 @@ pub struct RustStruct { } impl RustType { - pub fn to_string(&self, type_path: &str) -> String { + pub fn to_rust_string(&self, type_path: &str) -> String { let mut sb = String::new(); fn emit_pointer(sb: &mut String, p: &PointerType) { @@ -118,8 +119,25 @@ impl RustType { sb.push_str(digits.as_str()); sb.push(']'); } - Function(x, y) => { - todo!(); + Function(parameters, return_type) => { + emit_type_name(&mut sb); // extern fn + sb.push('('); + let params = parameters + .iter() + .map(|x| format!("{}: {}", x.escape_name(), x.rust_type.to_rust_string(type_path))) + .collect::>() + .join(", "); + sb.push_str(params.as_str()); + sb.push(')'); + if let Some(t) = return_type { + sb.push_str(" -> "); + sb.push_str(t.to_rust_string(type_path).as_str()); + } + }, + Option(inner) => { + sb.push_str("Option<"); + sb.push_str(inner.to_rust_string(type_path).as_str()); + sb.push('>'); } }; @@ -180,7 +198,7 @@ impl RustType { let mut sb = String::new(); - match self.type_kind { + match &self.type_kind { TypeKind::FixedArray(_, _) => { sb.push_str("fixed "); @@ -194,6 +212,27 @@ impl RustType { sb.push_str(type_name.as_str()); } + TypeKind::Function(parameters, return_type) => { + sb.push_str("delegate* unmanaged[Cdecl]"); + sb.push('<'); + for p in parameters { + sb.push_str(&p.rust_type.to_csharp_string(options, alias_map)); + sb.push_str(", "); + } + match return_type { + Some(x) => { + sb.push_str(&x.to_csharp_string(options, alias_map)); + } + None => { + sb.push_str("void"); + } + }; + sb.push('>'); + }, + TypeKind::Option(inner) =>{ + // function pointer can not annotate ? so emit inner only + sb.push_str(inner.to_csharp_string(options, alias_map).as_str()); + }, _ => { sb.push_str(convert_type_name(use_type.type_name.as_str(), options).as_str()); @@ -229,6 +268,6 @@ impl RustType { impl std::fmt::Display for RustType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.to_string("")) + write!(f, "{}", self.to_rust_string("")) } } diff --git a/dotnet-sandbox/Program.cs b/dotnet-sandbox/Program.cs index 22d4c74..abd25e6 100644 --- a/dotnet-sandbox/Program.cs +++ b/dotnet-sandbox/Program.cs @@ -13,16 +13,24 @@ using System.Text; unsafe { - // LibRust.call_bindgen(); + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })] + static int Method(int x) => x * x; + + var tako = LibRust.callback_test(&Method); + + + var n = LibRust.nullable_callback_test(null); + + Console.WriteLine(n); - var ctx = LibRust.create_counter_context(); - LibRust.insert_counter_context(ctx, 10); - LibRust.insert_counter_context(ctx, 20); - LibRust.insert_counter_context(ctx, 20); - LibRust.insert_counter_context(ctx, 30); - LibRust.insert_counter_context(ctx, 99); - LibRust.delete_counter_context(ctx); + //var ctx = LibRust.create_counter_context(); + //LibRust.insert_counter_context(ctx, 10); + //LibRust.insert_counter_context(ctx, 20); + //LibRust.insert_counter_context(ctx, 20); + //LibRust.insert_counter_context(ctx, 30); + //LibRust.insert_counter_context(ctx, 99); + //LibRust.delete_counter_context(ctx); //var cString = LibRust.alloc_c_string(); //var u8String = LibRust.alloc_u8_string(); diff --git a/dotnet-sandbox/method_call.cs b/dotnet-sandbox/method_call.cs index 1057f7e..89889ee 100644 --- a/dotnet-sandbox/method_call.cs +++ b/dotnet-sandbox/method_call.cs @@ -11,6 +11,18 @@ namespace CsBindgen { const string __DllName = "csbindgen_tests"; + [DllImport(__DllName, EntryPoint = "callback_test", CallingConvention = CallingConvention.Cdecl)] + public static extern int callback_test(delegate* unmanaged[Cdecl] cb); + + [DllImport(__DllName, EntryPoint = "nullable_callback_test", CallingConvention = CallingConvention.Cdecl)] + public static extern int nullable_callback_test(delegate* unmanaged[Cdecl] cb); + + [DllImport(__DllName, EntryPoint = "callback_test2", CallingConvention = CallingConvention.Cdecl)] + public static extern delegate* unmanaged[Cdecl] callback_test2(); + + [DllImport(__DllName, EntryPoint = "callback", CallingConvention = CallingConvention.Cdecl)] + public static extern int callback(int a); + [DllImport(__DllName, EntryPoint = "nop", CallingConvention = CallingConvention.Cdecl)] public static extern void nop();