Add ctype new support

This commit is contained in:
2025-06-22 18:41:19 +10:00
parent 0fd59f6874
commit 40478fb7de
7 changed files with 118 additions and 81 deletions

View File

@@ -12,11 +12,22 @@ pub fn type_id<T: 'static>() -> u64 {
hash.finish()
}
macro_rules! export {
($($fn:expr),+ $(,)?) => {
// this ensures ffi function symbol exports are actually present in the resulting binary,
// otherwise they may get dead code-eliminated before it reaches the linker
#[used]
static __FFI_EXPORTS: &[fn()] = unsafe {
&[$(::std::mem::transmute($fn as *const ())),*]
};
};
}
macro_rules! display {
($($fmt:expr),+) => {{ crate::__internal::disp(move |f| write!(f, $($fmt),+)) }};
}
pub(crate) use display;
pub(crate) use {display, export};
pub fn disp(f: impl Fn(&mut Formatter) -> fmt::Result) -> impl Display {
struct Disp<F: Fn(&mut Formatter) -> fmt::Result>(F);

View File

@@ -1,4 +1,4 @@
use crate::__internal::{disp, display, write_sep};
use crate::__internal::{disp, display, export, write_sep};
pub use luaffi_impl::*;
use std::{
collections::HashSet,
@@ -31,6 +31,8 @@ unsafe extern "C" fn __is_utf8(ptr: *const u8, len: usize) -> bool {
simdutf8::basic::from_utf8(unsafe { slice::from_raw_parts(ptr, len) }).is_ok()
}
export![__keep, __is_utf8];
const CACHE_LIBS: &[(&str, &str)] = &[
("table", "table"),
("string", "string"),
@@ -47,7 +49,7 @@ const CACHE_LIBS: &[(&str, &str)] = &[
];
// https://www.lua.org/manual/5.1/manual.html#5.1
const CACHE_GLOBALS: &[(&str, &str)] = &[
const CACHE_LOCALS: &[(&str, &str)] = &[
// base
("assert", "assert"),
("error", "error"),
@@ -86,14 +88,16 @@ const CACHE_GLOBALS: &[(&str, &str)] = &[
("__fmod", "math.fmod"),
// coroutine
("__yield", "coroutine.yield"),
// package
("__preload", "package.preload"),
// debug
("__traceback", "debug.traceback"),
// ffi
("__C", "ffi.C"),
("__ct", "{}"),
("__cdef", "ffi.cdef"),
("__cnew", "ffi.new"),
("__ctype", "ffi.typeof"),
("__ctypes", "{}"),
("__new", "ffi.new"),
("__typeof", "ffi.typeof"),
("__istype", "ffi.istype"),
("__metatype", "ffi.metatype"),
("__cast", "ffi.cast"),
@@ -117,7 +121,7 @@ const CACHE_GLOBALS: &[(&str, &str)] = &[
fn cache_local(f: &mut Formatter, list: &[(&str, &str)]) -> fmt::Result {
write!(f, "local ")?;
write_sep(f, ", ", list.iter().map(|(s, _)| s))?;
write!(f, "\n = ")?;
write!(f, " = ")?;
write_sep(f, ", ", list.iter().map(|(_, s)| s))?;
writeln!(f, ";")
}
@@ -141,7 +145,7 @@ impl Registry {
pub fn include<T: Type>(&mut self) -> &mut Self {
self.types
.insert(T::name().to_string())
.then(|| T::build(&mut TypeBuilder::new(self)));
.then(|| T::build(&mut TypeBuilder::new::<T>(self)));
self
}
@@ -149,7 +153,18 @@ impl Registry {
self.include::<T>()
.funcs
.insert(name.to_string())
.then(|| writeln!(self.cdef, "{};", T::cdecl(name)).unwrap());
.then(|| writeln!(self.cdef, "{};", T::extern_cdecl(name)).unwrap());
self
}
pub fn preload<T: Type>(&mut self, name: impl Display) -> &mut Self {
self.include::<T>();
writeln!(
self.lua,
r#"__preload["{name}"] = function(...) return __ct.{}(...); end;"#,
T::name()
)
.unwrap();
self
}
@@ -164,8 +179,8 @@ impl Display for Registry {
let version = env!("CARGO_PKG_VERSION");
writeln!(f, "--- automatically generated by {name} {version}")?;
cache_local(f, CACHE_LIBS)?;
cache_local(f, CACHE_GLOBALS)?;
writeln!(f, "__cdef [[{}]];", self.cdef)?;
cache_local(f, CACHE_LOCALS)?;
writeln!(f, "__cdef [[\n{}\n]];", self.cdef.trim())?;
write!(f, "{}", self.lua)
}
}
@@ -173,6 +188,10 @@ impl Display for Registry {
pub unsafe trait Type {
fn name() -> impl Display;
fn cdecl(name: impl Display) -> impl Display;
fn extern_cdecl(name: impl Display) -> impl Display {
Self::cdecl(name)
}
fn build(b: &mut TypeBuilder);
}
@@ -182,7 +201,10 @@ pub struct TypeBuilder<'r> {
}
impl<'r> TypeBuilder<'r> {
fn new(registry: &'r mut Registry) -> Self {
fn new<T: Type>(registry: &'r mut Registry) -> Self {
let ct = T::name();
let cdecl = T::cdecl("");
writeln!(registry.lua, r#"__ct.{ct} = __typeof("{cdecl}");"#).unwrap();
Self { registry }
}
@@ -283,7 +305,6 @@ pub unsafe trait Metatype {
pub struct MetatypeBuilder<'r> {
registry: &'r mut Registry,
name: String,
cdecl: String,
cdef: String,
lua: String,
}
@@ -293,7 +314,6 @@ impl<'r> MetatypeBuilder<'r> {
Self {
registry,
name: T::Target::name().to_string(),
cdecl: T::Target::cdecl("").to_string(),
cdef: String::new(),
lua: r#"do local __mt, __idx = {}, {}; __mt.__index = __idx; "#.into(),
}
@@ -325,7 +345,7 @@ impl<'r> MetatypeBuilder<'r> {
name: impl Display,
f: impl FnOnce(&mut MetatypeMethodBuilder),
) -> &mut Self {
write!(self.lua, "__mt.{name} = ").unwrap();
write!(self.lua, "__mt.__{name} = ").unwrap();
f(&mut MetatypeMethodBuilder::new(self));
write!(self.lua, "; ").unwrap();
self
@@ -342,7 +362,6 @@ impl<'r> Drop for MetatypeBuilder<'r> {
let Self {
registry,
name,
cdecl,
cdef,
lua,
..
@@ -350,12 +369,7 @@ impl<'r> Drop for MetatypeBuilder<'r> {
registry.cdef.push_str(cdef);
registry.lua.push_str(lua);
writeln!(
registry.lua,
r#"__ctypes.{name} = __metatype("{cdecl}", __mt); end;"#
)
.unwrap();
writeln!(registry.lua, r#"__metatype(__ct.{name}, __mt); end;"#).unwrap();
}
}
@@ -414,8 +428,6 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
}
pub fn param<T: FromFfi>(&mut self, name: impl Display) -> &mut Self {
self.metatype.registry.include::<T::From>();
(!self.params.is_empty()).then(|| self.params.push_str(", "));
(!self.args.is_empty()).then(|| self.args.push_str(", "));
write!(self.params, "{name}").unwrap();
@@ -450,6 +462,12 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
self
}
pub fn param_ignored(&mut self) -> &mut Self {
(!self.params.is_empty()).then(|| self.params.push_str(", "));
write!(self.params, "_").unwrap();
self
}
pub fn call<T: ToFfi>(&mut self, func: impl Display, ret: FfiReturnConvention) {
let Self {
metatype,
@@ -459,8 +477,6 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
postlude,
} = self;
metatype.registry.include::<T::To>();
let lua = &mut metatype.lua;
write!(lua, "function({params}) {prelude}").unwrap();
@@ -469,21 +485,21 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
write!(lua, "__C.{func}({args}); {postlude}end").unwrap();
}
FfiReturnConvention::ByValue => {
let check = T::postlude("res", ret);
let check = T::postlude("__res", ret);
write!(
lua,
"local res = __C.{func}({args}); {check}{postlude}return res; end"
"local __res = __C.{func}({args}); {check}{postlude}return __res; end"
)
.unwrap();
}
FfiReturnConvention::ByOutParam => {
let ct = T::To::name();
let check = T::postlude("res", ret);
write!(lua, "local res = __cnew(__ctypes.{ct}); __C.{func}(res").unwrap();
let check = T::postlude("__res", ret);
write!(lua, "local __res = __new(__ct.{ct}); __C.{func}(__res").unwrap();
if !args.is_empty() {
write!(lua, ", {args}").unwrap();
}
write!(lua, "); {check}{postlude}return res; end").unwrap()
write!(lua, "); {check}{postlude}return __res; end").unwrap()
}
}
}
@@ -560,7 +576,7 @@ macro_rules! impl_copy_primitive {
#[allow(unused)]
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
disp(move |f| {
disp(move |f| Ok({
match conv {
FfiReturnConvention::Void => unreachable!(),
FfiReturnConvention::ByValue => {},
@@ -568,9 +584,7 @@ macro_rules! impl_copy_primitive {
// the cdata containing the value and convert it to the equivalent lua value
FfiReturnConvention::ByOutParam => { $(write!(f, "{ret} = {}; ", $unwrap(ret))?;)? },
}
Ok(())
})
}))
}
}
};
@@ -592,7 +606,7 @@ impl_copy_primitive!(c_double, "double", "number", |n| display!("tonumber({n})")
unsafe impl<T: Type> Type for *const T {
fn name() -> impl Display {
display!("ptr_const_{}", T::name())
display!("const_{}_ptr", T::name())
}
fn cdecl(name: impl Display) -> impl Display {
@@ -606,7 +620,7 @@ unsafe impl<T: Type> Type for *const T {
unsafe impl<T: Type> Type for *mut T {
fn name() -> impl Display {
display!("ptr_mut_{}", T::name())
display!("{}_ptr", T::name())
}
fn cdecl(name: impl Display) -> impl Display {
@@ -684,7 +698,7 @@ unsafe impl<T: Type> ToFfi for *mut T {
//
unsafe impl<T: Type> Type for &T {
fn name() -> impl Display {
display!("ref_const_{}", T::name())
display!("const_{}_ptr", T::name())
}
fn cdecl(name: impl Display) -> impl Display {
@@ -698,7 +712,7 @@ unsafe impl<T: Type> Type for &T {
unsafe impl<T: Type> Type for &mut T {
fn name() -> impl Display {
display!("ref_mut_{}", T::name())
display!("{}_ptr", T::name())
}
fn cdecl(name: impl Display) -> impl Display {
@@ -782,7 +796,7 @@ unsafe impl<T: Type> FromFfi for &mut T {
//
unsafe impl<T: Type> Type for [T] {
fn name() -> impl Display {
display!("arr_{}", T::name())
display!("{}_arr", T::name())
}
fn cdecl(name: impl Display) -> impl Display {
@@ -796,7 +810,7 @@ unsafe impl<T: Type> Type for [T] {
unsafe impl<T: Type, const N: usize> Type for [T; N] {
fn name() -> impl Display {
display!("arr{N}_{}", T::name())
display!("{}_arr{N}", T::name())
}
fn cdecl(name: impl Display) -> impl Display {
@@ -824,20 +838,29 @@ macro_rules! impl_function {
//
unsafe impl<$($arg: Type,)* $ret: Type> Type for $($type)+ {
fn name() -> impl Display {
disp(|f| {
write!(f, "fn")?;
disp(|f| Ok({
write!(f, "fn_{}", $ret::name())?;
$(write!(f, "_{}", $arg::name())?;)*
write!(f, "_{}", $ret::name())
})
}))
}
fn cdecl(name: impl Display) -> impl Display {
$ret::cdecl(disp(move |f| {
$ret::cdecl(disp(move |f| Ok({
let mut _n = 0;
write!(f, "(*{name})(")?;
$(if _n != 0 { write!(f, ", ")?; } write!(f, "{}", $arg::cdecl(""))?; _n += 1;)*
write!(f, ")")
}))
write!(f, ")")?;
})))
}
fn extern_cdecl(name: impl Display) -> impl Display {
$ret::cdecl(disp(move |f| Ok({
// for top-level function declarations in cdef
let mut _n = 0;
write!(f, "{name}(")?;
$(if _n != 0 { write!(f, ", ")?; } write!(f, "{}", $arg::cdecl(""))?; _n += 1;)*
write!(f, ")")?;
})))
}
fn build(b: &mut TypeBuilder) {

View File

@@ -39,7 +39,7 @@ unsafe impl<T: FromFfi> FromFfi for Option<T> {
fn prelude(arg: &str) -> impl Display {
let ct = Self::From::name();
display!(
"if {arg} == nil then {arg} = __cnew(__ctypes.{ct}); else {}{arg} = __cnew(__ctypes.{ct}, 1, {arg}); end; ",
"if {arg} == nil then {arg} = __new(__ct.{ct}); else {}{arg} = __new(__ct.{ct}, 1, {arg}); end; ",
T::prelude(arg)
)
}

View File

@@ -5,17 +5,12 @@ use std::{fmt, ptr, slice};
#[derive(Debug, Clone, Copy)]
#[cdef]
pub struct lua_buf {
__ptr: *mut u8,
__ptr: *const u8,
__len: usize,
}
#[metatype]
impl lua_buf {
#[new]
extern "Lua-C" fn new() -> u32 {
todo!()
}
}
impl lua_buf {}
unsafe impl FromFfi for *const [u8] {
type From = lua_buf;
@@ -33,7 +28,7 @@ unsafe impl FromFfi for *const [u8] {
f,
r#"if {arg} ~= nil then assert(type({arg}) == "string", "string expected in argument '{arg}', got " .. type({arg})); "#
)?;
write!(f, "{arg} = __cnew(__ctypes.{ct}, {arg}, #{arg}); end; ")
write!(f, "{arg} = __new(__ct.{ct}, {arg}, #{arg}); end; ")
})
}
@@ -69,7 +64,7 @@ unsafe impl FromFfi for &str {
f,
r#"assert(__C.{IS_UTF8_FN}({arg}, #{arg}), "argument '{arg}' must be a valid utf-8 string"); "#
)?;
write!(f, "{arg} = __cnew(__ctypes.{ct}, {arg}, #{arg}); ")
write!(f, "{arg} = __new(__ct.{ct}, {arg}, #{arg}); ")
})
}