use crate::{HasStaticTypeInfo, TypeInfo};
use std::{
ffi::{c_void, CStr, CString},
fmt::{self, Formatter},
os::raw::c_char,
ptr, slice, str,
};
#[repr(C)]
#[derive(Clone)]
pub struct FunctionDefinition {
pub prototype: FunctionPrototype,
pub fn_ptr: *const c_void,
}
#[repr(C)]
#[derive(Clone)]
pub struct FunctionPrototype {
pub name: *const c_char,
pub signature: FunctionSignature,
}
#[repr(C)]
#[derive(Clone)]
pub struct FunctionSignature {
pub(crate) arg_types: *const *const TypeInfo,
pub(crate) return_type: *const TypeInfo,
pub num_arg_types: u16,
}
pub struct FunctionDefinitionStorage {
_name: CString,
_type_infos: Vec<&'static TypeInfo>,
}
unsafe impl Send for FunctionDefinition {}
unsafe impl Sync for FunctionDefinition {}
impl FunctionPrototype {
pub fn name(&self) -> &str {
unsafe { str::from_utf8_unchecked(CStr::from_ptr(self.name).to_bytes()) }
}
}
impl fmt::Display for FunctionPrototype {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "fn {}(", self.name())?;
for (i, arg) in self.signature.arg_types().iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", arg)?;
}
write!(f, ")")?;
if let Some(ret_type) = self.signature.return_type() {
write!(f, ":{}", ret_type)?
}
Ok(())
}
}
unsafe impl Send for FunctionPrototype {}
unsafe impl Sync for FunctionPrototype {}
impl FunctionSignature {
pub fn arg_types(&self) -> &[&TypeInfo] {
if self.num_arg_types == 0 {
&[]
} else {
unsafe {
slice::from_raw_parts(
self.arg_types.cast::<&TypeInfo>(),
self.num_arg_types as usize,
)
}
}
}
pub fn return_type(&self) -> Option<&TypeInfo> {
unsafe { self.return_type.as_ref() }
}
}
impl fmt::Display for FunctionSignature {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "fn(")?;
for (i, arg) in self.arg_types().iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", arg)?;
}
write!(f, ")")?;
if let Some(ret_type) = self.return_type() {
write!(f, ":{}", ret_type)?
}
Ok(())
}
}
impl PartialEq for FunctionSignature {
fn eq(&self, other: &Self) -> bool {
self.return_type() == other.return_type()
&& self.arg_types().len() == other.arg_types().len()
&& self
.arg_types()
.iter()
.zip(other.arg_types().iter())
.all(|(a, b)| PartialEq::eq(a, b))
}
}
impl Eq for FunctionSignature {}
unsafe impl Send for FunctionSignature {}
unsafe impl Sync for FunctionSignature {}
impl FunctionDefinitionStorage {
pub fn new_function(
name: &str,
args: &[&'static TypeInfo],
ret: Option<&'static TypeInfo>,
fn_ptr: *const c_void,
) -> (FunctionDefinition, FunctionDefinitionStorage) {
let name = CString::new(name).unwrap();
let type_infos: Vec<&'static TypeInfo> = args.iter().copied().collect();
let num_arg_types = type_infos.len() as u16;
let return_type = if let Some(ty) = ret {
ty as *const _
} else {
ptr::null()
};
let fn_info = FunctionDefinition {
prototype: FunctionPrototype {
name: name.as_ptr(),
signature: FunctionSignature {
arg_types: type_infos.as_ptr() as *const *const _,
return_type,
num_arg_types,
},
},
fn_ptr,
};
let fn_storage = FunctionDefinitionStorage {
_name: name,
_type_infos: type_infos,
};
(fn_info, fn_storage)
}
}
pub trait IntoFunctionDefinition {
fn into<S: AsRef<str>>(self, name: S) -> (FunctionDefinition, FunctionDefinitionStorage);
}
macro_rules! into_function_info_impl {
($(
extern "C" fn($($T:ident),*) -> $R:ident;
)+) => {
$(
impl<$R: HasStaticTypeInfo, $($T: HasStaticTypeInfo,)*> IntoFunctionDefinition
for extern "C" fn($($T),*) -> $R
{
fn into<S: AsRef<str>>(self, name: S) -> (FunctionDefinition, FunctionDefinitionStorage) {
FunctionDefinitionStorage::new_function(
name.as_ref(),
&[$($T::type_info(),)*],
Some($R::type_info()),
self as *const std::ffi::c_void,
)
}
}
impl<$($T: HasStaticTypeInfo,)*> IntoFunctionDefinition
for extern "C" fn($($T),*)
{
fn into<S: AsRef<str>>(self, name: S) -> (FunctionDefinition, FunctionDefinitionStorage) {
FunctionDefinitionStorage::new_function(
name.as_ref(),
&[$($T::type_info(),)*],
None,
self as *const std::ffi::c_void,
)
}
}
)+
}
}
into_function_info_impl! {
extern "C" fn() -> R;
extern "C" fn(A) -> R;
extern "C" fn(A, B) -> R;
extern "C" fn(A, B, C) -> R;
extern "C" fn(A, B, C, D) -> R;
extern "C" fn(A, B, C, D, E) -> R;
extern "C" fn(A, B, C, D, E, F) -> R;
extern "C" fn(A, B, C, D, E, F, G) -> R;
extern "C" fn(A, B, C, D, E, F, G, H) -> R;
extern "C" fn(A, B, C, D, E, F, G, H, I) -> R;
extern "C" fn(A, B, C, D, E, F, G, H, I, J) -> R;
}
#[cfg(test)]
mod tests {
use crate::{
test_utils::{
fake_fn_prototype, fake_fn_signature, fake_type_info, FAKE_FN_NAME, FAKE_TYPE_NAME,
},
TypeInfoData,
};
use std::ffi::CString;
#[test]
fn test_fn_prototype_name() {
let fn_name = CString::new(FAKE_FN_NAME).expect("Invalid fake fn name.");
let fn_signature = fake_fn_prototype(&fn_name, &[], None);
assert_eq!(fn_signature.name(), FAKE_FN_NAME);
}
#[test]
fn test_fn_signature_arg_types_none() {
let arg_types = &[];
let fn_signature = fake_fn_signature(arg_types, None);
assert_eq!(fn_signature.arg_types(), arg_types);
}
#[test]
fn test_fn_signature_arg_types_some() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let type_info = fake_type_info(&type_name, 1, 1, TypeInfoData::Primitive);
let arg_types = &[&type_info];
let fn_signature = fake_fn_signature(arg_types, None);
assert_eq!(fn_signature.arg_types(), arg_types);
}
#[test]
fn test_fn_signature_return_type_none() {
let return_type = None;
let fn_signature = fake_fn_signature(&[], return_type);
assert_eq!(fn_signature.return_type(), return_type);
}
#[test]
fn test_fn_signature_return_type_some() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let type_info = fake_type_info(&type_name, 1, 1, TypeInfoData::Primitive);
let return_type = Some(&type_info);
let fn_signature = fake_fn_signature(&[], return_type);
assert_eq!(fn_signature.return_type(), return_type);
}
}