957 lines
26 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();
}
}
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 postlude(_ret: &str) -> impl Display {
""
}
fn convert(self) -> Self::Into;
}
#[derive(Debug)]
pub struct MetatypeMethodBuilder<'r, 'm> {
metatype: &'m mut MetatypeBuilder<'r>,
params: String, // parameters to the lua function
args: 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,
params: String::new(),
args: 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"
);
(!self.params.is_empty()).then(|| self.params.push_str(", "));
(!self.args.is_empty()).then(|| self.args.push_str(", "));
write!(self.params, "{name}").unwrap();
write!(self.args, "{name}").unwrap();
if T::require_keepalive() {
write!(self.prelude, "local __keep_{name} = {name}; ").unwrap();
write!(self.postlude, "__C.{KEEP_FN}(__keep_{name}); ").unwrap();
}
let name = name.to_string();
write!(self.prelude, "{}", T::prelude(&name)).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
(!self.params.is_empty()).then(|| self.params.push_str(", "));
(!self.args.is_empty()).then(|| self.args.push_str(", "));
write!(self.params, "{name}").unwrap();
write!(self.args, "{name}, __{name}_len").unwrap();
write!(self.prelude, "local __{name}_len = 0; ").unwrap();
write!(
self.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.params.is_empty()).then(|| self.params.push_str(", "));
write!(self.params, "_").unwrap();
self
}
pub fn call<T: IntoFfi>(&mut self, func: impl Display) {
let Self {
metatype,
params,
args,
prelude,
postlude,
} = self;
let lua = &mut metatype.lua;
write!(lua, "function({params}) {prelude}").unwrap();
match T::Into::ty() {
TypeType::Void => {
write!(lua, "__C.{func}({args}); {postlude}end").unwrap();
}
TypeType::Primitive => {
let check = T::postlude("__res");
write!(
lua,
"local __res = __C.{func}({args}); {check}{postlude}return __res; end"
)
.unwrap();
}
TypeType::Aggregate => {
let ct = T::Into::name();
let check = T::postlude("__res");
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()
}
}
}
}
//
// 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);