Make an UnsafeExternCFn struct to overcome fn pointer hrtb
This commit is contained in:
parent
c39106b790
commit
2352ba66d4
@ -1,6 +1,7 @@
|
||||
use crate::{
|
||||
__internal::{display, type_id},
|
||||
Cdef, CdefBuilder, FfiReturnConvention, Metatype, MetatypeBuilder, ToFfi, Type, TypeBuilder,
|
||||
UnsafeExternCFn,
|
||||
};
|
||||
use luaify::luaify;
|
||||
use std::{
|
||||
@ -147,8 +148,8 @@ unsafe impl<F: Future<Output: ToFfi> + 'static> Type for lua_future<F> {
|
||||
unsafe impl<F: Future<Output: ToFfi> + 'static> Cdef for lua_future<F> {
|
||||
fn build(s: &mut CdefBuilder) {
|
||||
s.field_opaque(mem::offset_of!(Self, take)) // opaque .sig, .poll and .state
|
||||
.field::<unsafe extern "C" fn(*mut Self) -> <F::Output as ToFfi>::To>("__take")
|
||||
.field::<unsafe extern "C" fn(*mut Self)>("__drop");
|
||||
.field::<UnsafeExternCFn<(&mut Self,), <F::Output as ToFfi>::To>>("__take")
|
||||
.field::<UnsafeExternCFn<(&mut Self,), ()>>("__drop");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,11 +4,12 @@ use std::{
|
||||
collections::HashSet,
|
||||
ffi::{c_double, c_float, c_void},
|
||||
fmt::{self, Display, Formatter, Write},
|
||||
marker::PhantomData,
|
||||
mem, slice,
|
||||
};
|
||||
|
||||
pub mod future;
|
||||
pub mod option;
|
||||
// pub mod option;
|
||||
pub mod string;
|
||||
|
||||
#[doc(hidden)]
|
||||
@ -140,8 +141,8 @@ pub struct Registry {
|
||||
impl Registry {
|
||||
pub fn new() -> Self {
|
||||
let mut s = Self::default();
|
||||
s.declare::<extern "C" fn(ptr: *const c_void)>(KEEP_FN);
|
||||
s.declare::<unsafe extern "C" fn(ptr: *const u8, len: usize) -> bool>(IS_UTF8_FN);
|
||||
s.declare::<UnsafeExternCFn<(*const c_void,), ()>>(KEEP_FN);
|
||||
s.declare::<UnsafeExternCFn<(*const u8, usize), bool>>(IS_UTF8_FN);
|
||||
s
|
||||
}
|
||||
|
||||
@ -542,12 +543,11 @@ unsafe impl ToFfi for () {
|
||||
conv == FfiReturnConvention::Void,
|
||||
"void type cannot be instantiated"
|
||||
);
|
||||
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_copy_primitive {
|
||||
macro_rules! impl_primitive_abi {
|
||||
($rtype:ty, $ctype:expr, $ltype:expr $(, $unwrap:expr)?) => {
|
||||
impl_primitive!($rtype, $ctype);
|
||||
|
||||
@ -555,8 +555,8 @@ macro_rules! impl_copy_primitive {
|
||||
// SAFETY: Primitive types are always copyable so we can pass and return them by value.
|
||||
//
|
||||
unsafe impl FromFfi for $rtype {
|
||||
type From = $rtype;
|
||||
type FromArg = $rtype;
|
||||
type From = Self;
|
||||
type FromArg = Self;
|
||||
|
||||
fn prelude(arg: &str) -> impl Display {
|
||||
display!(r#"assert(type({arg}) == "{0}", "{0} expected in argument '{arg}', got " .. type({arg})); "#, $ltype)
|
||||
@ -572,7 +572,7 @@ macro_rules! impl_copy_primitive {
|
||||
}
|
||||
|
||||
unsafe impl ToFfi for $rtype {
|
||||
type To = $rtype;
|
||||
type To = Self;
|
||||
|
||||
fn convert(self) -> Self::To {
|
||||
self
|
||||
@ -585,7 +585,7 @@ macro_rules! impl_copy_primitive {
|
||||
FfiReturnConvention::Void => unreachable!(),
|
||||
FfiReturnConvention::ByValue => {},
|
||||
// if a primitive type for some reason gets returned by out-param, unwrap
|
||||
// the cdata containing the value and convert it to the equivalent lua value
|
||||
// the cdata containing the value to convert it to the equivalent lua value
|
||||
FfiReturnConvention::ByOutParam => { $(write!(f, "{ret} = {}; ", $unwrap(ret))?;)? },
|
||||
}
|
||||
}))
|
||||
@ -594,147 +594,98 @@ macro_rules! impl_copy_primitive {
|
||||
};
|
||||
}
|
||||
|
||||
impl_copy_primitive!(bool, "bool", "boolean", |n| display!("{n} ~= 0"));
|
||||
impl_copy_primitive!(u8, "uint8_t", "number", |n| display!("tonumber({n})"));
|
||||
impl_copy_primitive!(u16, "uint16_t", "number", |n| display!("tonumber({n})"));
|
||||
impl_copy_primitive!(u32, "uint32_t", "number", |n| display!("tonumber({n})"));
|
||||
impl_copy_primitive!(u64, "uint64_t", "number");
|
||||
impl_copy_primitive!(usize, "uintptr_t", "number");
|
||||
impl_copy_primitive!(i8, "int8_t", "number", |n| display!("tonumber({n})"));
|
||||
impl_copy_primitive!(i16, "int16_t", "number", |n| display!("tonumber({n})"));
|
||||
impl_copy_primitive!(i32, "int32_t", "number", |n| display!("tonumber({n})"));
|
||||
impl_copy_primitive!(i64, "int64_t", "number");
|
||||
impl_copy_primitive!(isize, "intptr_t", "number");
|
||||
impl_copy_primitive!(c_float, "float", "number", |n| display!("tonumber({n})"));
|
||||
impl_copy_primitive!(c_double, "double", "number", |n| display!("tonumber({n})"));
|
||||
impl_primitive_abi!(bool, "bool", "boolean", |n| display!("{n} ~= 0"));
|
||||
impl_primitive_abi!(u8, "uint8_t", "number", |n| display!("tonumber({n})"));
|
||||
impl_primitive_abi!(u16, "uint16_t", "number", |n| display!("tonumber({n})"));
|
||||
impl_primitive_abi!(u32, "uint32_t", "number", |n| display!("tonumber({n})"));
|
||||
impl_primitive_abi!(u64, "uint64_t", "number");
|
||||
impl_primitive_abi!(usize, "uintptr_t", "number");
|
||||
impl_primitive_abi!(i8, "int8_t", "number", |n| display!("tonumber({n})"));
|
||||
impl_primitive_abi!(i16, "int16_t", "number", |n| display!("tonumber({n})"));
|
||||
impl_primitive_abi!(i32, "int32_t", "number", |n| display!("tonumber({n})"));
|
||||
impl_primitive_abi!(i64, "int64_t", "number");
|
||||
impl_primitive_abi!(isize, "intptr_t", "number");
|
||||
impl_primitive_abi!(c_float, "float", "number", |n| display!("tonumber({n})"));
|
||||
impl_primitive_abi!(c_double, "double", "number", |n| display!("tonumber({n})"));
|
||||
|
||||
unsafe impl<T: Type> Type for *const T {
|
||||
fn name() -> impl Display {
|
||||
display!("const_{}_ptr", T::name())
|
||||
}
|
||||
macro_rules! impl_const_ptr {
|
||||
($ty:ty) => {
|
||||
unsafe impl<T: Type> Type for $ty {
|
||||
fn name() -> impl Display {
|
||||
display!("const_{}_ptr", T::name())
|
||||
}
|
||||
|
||||
fn cdecl(name: impl Display) -> impl Display {
|
||||
T::cdecl(display!("const *{name}"))
|
||||
}
|
||||
fn cdecl(name: impl Display) -> impl Display {
|
||||
T::cdecl(display!("const *{name}"))
|
||||
}
|
||||
|
||||
fn build(b: &mut TypeBuilder) {
|
||||
b.include::<T>();
|
||||
}
|
||||
fn build(b: &mut TypeBuilder) {
|
||||
b.include::<T>();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
unsafe impl<T: Type> Type for *mut T {
|
||||
fn name() -> impl Display {
|
||||
display!("{}_ptr", T::name())
|
||||
}
|
||||
impl_const_ptr!(*const T);
|
||||
impl_const_ptr!(&T);
|
||||
impl_const_ptr!(Option<&T>);
|
||||
|
||||
fn cdecl(name: impl Display) -> impl Display {
|
||||
T::cdecl(display!("*{name}"))
|
||||
}
|
||||
macro_rules! impl_mut_ptr {
|
||||
($ty:ty) => {
|
||||
unsafe impl<T: Type> Type for $ty {
|
||||
fn name() -> impl Display {
|
||||
display!("{}_ptr", T::name())
|
||||
}
|
||||
|
||||
fn build(b: &mut TypeBuilder) {
|
||||
b.include::<T>();
|
||||
}
|
||||
fn cdecl(name: impl Display) -> impl Display {
|
||||
T::cdecl(display!("*{name}"))
|
||||
}
|
||||
|
||||
fn build(b: &mut TypeBuilder) {
|
||||
b.include::<T>();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_mut_ptr!(*mut T);
|
||||
impl_mut_ptr!(&mut T);
|
||||
impl_mut_ptr!(Option<&mut T>);
|
||||
|
||||
//
|
||||
// SAFETY: Pass by value for pointers, which maps to a `cdata` argument in lua containing either:
|
||||
// * the pointer value itself (`T *`), or
|
||||
// * the pointer to the base of the cdata payload (`T`).
|
||||
//
|
||||
unsafe impl<T: Type> FromFfi for *const T {
|
||||
type From = *const T;
|
||||
type FromArg = *const T;
|
||||
// * a reference which gets converted to a pointer (`T &` cdata), or
|
||||
// * the pointer value itself (`T *` cdata), or
|
||||
// * the pointer to the base of the cdata payload (`T` cdata).
|
||||
//
|
||||
// LuaJIT will check for pointer compatibility automatically.
|
||||
//
|
||||
macro_rules! impl_ptr_fromabi {
|
||||
($ty:ty) => {
|
||||
unsafe impl<T: Type> FromFfi for $ty {
|
||||
type From = Self;
|
||||
type FromArg = Self;
|
||||
|
||||
fn convert(from: Self::From) -> Self {
|
||||
from
|
||||
}
|
||||
fn convert(from: Self::From) -> Self {
|
||||
from
|
||||
}
|
||||
|
||||
fn convert_arg(from: Self::FromArg) -> Self {
|
||||
from
|
||||
}
|
||||
fn convert_arg(from: Self::FromArg) -> Self {
|
||||
from
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
unsafe impl<T: Type> FromFfi for *mut T {
|
||||
type From = *mut T;
|
||||
type FromArg = *mut T;
|
||||
impl_ptr_fromabi!(*const T);
|
||||
impl_ptr_fromabi!(*mut T);
|
||||
impl_ptr_fromabi!(Option<&T>);
|
||||
impl_ptr_fromabi!(Option<&mut T>);
|
||||
|
||||
fn convert(from: Self::From) -> Self {
|
||||
from
|
||||
}
|
||||
|
||||
fn convert_arg(from: Self::FromArg) -> Self {
|
||||
from
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// SAFETY: Return by value for pointers, which maps to a `cdata` return in lua containing the
|
||||
// pointer (`T *`). We also map null pointers to `nil` for convenience (otherwise it's still a cdata
|
||||
// value containing a null pointer)
|
||||
//
|
||||
unsafe impl<T: Type> ToFfi for *const T {
|
||||
type To = *const T;
|
||||
|
||||
fn convert(self) -> Self::To {
|
||||
self
|
||||
}
|
||||
|
||||
fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display {
|
||||
display!("if {ret} == nil then {ret} = nil; end; ")
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Type> ToFfi for *mut T {
|
||||
type To = *mut T;
|
||||
|
||||
fn convert(self) -> Self::To {
|
||||
self
|
||||
}
|
||||
|
||||
fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display {
|
||||
display!("if {ret} == nil then {ret} = nil; end; ")
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// SAFETY: No `ToFfi` for references because we can't guarantee that the returned reference
|
||||
// converted to a pointer will not outlive the pointee.
|
||||
//
|
||||
unsafe impl<T: Type> Type for &T {
|
||||
fn name() -> impl Display {
|
||||
display!("const_{}_ptr", T::name())
|
||||
}
|
||||
|
||||
fn cdecl(name: impl Display) -> impl Display {
|
||||
T::cdecl(display!("const *{name}"))
|
||||
}
|
||||
|
||||
fn build(b: &mut TypeBuilder) {
|
||||
b.include::<T>();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Type> Type for &mut T {
|
||||
fn name() -> impl Display {
|
||||
display!("{}_ptr", T::name())
|
||||
}
|
||||
|
||||
fn cdecl(name: impl Display) -> impl Display {
|
||||
T::cdecl(display!("*{name}"))
|
||||
}
|
||||
|
||||
fn build(b: &mut TypeBuilder) {
|
||||
b.include::<T>();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// SAFETY: Pass by value for references, which have the same semantics as pointers (see above). Must
|
||||
// ensure that the pointer is not nil before being converted to a reference.
|
||||
//
|
||||
unsafe impl<T: Type> FromFfi for &T {
|
||||
type From = *const T;
|
||||
type FromArg = *const T;
|
||||
unsafe impl<'s, T: Type> FromFfi for &'s T {
|
||||
type From = Option<&'s T>;
|
||||
type FromArg = Option<&'s T>;
|
||||
|
||||
fn prelude(arg: &str) -> impl Display {
|
||||
display!(r#"assert({arg} ~= nil, "argument '{arg}' cannot be nil"); "#)
|
||||
@ -742,19 +693,19 @@ unsafe impl<T: Type> FromFfi for &T {
|
||||
|
||||
fn convert(from: Self::From) -> Self {
|
||||
debug_assert!(
|
||||
!from.is_null(),
|
||||
"<&T>::convert() called on a null pointer when it was checked to be non-null"
|
||||
from.is_some(),
|
||||
"<&T>::convert() called on a null reference when it was checked to be non-null"
|
||||
);
|
||||
|
||||
unsafe { &*from }
|
||||
unsafe { from.unwrap_unchecked() }
|
||||
}
|
||||
|
||||
fn convert_arg(from: Self::FromArg) -> Self {
|
||||
Self::convert(from)
|
||||
FromFfi::convert(from)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: Type> FromFfi for &mut T {
|
||||
unsafe impl<'s, T: Type> FromFfi for &'s mut T {
|
||||
//
|
||||
// SAFETY: `FromFfi` for *mutable* references is safe because it is guaranteed that no two Rust
|
||||
// code called via FFI can be running at the same time on the same OS thread (no Lua
|
||||
@ -771,8 +722,11 @@ unsafe impl<T: Type> FromFfi for &mut T {
|
||||
// FFI which could violate exclusive borrow semantics. This is prevented by not implementing
|
||||
// `FromFfi` for function pointers (see below).
|
||||
//
|
||||
type From = *mut T;
|
||||
type FromArg = *mut T;
|
||||
// The runtime does not keep any references to Rust user objects boxed in cdata (futures are
|
||||
// the only exception; their ownership is transferred to the runtime via yield).
|
||||
//
|
||||
type From = Option<&'s mut T>;
|
||||
type FromArg = Option<&'s mut T>;
|
||||
|
||||
fn prelude(arg: &str) -> impl Display {
|
||||
display!(r#"assert({arg} ~= nil, "argument '{arg}' cannot be nil"); "#)
|
||||
@ -780,18 +734,46 @@ unsafe impl<T: Type> FromFfi for &mut T {
|
||||
|
||||
fn convert(from: Self::From) -> Self {
|
||||
debug_assert!(
|
||||
!from.is_null(),
|
||||
"<&mut T>::convert() called on a null pointer when it was checked to be non-null"
|
||||
from.is_some(),
|
||||
"<&mut T>::convert() called on a null reference when it was checked to be non-null"
|
||||
);
|
||||
|
||||
unsafe { &mut *from }
|
||||
unsafe { from.unwrap_unchecked() }
|
||||
}
|
||||
|
||||
fn convert_arg(from: Self::FromArg) -> Self {
|
||||
Self::convert(from)
|
||||
FromFfi::convert(from)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// SAFETY: Return by value for pointers, which maps to a `cdata` return in lua containing the
|
||||
// pointer (`T *`). We also map null pointers to `nil` for convenience (otherwise it's still a cdata
|
||||
// value containing a null pointer)
|
||||
//
|
||||
macro_rules! impl_ptr_toabi {
|
||||
($ty:ty) => {
|
||||
unsafe impl<T: Type> ToFfi for $ty {
|
||||
type To = Self;
|
||||
|
||||
fn convert(self) -> Self::To {
|
||||
self
|
||||
}
|
||||
|
||||
fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display {
|
||||
display!("if {ret} == nil then {ret} = nil; end; ")
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_ptr_toabi!(*const T);
|
||||
impl_ptr_toabi!(*mut T);
|
||||
impl_ptr_toabi!(&'static T);
|
||||
impl_ptr_toabi!(&'static mut T);
|
||||
impl_ptr_toabi!(Option<&'static T>);
|
||||
impl_ptr_toabi!(Option<&'static mut T>);
|
||||
|
||||
//
|
||||
// SAFETY: No `FromFfi` and `ToFfi` for arrays because passing or returning them by value is not a
|
||||
// thing in C (they are just pointers).
|
||||
@ -826,13 +808,14 @@ unsafe impl<T: Type, const N: usize> Type for [T; N] {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UnsafeExternCFn<In, Out>(PhantomData<unsafe extern "C" fn(In) -> Out>);
|
||||
|
||||
macro_rules! impl_function {
|
||||
(fn($($arg:tt),*) -> $ret:tt) => {
|
||||
impl_function!((extern "C" fn($($arg),*) -> $ret), fn($($arg),*) -> $ret);
|
||||
impl_function!((unsafe extern "C" fn($($arg),*) -> $ret), fn($($arg),*) -> $ret);
|
||||
impl_function!(UnsafeExternCFn, fn($($arg),*) -> $ret);
|
||||
};
|
||||
|
||||
(($($type:tt)+), fn($($arg:tt),*) -> $ret:tt) => {
|
||||
($ty:tt, fn($($arg:tt),*) -> $ret:tt) => {
|
||||
//
|
||||
// SAFETY: No `FromFfi` for function pointers because of borrow safety invariants (see above
|
||||
// in `&mut T`).
|
||||
@ -840,7 +823,7 @@ macro_rules! impl_function {
|
||||
// We also can't implement `ToFfi` because we can't call `FromFfi` and `ToFfi` for the
|
||||
// function's respective argument and return values.
|
||||
//
|
||||
unsafe impl<$($arg: Type,)* $ret: Type> Type for $($type)+ {
|
||||
unsafe impl<$($arg: Type,)* $ret: Type> Type for $ty<($($arg,)*), $ret> {
|
||||
fn name() -> impl Display {
|
||||
disp(|f| Ok({
|
||||
write!(f, "fn_{}", $ret::name())?;
|
||||
@ -872,7 +855,7 @@ macro_rules! impl_function {
|
||||
b.include::<$ret>();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_function!(fn() -> Z);
|
||||
|
@ -67,7 +67,7 @@ fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
|
||||
|
||||
let ffi_register_drop = quote! {
|
||||
if ::std::mem::needs_drop::<Self>() {
|
||||
b.declare::<unsafe extern "C" fn(*mut Self)>(#ffi_drop_cname);
|
||||
b.declare::<#ffi::UnsafeExternCFn<(*mut Self,), ()>>(#ffi_drop_cname);
|
||||
b.metatable_raw("gc", ::std::format_args!("__C.{}", #ffi_drop_cname));
|
||||
}
|
||||
};
|
||||
@ -280,9 +280,9 @@ fn generate_ffi_register(func: &FfiFunction) -> Result<TokenStream> {
|
||||
};
|
||||
|
||||
let declare = if func.ret_by_out {
|
||||
quote! { b.declare::<unsafe extern "C" fn(*mut #ret, #(#params),*)>(#c_name); }
|
||||
quote! { b.declare::<#ffi::UnsafeExternCFn<(*mut #ret, #(#params,)*), ()>>(#c_name); }
|
||||
} else {
|
||||
quote! { b.declare::<unsafe extern "C" fn(#(#params),*) -> #ret>(#c_name); }
|
||||
quote! { b.declare::<#ffi::UnsafeExternCFn<(#(#params,)*), #ret>>(#c_name); }
|
||||
};
|
||||
|
||||
let register = match func.attrs.metatable {
|
||||
|
Loading…
x
Reference in New Issue
Block a user