From cadf0a855131219ddda13b1cecc4d2d2b544b71b Mon Sep 17 00:00:00 2001 From: luaneko Date: Tue, 24 Jun 2025 10:37:28 +1000 Subject: [PATCH] Update luaffi --- crates/luaffi/src/lib.rs | 142 +++++++++++++++++++++++++++--------- crates/luaffi/src/option.rs | 78 -------------------- crates/luaffi/src/string.rs | 111 +++++++++++++++++++++++----- 3 files changed, 201 insertions(+), 130 deletions(-) delete mode 100644 crates/luaffi/src/option.rs diff --git a/crates/luaffi/src/lib.rs b/crates/luaffi/src/lib.rs index 6c62d34..a1c2d11 100644 --- a/crates/luaffi/src/lib.rs +++ b/crates/luaffi/src/lib.rs @@ -9,7 +9,6 @@ use std::{ }; pub mod future; -// pub mod option; pub mod string; #[doc(hidden)] @@ -82,7 +81,7 @@ const CACHE_LOCALS: &[(&str, &str)] = &[ ("__tunpack", "table.unpack"), // string ("__slen", "string.len"), - ("__sformat", "string.format"), + ("__sprintf", "string.format"), ("__ssub", "string.sub"), ("__sgsub", "string.gsub"), ("__sgmatch", "string.gmatch"), @@ -528,6 +527,19 @@ macro_rules! impl_primitive { impl_primitive!((), "void"); impl_primitive!(c_void, "void"); +impl_primitive!(bool, "bool"); +impl_primitive!(u8, "uint8_t"); +impl_primitive!(u16, "uint16_t"); +impl_primitive!(u32, "uint32_t"); +impl_primitive!(u64, "uint64_t"); +impl_primitive!(usize, "uintptr_t"); +impl_primitive!(i8, "int8_t"); +impl_primitive!(i16, "int16_t"); +impl_primitive!(i32, "int32_t"); +impl_primitive!(i64, "int64_t"); +impl_primitive!(isize, "intptr_t"); +impl_primitive!(c_float, "float"); +impl_primitive!(c_double, "double"); unsafe impl ToFfi for () { // @@ -536,8 +548,8 @@ unsafe impl ToFfi for () { // so it should return the unit type instead. // type To = (); - fn convert(self) -> Self::To {} + fn convert(self) -> Self::To {} fn postlude(_ret: &str, conv: FfiReturnConvention) -> impl Display { assert!( conv == FfiReturnConvention::Void, @@ -547,19 +559,53 @@ unsafe impl ToFfi for () { } } -macro_rules! impl_primitive_abi { - ($rtype:ty, $ctype:expr, $ltype:expr $(, $unwrap:expr)?) => { - impl_primitive!($rtype, $ctype); +unsafe impl FromFfi for bool { + type From = bool; + type FromArg = bool; - // - // SAFETY: Primitive types are always copyable so we can pass and return them by value. - // + fn prelude(arg: &str) -> impl Display { + display!( + r#"assert(type({arg}) == "boolean", "boolean expected in argument '{arg}', got " .. type({arg})); "# + ) + } + + fn convert(from: Self::From) -> Self { + from + } + + fn convert_arg(from: Self::FromArg) -> Self { + from + } +} + +unsafe impl ToFfi for bool { + type To = bool; + + fn convert(self) -> Self::To { + self + } + + fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display { + disp(move |f| { + Ok({ + match conv { + FfiReturnConvention::Void => unreachable!(), + FfiReturnConvention::ByValue => {} + FfiReturnConvention::ByOutParam => write!(f, "{ret} = {ret} ~= 0; ")?, + } + }) + }) + } +} + +macro_rules! impl_number_fromabi { + ($rtype:ty) => { unsafe impl FromFfi for $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) + display!(r#"do local __{arg} = {arg}; {arg} = tonumber({arg}); assert(type({arg}) == "number", "number expected in argument '{arg}', got " .. type(__{arg})); end; "#) } fn convert(from: Self::From) -> Self { @@ -570,7 +616,49 @@ macro_rules! impl_primitive_abi { from } } + }; +} +impl_number_fromabi!(u8); +impl_number_fromabi!(u16); +impl_number_fromabi!(u32); +impl_number_fromabi!(u64); +impl_number_fromabi!(usize); +impl_number_fromabi!(i8); +impl_number_fromabi!(i16); +impl_number_fromabi!(i32); +impl_number_fromabi!(i64); +impl_number_fromabi!(isize); +impl_number_fromabi!(f32); +impl_number_fromabi!(f64); + +macro_rules! impl_number_toabi { + ($rtype:ty) => { + unsafe impl ToFfi 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; @@ -578,35 +666,19 @@ macro_rules! impl_primitive_abi { self } - #[allow(unused)] - fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display { - disp(move |f| Ok({ - match conv { - FfiReturnConvention::Void => unreachable!(), - FfiReturnConvention::ByValue => {}, - // if a primitive type for some reason gets returned by out-param, unwrap - // the cdata containing the value to convert it to the equivalent lua value - FfiReturnConvention::ByOutParam => { $(write!(f, "{ret} = {}; ", $unwrap(ret))?;)? }, - } - })) + fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display { + display!("{ret} = tonumber({ret}); ") } } }; } -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})")); +impl_bigint_toabi!(u64); +impl_bigint_toabi!(i64); +#[cfg(target_pointer_width = "64")] +impl_bigint_toabi!(usize); +#[cfg(target_pointer_width = "64")] +impl_bigint_toabi!(isize); macro_rules! impl_const_ptr { ($ty:ty) => { @@ -827,7 +899,7 @@ macro_rules! impl_function { fn name() -> impl Display { disp(|f| Ok({ write!(f, "fn_{}", $ret::name())?; - $(write!(f, "_{}", $arg::name())?;)* + $(write!(f, "__{}", $arg::name())?;)* })) } diff --git a/crates/luaffi/src/option.rs b/crates/luaffi/src/option.rs deleted file mode 100644 index fbfec5d..0000000 --- a/crates/luaffi/src/option.rs +++ /dev/null @@ -1,78 +0,0 @@ -use crate::{Cdef, CdefBuilder, FfiReturnConvention, FromFfi, ToFfi, Type, TypeBuilder, display}; -use std::{ffi::c_int, fmt::Display, ptr}; - -#[repr(C)] -#[allow(non_camel_case_types)] -pub enum lua_option { - None, // __tag = 0 - Some(T), // __tag = 1 -} - -unsafe impl Type for lua_option { - fn name() -> impl Display { - display!("option__{}", T::name()) - } - - fn cdecl(name: impl Display) -> impl Display { - display!("struct option__{} {name}", T::name()) - } - - fn build(b: &mut TypeBuilder) { - b.include::().cdef::(); - } -} - -unsafe impl Cdef for lua_option { - fn build(b: &mut CdefBuilder) { - b.field::("__tag").field::("__value"); - } -} - -unsafe impl FromFfi for Option { - type From = lua_option; - type FromArg = *mut Self::From; // pass by-ref - - fn require_keepalive() -> bool { - T::require_keepalive() - } - - fn prelude(arg: &str) -> impl Display { - let ct = Self::From::name(); - display!( - "if {arg} == nil then {arg} = __new(__ct.{ct}); else {}{arg} = __new(__ct.{ct}, 1, {arg}); end; ", - T::prelude(arg) - ) - } - - fn convert(from: Self::From) -> Self { - match from { - lua_option::Some(value) => Some(T::convert(value)), - lua_option::None => None, - } - } - - fn convert_arg(from: Self::FromArg) -> Self { - debug_assert!(!from.is_null()); - Self::convert(unsafe { ptr::replace(from, lua_option::None) }) - } -} - -unsafe impl ToFfi for Option { - type To = lua_option; - - fn convert(self) -> Self::To { - match self { - Some(value) => lua_option::Some(value.convert()), - None => lua_option::None, - } - } - - fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display { - // if we don't have a value, return nil. otherwise copy out the inner value immediately, - // forget the option cdata, then call postlude on the inner value. - display!( - "if {ret}.__tag == 0 then {ret} = nil; else {ret} = {ret}.__value; {}end; ", - T::postlude(ret, FfiReturnConvention::ByValue) - ) - } -} diff --git a/crates/luaffi/src/string.rs b/crates/luaffi/src/string.rs index ef0299d..cc70c1a 100644 --- a/crates/luaffi/src/string.rs +++ b/crates/luaffi/src/string.rs @@ -1,6 +1,10 @@ -use crate::{__internal::disp, FromFfi, IS_UTF8_FN, Type}; +use crate::{ + __internal::{disp, display}, + FfiReturnConvention, FromFfi, IS_UTF8_FN, ToFfi, Type, +}; +use bstr::BStr; use luaffi_impl::{cdef, metatype}; -use std::{fmt, ptr, slice}; +use std::{fmt::Display, slice}; #[derive(Debug, Clone, Copy)] #[cdef] @@ -12,48 +16,81 @@ pub struct lua_buf { #[metatype] impl lua_buf {} -unsafe impl FromFfi for *const [u8] { +// not implemented yet +#[derive(Debug, Clone, Copy)] +#[cdef] +pub struct lua_buffer { + __ptr: *const u8, + __len: usize, + __cap: usize, +} + +#[metatype] +impl lua_buffer {} + +unsafe impl<'s> FromFfi for &'s [u8] { type From = lua_buf; - type FromArg = *const Self::From; + type FromArg = Option<&'s Self::From>; fn require_keepalive() -> bool { true } - fn prelude(arg: &str) -> impl fmt::Display { + fn prelude(arg: &str) -> impl Display { // this converts string arguments to a `lua_buf` with a pointer to the string and its length disp(move |f| { let ct = Self::From::name(); write!( f, - r#"if {arg} ~= nil then assert(type({arg}) == "string", "string expected in argument '{arg}', got " .. type({arg})); "# + r#"assert(type({arg}) == "string", "string expected in argument '{arg}', got " .. type({arg})); "# )?; write!(f, "{arg} = __new(__ct.{ct}, {arg}, #{arg}); end; ") }) } fn convert(from: Self::From) -> Self { - ptr::slice_from_raw_parts(from.__ptr, from.__len) + // SAFETY: we already checked that the string is nonnull from the lua side + debug_assert!(!from.__ptr.is_null()); + unsafe { slice::from_raw_parts(from.__ptr, from.__len) } } fn convert_arg(from: Self::FromArg) -> Self { - if from.is_null() { - ptr::slice_from_raw_parts(ptr::null(), 0) - } else { - Self::convert(unsafe { *from }) - } + // 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 FromFfi for &str { +unsafe impl<'s> FromFfi for &'s BStr { + type From = <&'s [u8] as FromFfi>::From; + type FromArg = <&'s [u8] as FromFfi>::FromArg; + + fn require_keepalive() -> bool { + <&'s [u8] as FromFfi>::require_keepalive() + } + + fn prelude(arg: &str) -> impl Display { + <&'s [u8] as FromFfi>::prelude(arg) + } + + fn convert(from: Self::From) -> Self { + <&'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 { type From = lua_buf; - type FromArg = *const Self::From; + type FromArg = Option<&'s Self::From>; fn require_keepalive() -> bool { true } - fn prelude(arg: &str) -> impl fmt::Display { + fn prelude(arg: &str) -> impl Display { disp(move |f| { let ct = Self::From::name(); write!( @@ -82,7 +119,47 @@ unsafe impl FromFfi for &str { } fn convert_arg(from: Self::FromArg) -> Self { - debug_assert!(!from.is_null()); - unsafe { Self::convert(*from) } + // 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] { + type To = lua_buf; + + fn convert(self) -> Self::To { + lua_buf { + __ptr: self.as_ptr(), + __len: self.len(), + } + } + + fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display { + display!("{ret} = __intern({ret}.__ptr, {ret}.__len)") + } +} + +unsafe impl ToFfi for &'static BStr { + type To = <&'static [u8] as ToFfi>::To; + + fn convert(self) -> Self::To { + <&'static [u8] as ToFfi>::convert(self.as_ref()) + } + + fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display { + <&'static [u8] as ToFfi>::postlude(ret, conv) + } +} + +unsafe impl ToFfi for &'static str { + type To = <&'static [u8] as ToFfi>::To; + + fn convert(self) -> Self::To { + <&'static [u8] as ToFfi>::convert(self.as_bytes()) + } + + fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display { + <&'static [u8] as ToFfi>::postlude(ret, conv) } }