1017 lines
28 KiB
Rust
1017 lines
28 KiB
Rust
use crate::{
|
|
__internal::{disp, display, export, write_sep},
|
|
string::{DROP_BUFFER_FN, IS_UTF8_FN, lua_buffer},
|
|
};
|
|
pub use luaffi_impl::*;
|
|
use std::{
|
|
collections::HashSet,
|
|
ffi::{c_double, c_float, c_void},
|
|
fmt::{self, Display, Formatter, Write},
|
|
marker::PhantomData,
|
|
mem,
|
|
};
|
|
|
|
pub mod future;
|
|
pub mod string;
|
|
|
|
#[doc(hidden)]
|
|
#[path = "./internal.rs"]
|
|
pub mod __internal;
|
|
pub mod result;
|
|
|
|
// Dummy function to ensure that strings passed to Rust via wrapper objects will not be
|
|
// garbage-collected until the end of the function (used in string.rs when string marshalling is
|
|
// going through the slow-path). This shall exist until LuaJIT one day implements something like
|
|
// `ffi.keep(obj)`.
|
|
//
|
|
// https://github.com/LuaJIT/LuaJIT/issues/1167
|
|
pub(crate) const KEEP_FN: &str = "luaffi_keep";
|
|
#[unsafe(export_name = "luaffi_keep")]
|
|
extern "C" fn __keep(_ptr: *const c_void) {}
|
|
export![__keep];
|
|
|
|
const CACHE_LIBS: &[(&str, &str)] = &[
|
|
("table", "table"),
|
|
("string", "string"),
|
|
("math", "math"),
|
|
("coroutine", "coroutine"),
|
|
("package", "package"),
|
|
("debug", "debug"),
|
|
("jit", "jit"),
|
|
// require
|
|
("bit", r#"require("bit")"#),
|
|
("ffi", r#"require("ffi")"#),
|
|
("__tnew", r#"require("table.new")"#),
|
|
("__tclear", r#"require("table.clear")"#),
|
|
];
|
|
|
|
// https://www.lua.org/manual/5.1/manual.html#5.1
|
|
const CACHE_LOCALS: &[(&str, &str)] = &[
|
|
// base
|
|
("assert", "assert"),
|
|
("error", "error"),
|
|
("type", "type"),
|
|
("print", "print"),
|
|
("pcall", "pcall"),
|
|
("xpcall", "xpcall"),
|
|
("getfenv", "getfenv"),
|
|
("setfenv", "setfenv"),
|
|
("getmetatable", "getmetatable"),
|
|
("setmetatable", "setmetatable"),
|
|
("pairs", "pairs"),
|
|
("ipairs", "ipairs"),
|
|
("next", "next"),
|
|
("rawget", "rawget"),
|
|
("rawset", "rawset"),
|
|
("rawequal", "rawequal"),
|
|
("select", "select"),
|
|
("tonumber", "tonumber"),
|
|
("tostring", "tostring"),
|
|
("require", "require"),
|
|
// table
|
|
("__tconcat", "table.concat"),
|
|
("__tinsert", "table.insert"),
|
|
("__tmaxn", "table.maxn"),
|
|
("__tremove", "table.remove"),
|
|
("__tsort", "table.sort"),
|
|
("__tpack", "table.pack"),
|
|
("__tunpack", "table.unpack"),
|
|
// string
|
|
("__slen", "string.len"),
|
|
("__sprintf", "string.format"),
|
|
("__ssub", "string.sub"),
|
|
("__sgsub", "string.gsub"),
|
|
("__sgmatch", "string.gmatch"),
|
|
("__sdump", "string.dump"),
|
|
// math (used in luaify! macro)
|
|
("__fmod", "math.fmod"),
|
|
// coroutine (used in future.rs)
|
|
("__yield", "coroutine.yield"),
|
|
// package
|
|
("__preload", "package.preload"),
|
|
// debug
|
|
("__traceback", "debug.traceback"),
|
|
("__registry", "debug.getregistry()"), // (used in lib.lua)
|
|
// ffi
|
|
("__C", "ffi.C"),
|
|
("__ct", "{}"),
|
|
("__cdef", "ffi.cdef"),
|
|
("__new", "ffi.new"),
|
|
("__typeof", "ffi.typeof"),
|
|
("__istype", "ffi.istype"),
|
|
("__metatype", "ffi.metatype"),
|
|
("__cast", "ffi.cast"),
|
|
("__gc", "ffi.gc"),
|
|
("__sizeof", "ffi.sizeof"),
|
|
("__alignof", "ffi.alignof"),
|
|
("__intern", "ffi.string"), // (used in string.rs)
|
|
// bit (used in luaify! macro)
|
|
("__bnot", "bit.bnot"),
|
|
("__band", "bit.band"),
|
|
("__bor", "bit.bor"),
|
|
("__bxor", "bit.bxor"),
|
|
("__blshift", "bit.lshift"),
|
|
("__brshift", "bit.rshift"),
|
|
("__barshift", "bit.arshift"),
|
|
("__brol", "bit.rol"),
|
|
("__bror", "bit.ror"),
|
|
("__bswap", "bit.bswap"),
|
|
];
|
|
|
|
fn cache_local(f: &mut Formatter, list: &[(&str, &str)]) -> fmt::Result {
|
|
write!(f, "local ")?;
|
|
write_sep(f, ", ", list.iter().map(|(s, _)| s))?;
|
|
write!(f, " = ")?;
|
|
write_sep(f, ", ", list.iter().map(|(_, s)| s))?;
|
|
writeln!(f, ";")
|
|
}
|
|
|
|
#[derive(Debug, Clone, Default)]
|
|
pub struct Registry {
|
|
types: HashSet<String>,
|
|
funcs: HashSet<String>,
|
|
cdef: String,
|
|
lua: String,
|
|
}
|
|
|
|
impl Registry {
|
|
pub fn new() -> Self {
|
|
let mut s = Self::default();
|
|
s.declare::<UnsafeExternCFn<(*const c_void,), ()>>(KEEP_FN);
|
|
s.declare::<UnsafeExternCFn<(*const u8, usize), bool>>(IS_UTF8_FN);
|
|
s.declare::<UnsafeExternCFn<(*mut lua_buffer,), ()>>(DROP_BUFFER_FN);
|
|
s
|
|
}
|
|
|
|
pub fn include<T: Type>(&mut self) -> &mut Self {
|
|
self.types
|
|
.insert(T::name().to_string())
|
|
.then(|| T::build(&mut TypeBuilder::new::<T>(self)));
|
|
self
|
|
}
|
|
|
|
pub fn declare<T: Type>(&mut self, name: impl Display) -> &mut Self {
|
|
assert!(T::ty() != TypeType::Void, "cannot declare void type");
|
|
self.include::<T>()
|
|
.funcs
|
|
.insert(name.to_string())
|
|
.then(|| writeln!(self.cdef, "{};", T::extern_cdecl(name)).unwrap());
|
|
self
|
|
}
|
|
|
|
pub fn preload<T: Type>(&mut self, name: impl Display) -> &mut Self {
|
|
assert!(T::ty() != TypeType::Void, "cannot declare void type");
|
|
self.include::<T>();
|
|
let ct = T::name();
|
|
writeln!(
|
|
self.lua,
|
|
r#"__preload["{name}"] = function(...) return __ct.{ct}(...); end;"#,
|
|
)
|
|
.unwrap();
|
|
self
|
|
}
|
|
|
|
pub fn done(&self) -> String {
|
|
self.to_string()
|
|
}
|
|
}
|
|
|
|
impl Display for Registry {
|
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
let name = env!("CARGO_PKG_NAME");
|
|
let version = env!("CARGO_PKG_VERSION");
|
|
writeln!(f, "--- automatically generated by {name} {version}")?;
|
|
cache_local(f, CACHE_LIBS)?;
|
|
cache_local(f, CACHE_LOCALS)?;
|
|
writeln!(f, "{}", include_str!("./lib.lua"))?;
|
|
writeln!(f, "__cdef [[\n{}\n]];", self.cdef.trim())?;
|
|
write!(f, "{}", self.lua)
|
|
}
|
|
}
|
|
|
|
pub unsafe trait Type {
|
|
fn name() -> impl Display;
|
|
fn ty() -> TypeType;
|
|
|
|
fn cdecl(name: impl Display) -> impl Display;
|
|
fn extern_cdecl(name: impl Display) -> impl Display {
|
|
Self::cdecl(name)
|
|
}
|
|
|
|
fn build(b: &mut TypeBuilder);
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub enum TypeType {
|
|
Void,
|
|
Primitive,
|
|
Aggregate,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TypeBuilder<'r> {
|
|
registry: &'r mut Registry,
|
|
}
|
|
|
|
impl<'r> TypeBuilder<'r> {
|
|
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 }
|
|
}
|
|
|
|
pub fn include<T: Type>(&mut self) -> &mut Self {
|
|
self.registry.include::<T>();
|
|
self
|
|
}
|
|
|
|
pub fn cdef<T: Cdef>(&mut self) -> &mut Self {
|
|
let mut b = CdefBuilder::new::<T>(self.registry);
|
|
<T as Cdef>::build(&mut b);
|
|
drop(b);
|
|
self
|
|
}
|
|
|
|
pub fn metatype<T: Metatype>(&mut self) -> &mut Self {
|
|
let mut b = MetatypeBuilder::new::<T>(self.registry);
|
|
<T as Metatype>::build(&mut b);
|
|
drop(b);
|
|
self
|
|
}
|
|
}
|
|
|
|
pub unsafe trait Cdef: Type {
|
|
fn build(b: &mut CdefBuilder);
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct CdefBuilder<'r> {
|
|
registry: &'r mut Registry,
|
|
cdef: String,
|
|
align: usize,
|
|
opaque: usize,
|
|
}
|
|
|
|
impl<'r> CdefBuilder<'r> {
|
|
fn new<T: Cdef>(registry: &'r mut Registry) -> Self {
|
|
Self {
|
|
registry,
|
|
cdef: format!("{} {{ ", T::cdecl("")),
|
|
align: mem::align_of::<T>(),
|
|
opaque: 0,
|
|
}
|
|
}
|
|
|
|
pub fn field<T: Type>(&mut self, name: impl Display) -> &mut Self {
|
|
assert!(T::ty() != TypeType::Void, "cannot declare void field");
|
|
self.registry.include::<T>();
|
|
self.field_raw(T::cdecl(name))
|
|
}
|
|
|
|
pub fn field_opaque(&mut self, size: usize) -> &mut Self {
|
|
let i = self.opaque;
|
|
self.field_raw(format_args!("uint8_t __opaque{i}[{size}]"));
|
|
self.opaque += 1;
|
|
self
|
|
}
|
|
|
|
pub fn field_raw(&mut self, cdecl: impl Display) -> &mut Self {
|
|
write!(self.cdef, "{cdecl}; ").unwrap();
|
|
self
|
|
}
|
|
|
|
pub fn inner_struct(&mut self, f: impl FnOnce(&mut CdefBuilder)) -> &mut Self {
|
|
self.cdef.push_str("struct { ");
|
|
f(self);
|
|
self.cdef.push_str("}; ");
|
|
self
|
|
}
|
|
|
|
pub fn inner_union(&mut self, f: impl FnOnce(&mut CdefBuilder)) -> &mut Self {
|
|
self.cdef.push_str("union { ");
|
|
f(self);
|
|
self.cdef.push_str("}; ");
|
|
self
|
|
}
|
|
}
|
|
|
|
impl<'r> Drop for CdefBuilder<'r> {
|
|
fn drop(&mut self) {
|
|
let Self {
|
|
registry,
|
|
cdef,
|
|
align,
|
|
..
|
|
} = self;
|
|
|
|
registry.cdef.push_str(cdef);
|
|
writeln!(registry.cdef, "}} __attribute__((aligned({align})));").unwrap();
|
|
}
|
|
}
|
|
|
|
pub unsafe trait Metatype {
|
|
type Target: Cdef;
|
|
fn build(b: &mut MetatypeBuilder);
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct MetatypeBuilder<'r> {
|
|
registry: &'r mut Registry,
|
|
ct: String,
|
|
cdef: String,
|
|
lua: String,
|
|
}
|
|
|
|
impl<'r> MetatypeBuilder<'r> {
|
|
fn new<T: Metatype>(registry: &'r mut Registry) -> Self {
|
|
Self {
|
|
registry,
|
|
ct: T::Target::name().to_string(),
|
|
cdef: String::new(),
|
|
lua: r#"do local __mt, __idx = {}, {}; __mt.__index = __idx; "#.into(),
|
|
}
|
|
}
|
|
|
|
pub fn declare<T: Type>(&mut self, name: impl Display) -> &mut Self {
|
|
self.registry.declare::<T>(name);
|
|
self
|
|
}
|
|
|
|
pub fn index(
|
|
&mut self,
|
|
name: impl Display,
|
|
f: impl FnOnce(&mut MetatypeMethodBuilder),
|
|
) -> &mut Self {
|
|
write!(self.lua, "__idx.{name} = ").unwrap();
|
|
f(&mut MetatypeMethodBuilder::new(self));
|
|
write!(self.lua, "; ").unwrap();
|
|
self
|
|
}
|
|
|
|
pub fn index_raw(&mut self, name: impl Display, value: impl Display) -> &mut Self {
|
|
write!(self.lua, "__idx.{name} = {value}; ").unwrap();
|
|
self
|
|
}
|
|
|
|
pub fn metatable(
|
|
&mut self,
|
|
name: impl Display,
|
|
f: impl FnOnce(&mut MetatypeMethodBuilder),
|
|
) -> &mut Self {
|
|
write!(self.lua, "__mt.__{name} = ").unwrap();
|
|
f(&mut MetatypeMethodBuilder::new(self));
|
|
write!(self.lua, "; ").unwrap();
|
|
self
|
|
}
|
|
|
|
pub fn metatable_raw(&mut self, name: impl Display, value: impl Display) -> &mut Self {
|
|
write!(self.lua, "__mt.__{name} = {value}; ").unwrap();
|
|
self
|
|
}
|
|
}
|
|
|
|
impl<'r> Drop for MetatypeBuilder<'r> {
|
|
fn drop(&mut self) {
|
|
let Self {
|
|
registry,
|
|
ct,
|
|
cdef,
|
|
lua,
|
|
..
|
|
} = self;
|
|
|
|
registry.cdef.push_str(cdef);
|
|
registry.lua.push_str(lua);
|
|
writeln!(registry.lua, r#"__metatype(__ct.{ct}, __mt); end;"#).unwrap();
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum FfiReturnConvention {
|
|
ByValue,
|
|
ByOutParam,
|
|
}
|
|
|
|
pub unsafe trait FromFfi: Sized {
|
|
type From: Type + Sized;
|
|
|
|
fn require_keepalive() -> bool {
|
|
false
|
|
}
|
|
|
|
fn prelude(_arg: &str) -> impl Display {
|
|
""
|
|
}
|
|
|
|
fn convert(from: Self::From) -> Self;
|
|
}
|
|
|
|
pub unsafe trait IntoFfi: Sized {
|
|
type Into: Type + Sized;
|
|
|
|
fn convention() -> FfiReturnConvention {
|
|
match Self::Into::ty() {
|
|
TypeType::Void | TypeType::Primitive => FfiReturnConvention::ByValue,
|
|
TypeType::Aggregate => FfiReturnConvention::ByOutParam,
|
|
}
|
|
}
|
|
|
|
fn postlude(_ret: &str) -> impl Display {
|
|
""
|
|
}
|
|
|
|
fn convert(self) -> Self::Into;
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct MetatypeMethodBuilder<'r, 'm> {
|
|
metatype: &'m mut MetatypeBuilder<'r>,
|
|
lparams: String, // parameters to the lua function
|
|
cparams: String, // parameters to the lua function
|
|
cargs: String, // arguments to the C call
|
|
prelude: String, // function body prelude
|
|
postlude: String, // function body postlude
|
|
}
|
|
|
|
impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
|
|
pub fn new(metatype: &'m mut MetatypeBuilder<'r>) -> Self {
|
|
Self {
|
|
metatype,
|
|
lparams: String::new(),
|
|
cparams: String::new(),
|
|
cargs: String::new(),
|
|
prelude: String::new(),
|
|
postlude: String::new(),
|
|
}
|
|
}
|
|
|
|
pub fn param<T: FromFfi>(&mut self, name: impl Display) -> &mut Self {
|
|
assert!(
|
|
T::From::ty() != TypeType::Void,
|
|
"cannot declare void parameter"
|
|
);
|
|
|
|
let Self {
|
|
metatype: MetatypeBuilder { registry, .. },
|
|
lparams,
|
|
cparams,
|
|
cargs,
|
|
prelude,
|
|
postlude,
|
|
..
|
|
} = self;
|
|
|
|
registry.include::<T::From>();
|
|
|
|
(!lparams.is_empty()).then(|| lparams.push_str(", "));
|
|
(!cparams.is_empty()).then(|| cparams.push_str(", "));
|
|
(!cargs.is_empty()).then(|| cargs.push_str(", "));
|
|
|
|
write!(lparams, "{name}").unwrap();
|
|
write!(cparams, "{}", T::From::cdecl(&name)).unwrap();
|
|
write!(cargs, "{name}").unwrap();
|
|
|
|
if T::require_keepalive() {
|
|
write!(prelude, "local __keep_{name} = {name}; ").unwrap();
|
|
write!(postlude, "__C.{KEEP_FN}(__keep_{name}); ").unwrap();
|
|
}
|
|
|
|
write!(prelude, "{}", T::prelude(&name.to_string())).unwrap();
|
|
self
|
|
}
|
|
|
|
pub fn param_str(&mut self, name: impl Display) -> &mut Self {
|
|
// fast-path for &str and &[u8]-like parameters
|
|
//
|
|
// this passes one lua `string` argument as two C `const uint8_t *ptr` and `uintptr_t len`
|
|
// arguments, bypassing the slower generic `&[u8]: FromFfi` path which constructs a
|
|
// temporary cdata to pass the string and its length in one argument
|
|
let Self {
|
|
lparams,
|
|
cparams,
|
|
cargs,
|
|
prelude,
|
|
..
|
|
} = self;
|
|
|
|
let param_ptr = <*const u8>::cdecl("ptr");
|
|
let param_len = usize::cdecl("len");
|
|
|
|
(!lparams.is_empty()).then(|| lparams.push_str(", "));
|
|
(!cparams.is_empty()).then(|| cparams.push_str(", "));
|
|
(!cargs.is_empty()).then(|| cargs.push_str(", "));
|
|
|
|
write!(lparams, "{name}").unwrap();
|
|
write!(cparams, "{param_ptr}, {param_len}",).unwrap();
|
|
write!(cargs, "{name}, __{name}_len").unwrap();
|
|
write!(prelude, "local __{name}_len = 0; ").unwrap();
|
|
write!(
|
|
prelude,
|
|
r#"if {name} ~= nil then assert(type({name}) == "string", "string expected in argument '{name}', got " .. type({name})); __{name}_len = #{name}; end; "#
|
|
)
|
|
.unwrap();
|
|
self
|
|
}
|
|
|
|
pub fn param_ignored(&mut self) -> &mut Self {
|
|
(!self.lparams.is_empty()).then(|| self.lparams.push_str(", "));
|
|
write!(self.lparams, "_").unwrap();
|
|
self
|
|
}
|
|
|
|
pub fn call<T: IntoFfi>(&mut self, func: impl Display) {
|
|
let Self {
|
|
metatype:
|
|
MetatypeBuilder {
|
|
registry,
|
|
cdef,
|
|
lua,
|
|
..
|
|
},
|
|
lparams,
|
|
cparams,
|
|
cargs,
|
|
prelude,
|
|
postlude,
|
|
..
|
|
} = self;
|
|
|
|
registry.include::<T::Into>();
|
|
write!(lua, "function({lparams}) {prelude}").unwrap();
|
|
|
|
match T::convention() {
|
|
FfiReturnConvention::ByValue => {
|
|
if T::Into::ty() == TypeType::Void {
|
|
write!(lua, "__C.{func}({cargs}); {postlude}end").unwrap();
|
|
} else {
|
|
let check = T::postlude("__res");
|
|
write!(lua, "local __res = __C.{func}({cargs}); ").unwrap();
|
|
write!(lua, "{check}{postlude}return __res; end").unwrap();
|
|
}
|
|
|
|
writeln!(cdef, "{};", T::Into::cdecl(display!("{func}({cparams})"))).unwrap();
|
|
}
|
|
FfiReturnConvention::ByOutParam => {
|
|
let ct = T::Into::name();
|
|
let check = T::postlude("__res");
|
|
write!(lua, "local __res = __new(__ct.{ct}); __C.{func}(__res").unwrap();
|
|
if !cargs.is_empty() {
|
|
write!(lua, ", {cargs}").unwrap();
|
|
}
|
|
write!(lua, "); {check}{postlude}return __res; end").unwrap();
|
|
write!(cdef, "void {func}({}", <*mut T::Into>::cdecl("out")).unwrap();
|
|
if !cparams.is_empty() {
|
|
write!(cdef, ", {cparams}").unwrap();
|
|
}
|
|
writeln!(cdef, ");").unwrap();
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn call_inferred<T: IntoFfi>(&mut self, func: impl Display, _infer: impl FnOnce() -> T) {
|
|
self.call::<T>(func)
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
macro_rules! impl_void {
|
|
($rty:ty) => {
|
|
unsafe impl Type for $rty {
|
|
fn name() -> impl Display {
|
|
"void"
|
|
}
|
|
|
|
fn ty() -> TypeType {
|
|
TypeType::Void
|
|
}
|
|
|
|
fn cdecl(name: impl Display) -> impl Display {
|
|
display!("void {name}")
|
|
}
|
|
|
|
fn build(_b: &mut TypeBuilder) {}
|
|
}
|
|
|
|
unsafe impl IntoFfi for $rty {
|
|
type Into = ();
|
|
fn convert(self) -> Self::Into {}
|
|
}
|
|
};
|
|
}
|
|
|
|
impl_void!(());
|
|
impl_void!(c_void);
|
|
|
|
macro_rules! impl_primitive {
|
|
($rty:ty, $cty:expr) => {
|
|
unsafe impl Type for $rty {
|
|
fn name() -> impl Display {
|
|
$cty
|
|
}
|
|
|
|
fn ty() -> TypeType {
|
|
TypeType::Primitive
|
|
}
|
|
|
|
fn cdecl(name: impl Display) -> impl Display {
|
|
display!("{} {name}", $cty)
|
|
}
|
|
|
|
fn build(_b: &mut TypeBuilder) {}
|
|
}
|
|
};
|
|
}
|
|
|
|
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 FromFfi for bool {
|
|
type From = bool;
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
unsafe impl IntoFfi for bool {
|
|
type Into = bool;
|
|
|
|
fn convert(self) -> Self::Into {
|
|
self
|
|
}
|
|
}
|
|
|
|
macro_rules! impl_number_fromabi {
|
|
($rty:ty) => {
|
|
unsafe impl FromFfi for $rty {
|
|
type From = 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
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
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_intoabi {
|
|
($rty:ty) => {
|
|
unsafe impl IntoFfi for $rty {
|
|
type Into = Self;
|
|
|
|
fn convert(self) -> Self::Into {
|
|
self
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
impl_number_intoabi!(u8);
|
|
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 {
|
|
($rty:ty) => {
|
|
unsafe impl IntoFfi for $rty {
|
|
type Into = Self;
|
|
|
|
fn convert(self) -> Self::Into {
|
|
self
|
|
}
|
|
|
|
fn postlude(ret: &str) -> impl Display {
|
|
// this isn't "correct" per se, but it's much more ergonomic to work with numbers in
|
|
// lua than with long longs wrapped in cdata. we gracefully accept the loss of
|
|
// precision here and that 53 bits of precision for big integers are enough. (the
|
|
// vain of Lua 5.3 integer subtype ;D )
|
|
display!("{ret} = tonumber({ret}); ")
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
impl_bigint_intoabi!(u64);
|
|
impl_bigint_intoabi!(i64);
|
|
#[cfg(target_pointer_width = "64")]
|
|
impl_bigint_intoabi!(usize);
|
|
#[cfg(target_pointer_width = "64")]
|
|
impl_bigint_intoabi!(isize);
|
|
|
|
macro_rules! impl_const_ptr {
|
|
($ty:ty) => {
|
|
unsafe impl<T: Type> Type for $ty {
|
|
fn name() -> impl Display {
|
|
display!("const_{}_ptr", T::name())
|
|
}
|
|
|
|
fn ty() -> TypeType {
|
|
TypeType::Primitive
|
|
}
|
|
|
|
fn cdecl(name: impl Display) -> impl Display {
|
|
T::cdecl(display!("const *{name}"))
|
|
}
|
|
|
|
fn build(b: &mut TypeBuilder) {
|
|
b.include::<T>();
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
impl_const_ptr!(*const T);
|
|
impl_const_ptr!(&T);
|
|
impl_const_ptr!(Option<&T>);
|
|
|
|
macro_rules! impl_mut_ptr {
|
|
($ty:ty) => {
|
|
unsafe impl<T: Type> Type for $ty {
|
|
fn name() -> impl Display {
|
|
display!("{}_ptr", T::name())
|
|
}
|
|
|
|
fn ty() -> TypeType {
|
|
TypeType::Primitive
|
|
}
|
|
|
|
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:
|
|
//
|
|
// * 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;
|
|
|
|
fn convert(from: Self::From) -> Self {
|
|
from
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
impl_ptr_fromabi!(*const T);
|
|
impl_ptr_fromabi!(*mut T);
|
|
impl_ptr_fromabi!(Option<&T>);
|
|
impl_ptr_fromabi!(Option<&mut T>);
|
|
|
|
//
|
|
// 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_intoabi {
|
|
($ty:ty) => {
|
|
unsafe impl<T: Type> IntoFfi for $ty {
|
|
type Into = Self;
|
|
|
|
fn convert(self) -> Self::Into {
|
|
self
|
|
}
|
|
|
|
fn postlude(ret: &str) -> impl Display {
|
|
display!("if {ret} == nil then {ret} = nil; end; ")
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
impl_ptr_intoabi!(*const T);
|
|
impl_ptr_intoabi!(*mut T);
|
|
impl_ptr_intoabi!(Option<&'static T>);
|
|
impl_ptr_intoabi!(Option<&'static 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).
|
|
//
|
|
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 Into = Self;
|
|
|
|
fn convert(self) -> Self::Into {
|
|
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).
|
|
//
|
|
// TODO: we could automatically convert them to tables and vice-versa
|
|
//
|
|
unsafe impl<T: Type> Type for [T] {
|
|
fn name() -> impl Display {
|
|
display!("{}_arr", T::name())
|
|
}
|
|
|
|
fn ty() -> TypeType {
|
|
TypeType::Aggregate
|
|
}
|
|
|
|
fn cdecl(name: impl Display) -> impl Display {
|
|
display!("{name}[]")
|
|
}
|
|
|
|
fn build(b: &mut TypeBuilder) {
|
|
b.include::<T>();
|
|
}
|
|
}
|
|
|
|
unsafe impl<T: Type, const N: usize> Type for [T; N] {
|
|
fn name() -> impl Display {
|
|
display!("{}_arr{N}", T::name())
|
|
}
|
|
|
|
fn ty() -> TypeType {
|
|
TypeType::Aggregate
|
|
}
|
|
|
|
fn cdecl(name: impl Display) -> impl Display {
|
|
display!("{name}[{N}]")
|
|
}
|
|
|
|
fn build(b: &mut TypeBuilder) {
|
|
b.include::<T>();
|
|
}
|
|
}
|
|
|
|
pub struct UnsafeExternCFn<In, Out>(PhantomData<unsafe extern "C" fn(In) -> Out>);
|
|
|
|
macro_rules! impl_function {
|
|
(fn($($arg:tt),*) -> $ret:tt) => {
|
|
impl_function!(UnsafeExternCFn, fn($($arg),*) -> $ret);
|
|
};
|
|
|
|
($ty:tt, fn($($arg:tt),*) -> $ret:tt) => {
|
|
//
|
|
// SAFETY: No `FromFfi` for function pointers because of borrow safety invariants (see above
|
|
// in `&mut T`).
|
|
//
|
|
// We also can't implement `IntoFfi` because we can't call `FromFfi` and `IntoFfi` for the
|
|
// function's respective argument and return values.
|
|
//
|
|
unsafe impl<$($arg: Type,)* $ret: Type> Type for $ty<($($arg,)*), $ret> {
|
|
fn name() -> impl Display {
|
|
disp(|f| Ok({
|
|
write!(f, "fn_{}", $ret::name())?;
|
|
$(write!(f, "__{}", $arg::name())?;)*
|
|
}))
|
|
}
|
|
|
|
fn ty() -> TypeType {
|
|
TypeType::Primitive
|
|
}
|
|
|
|
fn cdecl(name: impl Display) -> impl Display {
|
|
$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, ")")?;
|
|
})))
|
|
}
|
|
|
|
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) {
|
|
$(b.include::<$arg>();)*
|
|
b.include::<$ret>();
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
impl_function!(fn() -> Z);
|
|
impl_function!(fn(A) -> Z);
|
|
impl_function!(fn(A, B) -> Z);
|
|
impl_function!(fn(A, B, C) -> Z);
|
|
impl_function!(fn(A, B, C, D) -> Z);
|
|
impl_function!(fn(A, B, C, D, E) -> Z);
|
|
impl_function!(fn(A, B, C, D, E, F) -> Z);
|
|
impl_function!(fn(A, B, C, D, E, F, G) -> Z);
|