Rename ToFfi to IntoFfi
This commit is contained in:
parent
cadf0a8551
commit
45db380466
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
__internal::{display, type_id},
|
__internal::{display, type_id},
|
||||||
Cdef, CdefBuilder, FfiReturnConvention, Metatype, MetatypeBuilder, ToFfi, Type, TypeBuilder,
|
Cdef, CdefBuilder, FfiReturnConvention, IntoFfi, Metatype, MetatypeBuilder, Type, TypeBuilder,
|
||||||
UnsafeExternCFn,
|
UnsafeExternCFn,
|
||||||
};
|
};
|
||||||
use luaify::luaify;
|
use luaify::luaify;
|
||||||
@ -21,7 +21,7 @@ const SIGNATURE: Signature = Signature::from_ne_bytes(*b"\x00lb_poll");
|
|||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub struct lua_future<F: Future<Output: ToFfi>> {
|
pub struct lua_future<F: Future<Output: IntoFfi>> {
|
||||||
//
|
//
|
||||||
// SAFETY: LuaJIT guarantees that cdata payloads, which are GC-managed, are never relocated
|
// SAFETY: LuaJIT guarantees that cdata payloads, which are GC-managed, are never relocated
|
||||||
// (i.e. pinned). We can safely assume that we are pinned and poll the future inside this
|
// (i.e. pinned). We can safely assume that we are pinned and poll the future inside this
|
||||||
@ -43,7 +43,7 @@ pub struct lua_future<F: Future<Output: ToFfi>> {
|
|||||||
sig: Signature,
|
sig: Signature,
|
||||||
poll: fn(Pin<&mut Self>, cx: &mut Context) -> Poll<()>,
|
poll: fn(Pin<&mut Self>, cx: &mut Context) -> Poll<()>,
|
||||||
state: State<F>,
|
state: State<F>,
|
||||||
take: unsafe extern "C" fn(&mut Self) -> <F::Output as ToFfi>::To,
|
take: unsafe extern "C" fn(&mut Self) -> <F::Output as IntoFfi>::To,
|
||||||
drop: unsafe extern "C" fn(&mut Self),
|
drop: unsafe extern "C" fn(&mut Self),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ enum State<F: Future> {
|
|||||||
Complete,
|
Complete,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Future<Output: ToFfi>> lua_future<F> {
|
impl<F: Future<Output: IntoFfi>> lua_future<F> {
|
||||||
pub fn new(fut: F) -> Self {
|
pub fn new(fut: F) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sig: SIGNATURE,
|
sig: SIGNATURE,
|
||||||
@ -94,7 +94,7 @@ impl<F: Future<Output: ToFfi>> lua_future<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn take(&mut self) -> <F::Output as ToFfi>::To {
|
unsafe extern "C" fn take(&mut self) -> <F::Output as IntoFfi>::To {
|
||||||
// `fut:__take()` returns the fulfilled value by-value (not by out-param) because if we
|
// `fut:__take()` returns the fulfilled value by-value (not by out-param) because if we
|
||||||
// preallocate a cdata for the out-param and the thread for some reason gets dropped and
|
// preallocate a cdata for the out-param and the thread for some reason gets dropped and
|
||||||
// never resumed, the GC could call the destructor on an uninitialised cdata.
|
// never resumed, the GC could call the destructor on an uninitialised cdata.
|
||||||
@ -131,7 +131,7 @@ impl Future for lua_pollable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<F: Future<Output: ToFfi> + 'static> Type for lua_future<F> {
|
unsafe impl<F: Future<Output: IntoFfi> + 'static> Type for lua_future<F> {
|
||||||
fn name() -> impl Display {
|
fn name() -> impl Display {
|
||||||
display!("future__{:x}", type_id::<F>())
|
display!("future__{:x}", type_id::<F>())
|
||||||
}
|
}
|
||||||
@ -145,15 +145,15 @@ unsafe impl<F: Future<Output: ToFfi> + 'static> Type for lua_future<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<F: Future<Output: ToFfi> + 'static> Cdef for lua_future<F> {
|
unsafe impl<F: Future<Output: IntoFfi> + 'static> Cdef for lua_future<F> {
|
||||||
fn build(s: &mut CdefBuilder) {
|
fn build(s: &mut CdefBuilder) {
|
||||||
s.field_opaque(mem::offset_of!(Self, take)) // opaque .sig, .poll and .state
|
s.field_opaque(mem::offset_of!(Self, take)) // opaque .sig, .poll and .state
|
||||||
.field::<UnsafeExternCFn<(&mut Self,), <F::Output as ToFfi>::To>>("__take")
|
.field::<UnsafeExternCFn<(&mut Self,), <F::Output as IntoFfi>::To>>("__take")
|
||||||
.field::<UnsafeExternCFn<(&mut Self,), ()>>("__drop");
|
.field::<UnsafeExternCFn<(&mut Self,), ()>>("__drop");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<F: Future<Output: ToFfi> + 'static> Metatype for lua_future<F> {
|
unsafe impl<F: Future<Output: IntoFfi> + 'static> Metatype for lua_future<F> {
|
||||||
type Target = Self;
|
type Target = Self;
|
||||||
|
|
||||||
fn build(s: &mut MetatypeBuilder) {
|
fn build(s: &mut MetatypeBuilder) {
|
||||||
@ -161,7 +161,7 @@ unsafe impl<F: Future<Output: ToFfi> + 'static> Metatype for lua_future<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<F: Future<Output: ToFfi> + 'static> ToFfi for lua_future<F> {
|
unsafe impl<F: Future<Output: IntoFfi> + 'static> IntoFfi for lua_future<F> {
|
||||||
type To = lua_future<F>;
|
type To = lua_future<F>;
|
||||||
|
|
||||||
fn convert(self) -> Self::To {
|
fn convert(self) -> Self::To {
|
||||||
@ -179,12 +179,12 @@ unsafe impl<F: Future<Output: ToFfi> + 'static> ToFfi for lua_future<F> {
|
|||||||
// `coroutine.yield` is cached as `yield` and `ffi.gc` as `gc` in locals (see lib.rs)
|
// `coroutine.yield` is cached as `yield` and `ffi.gc` as `gc` in locals (see lib.rs)
|
||||||
display!(
|
display!(
|
||||||
"yield({ret}); {ret} = gc({ret}, nil):__take(); {}",
|
"yield({ret}); {ret} = gc({ret}, nil):__take(); {}",
|
||||||
<F::Output as ToFfi>::postlude(ret, FfiReturnConvention::ByValue)
|
<F::Output as IntoFfi>::postlude(ret, FfiReturnConvention::ByValue)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: IntoFuture<Output: ToFfi>> From<F> for lua_future<F::IntoFuture> {
|
impl<F: IntoFuture<Output: IntoFfi>> From<F> for lua_future<F::IntoFuture> {
|
||||||
fn from(value: F) -> Self {
|
fn from(value: F) -> Self {
|
||||||
Self::new(value.into_future())
|
Self::new(value.into_future())
|
||||||
}
|
}
|
||||||
|
@ -379,7 +379,6 @@ impl<'r> Drop for MetatypeBuilder<'r> {
|
|||||||
|
|
||||||
pub unsafe trait FromFfi: Sized {
|
pub unsafe trait FromFfi: Sized {
|
||||||
type From: Type + Sized;
|
type From: Type + Sized;
|
||||||
type FromArg: Type + Sized;
|
|
||||||
|
|
||||||
fn require_keepalive() -> bool {
|
fn require_keepalive() -> bool {
|
||||||
false
|
false
|
||||||
@ -390,10 +389,9 @@ pub unsafe trait FromFfi: Sized {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self;
|
fn convert(from: Self::From) -> Self;
|
||||||
fn convert_arg(from: Self::FromArg) -> Self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe trait ToFfi: Sized {
|
pub unsafe trait IntoFfi: Sized {
|
||||||
type To: Type + Sized;
|
type To: Type + Sized;
|
||||||
|
|
||||||
fn postlude(_ret: &str, _conv: FfiReturnConvention) -> impl Display {
|
fn postlude(_ret: &str, _conv: FfiReturnConvention) -> impl Display {
|
||||||
@ -472,7 +470,7 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call<T: ToFfi>(&mut self, func: impl Display, ret: FfiReturnConvention) {
|
pub fn call<T: IntoFfi>(&mut self, func: impl Display, ret: FfiReturnConvention) {
|
||||||
let Self {
|
let Self {
|
||||||
metatype,
|
metatype,
|
||||||
params,
|
params,
|
||||||
@ -541,7 +539,7 @@ impl_primitive!(isize, "intptr_t");
|
|||||||
impl_primitive!(c_float, "float");
|
impl_primitive!(c_float, "float");
|
||||||
impl_primitive!(c_double, "double");
|
impl_primitive!(c_double, "double");
|
||||||
|
|
||||||
unsafe impl ToFfi for () {
|
unsafe impl IntoFfi for () {
|
||||||
//
|
//
|
||||||
// SAFETY: Unit type return maps to a C void return, which is a nil return in lua. There is no
|
// SAFETY: Unit type return maps to a C void return, which is a nil return in lua. There is no
|
||||||
// equivalent to passing a unit type as an argument in C. `c_void` cannot be returned from rust
|
// equivalent to passing a unit type as an argument in C. `c_void` cannot be returned from rust
|
||||||
@ -561,7 +559,6 @@ unsafe impl ToFfi for () {
|
|||||||
|
|
||||||
unsafe impl FromFfi for bool {
|
unsafe impl FromFfi for bool {
|
||||||
type From = bool;
|
type From = bool;
|
||||||
type FromArg = bool;
|
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl Display {
|
fn prelude(arg: &str) -> impl Display {
|
||||||
display!(
|
display!(
|
||||||
@ -572,13 +569,9 @@ unsafe impl FromFfi for bool {
|
|||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
from
|
from
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_arg(from: Self::FromArg) -> Self {
|
|
||||||
from
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl ToFfi for bool {
|
unsafe impl IntoFfi for bool {
|
||||||
type To = bool;
|
type To = bool;
|
||||||
|
|
||||||
fn convert(self) -> Self::To {
|
fn convert(self) -> Self::To {
|
||||||
@ -586,14 +579,10 @@ unsafe impl ToFfi for bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
|
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
|
||||||
disp(move |f| {
|
disp(move |f| match conv {
|
||||||
Ok({
|
FfiReturnConvention::Void => unreachable!(),
|
||||||
match conv {
|
FfiReturnConvention::ByValue => Ok(()),
|
||||||
FfiReturnConvention::Void => unreachable!(),
|
FfiReturnConvention::ByOutParam => write!(f, "{ret} = {ret} ~= 0; "),
|
||||||
FfiReturnConvention::ByValue => {}
|
|
||||||
FfiReturnConvention::ByOutParam => write!(f, "{ret} = {ret} ~= 0; ")?,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -602,7 +591,6 @@ macro_rules! impl_number_fromabi {
|
|||||||
($rtype:ty) => {
|
($rtype:ty) => {
|
||||||
unsafe impl FromFfi for $rtype {
|
unsafe impl FromFfi for $rtype {
|
||||||
type From = Self;
|
type From = Self;
|
||||||
type FromArg = Self;
|
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl Display {
|
fn prelude(arg: &str) -> impl Display {
|
||||||
display!(r#"do local __{arg} = {arg}; {arg} = tonumber({arg}); assert(type({arg}) == "number", "number expected in argument '{arg}', got " .. type(__{arg})); end; "#)
|
display!(r#"do local __{arg} = {arg}; {arg} = tonumber({arg}); assert(type({arg}) == "number", "number expected in argument '{arg}', got " .. type(__{arg})); end; "#)
|
||||||
@ -611,10 +599,6 @@ macro_rules! impl_number_fromabi {
|
|||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
from
|
from
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_arg(from: Self::FromArg) -> Self {
|
|
||||||
from
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -632,53 +616,66 @@ impl_number_fromabi!(isize);
|
|||||||
impl_number_fromabi!(f32);
|
impl_number_fromabi!(f32);
|
||||||
impl_number_fromabi!(f64);
|
impl_number_fromabi!(f64);
|
||||||
|
|
||||||
macro_rules! impl_number_toabi {
|
macro_rules! impl_number_intoabi {
|
||||||
($rtype:ty) => {
|
($rtype:ty) => {
|
||||||
unsafe impl ToFfi for $rtype {
|
unsafe impl IntoFfi for $rtype {
|
||||||
type To = Self;
|
|
||||||
|
|
||||||
fn convert(self) -> Self::To {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_number_toabi!(u8);
|
|
||||||
impl_number_toabi!(u16);
|
|
||||||
impl_number_toabi!(u32);
|
|
||||||
impl_number_toabi!(i8);
|
|
||||||
impl_number_toabi!(i16);
|
|
||||||
impl_number_toabi!(i32);
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
impl_number_toabi!(usize);
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
impl_number_toabi!(isize);
|
|
||||||
impl_number_toabi!(c_float);
|
|
||||||
impl_number_toabi!(c_double);
|
|
||||||
|
|
||||||
macro_rules! impl_bigint_toabi {
|
|
||||||
($rtype:ty) => {
|
|
||||||
unsafe impl ToFfi for $rtype {
|
|
||||||
type To = Self;
|
type To = Self;
|
||||||
|
|
||||||
fn convert(self) -> Self::To {
|
fn convert(self) -> Self::To {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display {
|
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
|
||||||
display!("{ret} = tonumber({ret}); ")
|
disp(move |f| match conv {
|
||||||
|
FfiReturnConvention::Void => unreachable!(),
|
||||||
|
FfiReturnConvention::ByValue => Ok(()),
|
||||||
|
FfiReturnConvention::ByOutParam => write!(f, "{ret} = tonumber({ret}); "),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_bigint_toabi!(u64);
|
impl_number_intoabi!(u8);
|
||||||
impl_bigint_toabi!(i64);
|
impl_number_intoabi!(u16);
|
||||||
|
impl_number_intoabi!(u32);
|
||||||
|
impl_number_intoabi!(i8);
|
||||||
|
impl_number_intoabi!(i16);
|
||||||
|
impl_number_intoabi!(i32);
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
impl_number_intoabi!(usize);
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
impl_number_intoabi!(isize);
|
||||||
|
impl_number_intoabi!(c_float);
|
||||||
|
impl_number_intoabi!(c_double);
|
||||||
|
|
||||||
|
macro_rules! impl_bigint_intoabi {
|
||||||
|
($rtype:ty) => {
|
||||||
|
unsafe impl IntoFfi for $rtype {
|
||||||
|
type To = Self;
|
||||||
|
|
||||||
|
fn convert(self) -> Self::To {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
|
||||||
|
disp(move |f| match conv {
|
||||||
|
FfiReturnConvention::Void => unreachable!(),
|
||||||
|
FfiReturnConvention::ByValue | FfiReturnConvention::ByOutParam => {
|
||||||
|
write!(f, "{ret} = tonumber({ret}); ")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_bigint_intoabi!(u64);
|
||||||
|
impl_bigint_intoabi!(i64);
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
impl_bigint_toabi!(usize);
|
impl_bigint_intoabi!(usize);
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
impl_bigint_toabi!(isize);
|
impl_bigint_intoabi!(isize);
|
||||||
|
|
||||||
macro_rules! impl_const_ptr {
|
macro_rules! impl_const_ptr {
|
||||||
($ty:ty) => {
|
($ty:ty) => {
|
||||||
@ -737,15 +734,10 @@ macro_rules! impl_ptr_fromabi {
|
|||||||
($ty:ty) => {
|
($ty:ty) => {
|
||||||
unsafe impl<T: Type> FromFfi for $ty {
|
unsafe impl<T: Type> FromFfi for $ty {
|
||||||
type From = Self;
|
type From = Self;
|
||||||
type FromArg = Self;
|
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
from
|
from
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_arg(from: Self::FromArg) -> Self {
|
|
||||||
from
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -755,77 +747,14 @@ impl_ptr_fromabi!(*mut T);
|
|||||||
impl_ptr_fromabi!(Option<&T>);
|
impl_ptr_fromabi!(Option<&T>);
|
||||||
impl_ptr_fromabi!(Option<&mut T>);
|
impl_ptr_fromabi!(Option<&mut 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"); "#)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
|
||||||
debug_assert!(
|
|
||||||
from.is_some(),
|
|
||||||
"<&T>::convert() called on a null reference when it was checked to be non-null"
|
|
||||||
);
|
|
||||||
|
|
||||||
unsafe { from.unwrap_unchecked() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_arg(from: Self::FromArg) -> Self {
|
|
||||||
FromFfi::convert(from)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
// reentrancy).
|
|
||||||
//
|
|
||||||
// i.e. The call stack will always look something like this:
|
|
||||||
//
|
|
||||||
// * Runtime (LuaJIT/Rust) -> Lua (via C) -> Rust (via FFI): This is SAFE and the only use case
|
|
||||||
// we support. All references (mutable or not) to Rust user objects will be dropped before
|
|
||||||
// returning to Lua.
|
|
||||||
//
|
|
||||||
// * Runtime (LuaJIT/Rust) -> Lua (via C) -> Rust (via FFI) -> Lua (via callback): This is
|
|
||||||
// UNSAFE because we cannot prevent the Lua callback from calling back into Rust code via
|
|
||||||
// FFI which could violate exclusive borrow semantics. This is prevented by not implementing
|
|
||||||
// `FromFfi` for function pointers (see below).
|
|
||||||
//
|
|
||||||
// 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"); "#)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
|
||||||
debug_assert!(
|
|
||||||
from.is_some(),
|
|
||||||
"<&mut T>::convert() called on a null reference when it was checked to be non-null"
|
|
||||||
);
|
|
||||||
|
|
||||||
unsafe { from.unwrap_unchecked() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_arg(from: Self::FromArg) -> Self {
|
|
||||||
FromFfi::convert(from)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// SAFETY: Return by value for pointers, which maps to a `cdata` return in lua containing the
|
// 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
|
// pointer (`T *`). We also map null pointers to `nil` for convenience (otherwise it's still a cdata
|
||||||
// value containing a null pointer)
|
// value containing a null pointer)
|
||||||
//
|
//
|
||||||
macro_rules! impl_ptr_toabi {
|
macro_rules! impl_ptr_intoabi {
|
||||||
($ty:ty) => {
|
($ty:ty) => {
|
||||||
unsafe impl<T: Type> ToFfi for $ty {
|
unsafe impl<T: Type> IntoFfi for $ty {
|
||||||
type To = Self;
|
type To = Self;
|
||||||
|
|
||||||
fn convert(self) -> Self::To {
|
fn convert(self) -> Self::To {
|
||||||
@ -839,15 +768,76 @@ macro_rules! impl_ptr_toabi {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_ptr_toabi!(*const T);
|
impl_ptr_intoabi!(*const T);
|
||||||
impl_ptr_toabi!(*mut T);
|
impl_ptr_intoabi!(*mut T);
|
||||||
impl_ptr_toabi!(&'static T);
|
impl_ptr_intoabi!(Option<&'static T>);
|
||||||
impl_ptr_toabi!(&'static mut T);
|
impl_ptr_intoabi!(Option<&'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
|
// 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 reentrancy).
|
||||||
|
//
|
||||||
|
// i.e. The call stack will always look something like this:
|
||||||
|
//
|
||||||
|
// * Runtime (LuaJIT/Rust) -> Lua (via C) -> Rust (via FFI): This is SAFE and the only use case we
|
||||||
|
// support. All references (mutable or not) to Rust user objects will be dropped before
|
||||||
|
// returning to Lua.
|
||||||
|
//
|
||||||
|
// * Runtime (LuaJIT/Rust) -> Lua (via C) -> Rust (via FFI) -> Lua (via callback): This is UNSAFE
|
||||||
|
// because we cannot prevent the Lua callback from calling back into Rust code via FFI which
|
||||||
|
// could violate exclusive borrow semantics. This is prevented by not implementing `FromFfi` for
|
||||||
|
// function pointers (see below).
|
||||||
|
//
|
||||||
|
// 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).
|
||||||
|
//
|
||||||
|
macro_rules! impl_ref_fromabi {
|
||||||
|
($ty:ty) => {
|
||||||
|
unsafe impl<'s, T: Type> FromFfi for $ty {
|
||||||
|
type From = Option<$ty>;
|
||||||
|
|
||||||
|
fn prelude(arg: &str) -> impl Display {
|
||||||
|
display!(r#"assert({arg} ~= nil, "argument '{arg}' cannot be nil"); "#)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert(from: Self::From) -> Self {
|
||||||
|
// SAFETY: we already checked that the reference is nonnull from the lua side
|
||||||
|
debug_assert!(
|
||||||
|
from.is_some(),
|
||||||
|
"<{}>::convert() called on a null reference when it was checked to be nonnull",
|
||||||
|
stringify!($ty),
|
||||||
|
);
|
||||||
|
|
||||||
|
unsafe { from.unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_ref_fromabi!(&'s T);
|
||||||
|
impl_ref_fromabi!(&'s mut T);
|
||||||
|
|
||||||
|
//
|
||||||
|
// SAFETY: `IntoFfi` only for 'static references because we cannot guarantee that the pointer will
|
||||||
|
// not outlive the pointee otherwise.
|
||||||
|
//
|
||||||
|
macro_rules! impl_ref_intoabi {
|
||||||
|
($ty:ty) => {
|
||||||
|
unsafe impl<T: Type> IntoFfi for $ty {
|
||||||
|
type To = Self;
|
||||||
|
|
||||||
|
fn convert(self) -> Self::To {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_ref_intoabi!(&'static T);
|
||||||
|
impl_ref_intoabi!(&'static mut T);
|
||||||
|
|
||||||
|
//
|
||||||
|
// SAFETY: No `FromFfi` and `IntoFfi` for arrays because passing or returning them by value is not a
|
||||||
// thing in C (they are just pointers).
|
// thing in C (they are just pointers).
|
||||||
//
|
//
|
||||||
// TODO: we could automatically convert them to tables and vice-versa
|
// TODO: we could automatically convert them to tables and vice-versa
|
||||||
@ -892,7 +882,7 @@ macro_rules! impl_function {
|
|||||||
// SAFETY: No `FromFfi` for function pointers because of borrow safety invariants (see above
|
// SAFETY: No `FromFfi` for function pointers because of borrow safety invariants (see above
|
||||||
// in `&mut T`).
|
// in `&mut T`).
|
||||||
//
|
//
|
||||||
// We also can't implement `ToFfi` because we can't call `FromFfi` and `ToFfi` for the
|
// We also can't implement `IntoFfi` because we can't call `FromFfi` and `IntoFfi` for the
|
||||||
// function's respective argument and return values.
|
// function's respective argument and return values.
|
||||||
//
|
//
|
||||||
unsafe impl<$($arg: Type,)* $ret: Type> Type for $ty<($($arg,)*), $ret> {
|
unsafe impl<$($arg: Type,)* $ret: Type> Type for $ty<($($arg,)*), $ret> {
|
||||||
|
4
crates/luaffi/src/result.rs
Normal file
4
crates/luaffi/src/result.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
// pub enum lua_result<T> {
|
||||||
|
// Err(lua_error),
|
||||||
|
// Ok(T),
|
||||||
|
// }
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
__internal::{disp, display},
|
__internal::{disp, display},
|
||||||
FfiReturnConvention, FromFfi, IS_UTF8_FN, ToFfi, Type,
|
FfiReturnConvention, FromFfi, IS_UTF8_FN, IntoFfi, Type,
|
||||||
};
|
};
|
||||||
use bstr::BStr;
|
use bstr::BStr;
|
||||||
use luaffi_impl::{cdef, metatype};
|
use luaffi_impl::{cdef, metatype};
|
||||||
@ -29,8 +29,7 @@ pub struct lua_buffer {
|
|||||||
impl lua_buffer {}
|
impl lua_buffer {}
|
||||||
|
|
||||||
unsafe impl<'s> FromFfi for &'s [u8] {
|
unsafe impl<'s> FromFfi for &'s [u8] {
|
||||||
type From = lua_buf;
|
type From = Option<&'s lua_buf>;
|
||||||
type FromArg = Option<&'s Self::From>;
|
|
||||||
|
|
||||||
fn require_keepalive() -> bool {
|
fn require_keepalive() -> bool {
|
||||||
true
|
true
|
||||||
@ -50,20 +49,15 @@ unsafe impl<'s> FromFfi for &'s [u8] {
|
|||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
// SAFETY: we already checked that the string is nonnull from the lua side
|
// SAFETY: we already checked that the string is nonnull from the lua side
|
||||||
|
debug_assert!(from.is_some());
|
||||||
|
let from = unsafe { from.unwrap_unchecked() };
|
||||||
debug_assert!(!from.__ptr.is_null());
|
debug_assert!(!from.__ptr.is_null());
|
||||||
unsafe { slice::from_raw_parts(from.__ptr, from.__len) }
|
unsafe { slice::from_raw_parts(from.__ptr, from.__len) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_arg(from: Self::FromArg) -> Self {
|
|
||||||
// SAFETY: we already checked that the string is nonnull from the lua side
|
|
||||||
debug_assert!(from.is_some());
|
|
||||||
unsafe { FromFfi::convert(*from.unwrap_unchecked()) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'s> FromFfi for &'s BStr {
|
unsafe impl<'s> FromFfi for &'s BStr {
|
||||||
type From = <&'s [u8] as FromFfi>::From;
|
type From = <&'s [u8] as FromFfi>::From;
|
||||||
type FromArg = <&'s [u8] as FromFfi>::FromArg;
|
|
||||||
|
|
||||||
fn require_keepalive() -> bool {
|
fn require_keepalive() -> bool {
|
||||||
<&'s [u8] as FromFfi>::require_keepalive()
|
<&'s [u8] as FromFfi>::require_keepalive()
|
||||||
@ -76,21 +70,18 @@ unsafe impl<'s> FromFfi for &'s BStr {
|
|||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
<&'s [u8] as FromFfi>::convert(from).into()
|
<&'s [u8] as FromFfi>::convert(from).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_arg(from: Self::FromArg) -> Self {
|
|
||||||
<&'s [u8] as FromFfi>::convert_arg(from).into()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'s> FromFfi for &'s str {
|
unsafe impl<'s> FromFfi for &'s str {
|
||||||
type From = lua_buf;
|
type From = Option<&'s lua_buf>;
|
||||||
type FromArg = Option<&'s Self::From>;
|
|
||||||
|
|
||||||
fn require_keepalive() -> bool {
|
fn require_keepalive() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl Display {
|
fn prelude(arg: &str) -> impl Display {
|
||||||
|
// this converts string arguments to a `lua_buf` with a pointer to the string and its length
|
||||||
|
// and ensures that the string is valid utf8
|
||||||
disp(move |f| {
|
disp(move |f| {
|
||||||
let ct = Self::From::name();
|
let ct = Self::From::name();
|
||||||
write!(
|
write!(
|
||||||
@ -107,25 +98,19 @@ unsafe impl<'s> FromFfi for &'s str {
|
|||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
// SAFETY: we already checked that the string is nonnull and valid utf8 from the lua side
|
// SAFETY: we already checked that the string is nonnull and valid utf8 from the lua side
|
||||||
|
debug_assert!(from.is_some());
|
||||||
|
let from = unsafe { from.unwrap_unchecked() };
|
||||||
debug_assert!(!from.__ptr.is_null());
|
debug_assert!(!from.__ptr.is_null());
|
||||||
let s = unsafe { slice::from_raw_parts(from.__ptr, from.__len) };
|
let from = unsafe { slice::from_raw_parts(from.__ptr, from.__len) };
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
std::str::from_utf8(s).is_ok(),
|
std::str::from_utf8(from).is_ok(),
|
||||||
"<&str>::convert() called on an invalid utf8 string when it was checked to be valid"
|
"<&str>::convert() called on an invalid utf8 string when it was checked to be valid"
|
||||||
);
|
);
|
||||||
|
unsafe { std::str::from_utf8_unchecked(from) }
|
||||||
unsafe { std::str::from_utf8_unchecked(s) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_arg(from: Self::FromArg) -> Self {
|
|
||||||
// SAFETY: we already checked that the string is nonnull and valid utf8 from the lua side
|
|
||||||
debug_assert!(from.is_some());
|
|
||||||
unsafe { FromFfi::convert(*from.unwrap_unchecked()) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl ToFfi for &'static [u8] {
|
unsafe impl IntoFfi for &'static [u8] {
|
||||||
type To = lua_buf;
|
type To = lua_buf;
|
||||||
|
|
||||||
fn convert(self) -> Self::To {
|
fn convert(self) -> Self::To {
|
||||||
@ -140,26 +125,26 @@ unsafe impl ToFfi for &'static [u8] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl ToFfi for &'static BStr {
|
unsafe impl IntoFfi for &'static BStr {
|
||||||
type To = <&'static [u8] as ToFfi>::To;
|
type To = <&'static [u8] as IntoFfi>::To;
|
||||||
|
|
||||||
fn convert(self) -> Self::To {
|
fn convert(self) -> Self::To {
|
||||||
<&'static [u8] as ToFfi>::convert(self.as_ref())
|
<&'static [u8] as IntoFfi>::convert(self.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
|
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
|
||||||
<&'static [u8] as ToFfi>::postlude(ret, conv)
|
<&'static [u8] as IntoFfi>::postlude(ret, conv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl ToFfi for &'static str {
|
unsafe impl IntoFfi for &'static str {
|
||||||
type To = <&'static [u8] as ToFfi>::To;
|
type To = <&'static [u8] as IntoFfi>::To;
|
||||||
|
|
||||||
fn convert(self) -> Self::To {
|
fn convert(self) -> Self::To {
|
||||||
<&'static [u8] as ToFfi>::convert(self.as_bytes())
|
<&'static [u8] as IntoFfi>::convert(self.as_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
|
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
|
||||||
<&'static [u8] as ToFfi>::postlude(ret, conv)
|
<&'static [u8] as IntoFfi>::postlude(ret, conv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,8 @@ fn generate_type(ty: &Ident) -> Result<TokenStream> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl #ffi::ToFfi for #ty {
|
// SAFETY: we can always implement `IntoFfi` because it transfers ownership from Rust to Lua
|
||||||
|
unsafe impl #ffi::IntoFfi for #ty {
|
||||||
type To = Self;
|
type To = Self;
|
||||||
fn convert(self) -> Self::To { self }
|
fn convert(self) -> Self::To { self }
|
||||||
}
|
}
|
||||||
|
@ -231,8 +231,8 @@ fn generate_ffi_wrapper(func: &FfiFunction) -> Result<TokenStream> {
|
|||||||
|
|
||||||
match get_ffi_arg_type(ty) {
|
match get_ffi_arg_type(ty) {
|
||||||
FfiArgType::Default => {
|
FfiArgType::Default => {
|
||||||
params.push(quote! { #name: <#ty as #ffi::FromFfi>::FromArg });
|
params.push(quote! { #name: <#ty as #ffi::FromFfi>::From });
|
||||||
args.push(quote! { <#ty as #ffi::FromFfi>::convert_arg(#name) });
|
args.push(quote! { <#ty as #ffi::FromFfi>::convert(#name) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,19 +240,22 @@ fn generate_ffi_wrapper(func: &FfiFunction) -> Result<TokenStream> {
|
|||||||
let (ret, call) = if func.ret_by_out {
|
let (ret, call) = if func.ret_by_out {
|
||||||
// make return by out-param the first parameter
|
// make return by out-param the first parameter
|
||||||
let ret = &func.ret;
|
let ret = &func.ret;
|
||||||
params.insert(0, quote! { out: *mut #ret });
|
params.insert(0, quote! { out: *mut <#ret as #ffi::IntoFfi>::To });
|
||||||
(
|
(
|
||||||
quote!(()),
|
quote!(()),
|
||||||
quote! { ::std::ptr::write(out, Self::#name(#(#args),*)) },
|
quote! { ::std::ptr::write(out, <#ret as #ffi::IntoFfi>::convert(Self::#name(#(#args),*))) },
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let ret = &func.ret;
|
let ret = &func.ret;
|
||||||
(quote! { #ret }, quote! { Self::#name(#(#args),*) })
|
(
|
||||||
|
quote! { <#ret as #ffi::IntoFfi>::To },
|
||||||
|
quote! { <#ret as #ffi::IntoFfi>::convert(Self::#name(#(#args),*)) },
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#[unsafe(export_name = #c_name)]
|
#[unsafe(export_name = #c_name)]
|
||||||
unsafe extern "C" fn #rust_name(#(#params),*) -> #ret { unsafe { #call } }
|
unsafe extern "C" fn #rust_name(#(#params),*) -> #ret { #call }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,7 +278,7 @@ fn generate_ffi_register(func: &FfiFunction) -> Result<TokenStream> {
|
|||||||
|
|
||||||
match get_ffi_arg_type(ty) {
|
match get_ffi_arg_type(ty) {
|
||||||
FfiArgType::Default => {
|
FfiArgType::Default => {
|
||||||
params.push(quote! { <#ty as #ffi::FromFfi>::FromArg });
|
params.push(quote! { <#ty as #ffi::FromFfi>::From });
|
||||||
register.push(quote! { b.param::<#ty>(#name); })
|
register.push(quote! { b.param::<#ty>(#name); })
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -291,9 +294,9 @@ fn generate_ffi_register(func: &FfiFunction) -> Result<TokenStream> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let declare = if func.ret_by_out {
|
let declare = if func.ret_by_out {
|
||||||
quote! { b.declare::<#ffi::UnsafeExternCFn<(*mut #ret, #(#params,)*), ()>>(#c_name); }
|
quote! { b.declare::<#ffi::UnsafeExternCFn<(*mut <#ret as #ffi::IntoFfi>::To, #(#params,)*), ()>>(#c_name); }
|
||||||
} else {
|
} else {
|
||||||
quote! { b.declare::<#ffi::UnsafeExternCFn<(#(#params,)*), #ret>>(#c_name); }
|
quote! { b.declare::<#ffi::UnsafeExternCFn<(#(#params,)*), <#ret as #ffi::IntoFfi>::To>>(#c_name); }
|
||||||
};
|
};
|
||||||
|
|
||||||
let register = match func.attrs.metatable {
|
let register = match func.attrs.metatable {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user