Update luaffi

This commit is contained in:
lumi 2025-06-24 10:37:28 +10:00
parent 7548c68c7c
commit cadf0a8551
Signed by: luaneko
GPG Key ID: 406809B8763FF07A
3 changed files with 201 additions and 130 deletions

View File

@ -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,14 @@ unsafe impl ToFfi for () {
}
}
macro_rules! impl_primitive_abi {
($rtype:ty, $ctype:expr, $ltype:expr $(, $unwrap:expr)?) => {
impl_primitive!($rtype, $ctype);
//
// SAFETY: Primitive types are always copyable so we can pass and return them by value.
//
unsafe impl FromFfi for $rtype {
type From = Self;
type FromArg = Self;
unsafe impl FromFfi for bool {
type From = bool;
type FromArg = bool;
fn prelude(arg: &str) -> impl Display {
display!(r#"assert(type({arg}) == "{0}", "{0} expected in argument '{arg}', got " .. type({arg})); "#, $ltype)
display!(
r#"assert(type({arg}) == "boolean", "boolean expected in argument '{arg}', got " .. type({arg})); "#
)
}
fn convert(from: Self::From) -> Self {
@ -571,6 +578,87 @@ macro_rules! impl_primitive_abi {
}
}
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#"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 {
from
}
fn convert_arg(from: Self::FromArg) -> Self {
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())?;)*
}))
}

View File

@ -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<T> {
None, // __tag = 0
Some(T), // __tag = 1
}
unsafe impl<T: Type> Type for lua_option<T> {
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::<T>().cdef::<Self>();
}
}
unsafe impl<T: Type> Cdef for lua_option<T> {
fn build(b: &mut CdefBuilder) {
b.field::<c_int>("__tag").field::<T>("__value");
}
}
unsafe impl<T: FromFfi> FromFfi for Option<T> {
type From = lua_option<T::From>;
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<T: ToFfi> ToFfi for Option<T> {
type To = lua_option<T::To>;
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)
)
}
}

View File

@ -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)
}
}