Implement the foundations for annotation generation

This commit is contained in:
lumi 2025-06-28 04:15:27 +10:00
parent 6a4c726965
commit e05e2f4cb3
Signed by: luaneko
GPG Key ID: 406809B8763FF07A
9 changed files with 510 additions and 268 deletions

View File

@ -32,7 +32,7 @@ impl lb_chanlib {
(send, recv) (send, recv)
} }
extern "Lua" fn bounded(self, cap: number) { extern "Lua" fn bounded(self, cap: u32) {
assert(cap >= 0, "channel capacity must be nonnegative"); assert(cap >= 0, "channel capacity must be nonnegative");
let (send, recv) = (__new(__ct.lb_sender), __new(__ct.lb_receiver)); let (send, recv) = (__new(__ct.lb_sender), __new(__ct.lb_receiver));
self.__bounded(cap, send, recv); self.__bounded(cap, send, recv);

View File

@ -7,7 +7,7 @@
//! //!
//! See [`lb_netlib`] for items exported by this library. //! See [`lb_netlib`] for items exported by this library.
use derive_more::{From, FromStr}; use derive_more::{From, FromStr};
use luaffi::{cdef, metatype}; use luaffi::{cdef, marker::OneOf, metatype};
use std::{ use std::{
net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
time::Duration, time::Duration,
@ -82,7 +82,10 @@ impl lb_netlib {
/// If `s` is an [`lb_ipaddr`], a copy of that value is returned. If `s` is an /// If `s` is an [`lb_ipaddr`], a copy of that value is returned. If `s` is an
/// [`lb_socketaddr`], the IP address part of the socket address is returned. Otherwise, parses /// [`lb_socketaddr`], the IP address part of the socket address is returned. Otherwise, parses
/// `s` as an IP address string. Both IPv4 or IPv6 addresses are supported. /// `s` as an IP address string. Both IPv4 or IPv6 addresses are supported.
pub extern "Lua" fn ipaddr(&self, addr: any) -> Result<lb_ipaddr> { pub extern "Lua" fn ipaddr(
&self,
addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>,
) -> Result<lb_ipaddr> {
if __istype(__ct.lb_ipaddr, addr) { if __istype(__ct.lb_ipaddr, addr) {
__new(__ct.lb_ipaddr, addr) // copy constructor __new(__ct.lb_ipaddr, addr) // copy constructor
} else if __istype(__ct.lb_socketaddr, addr) { } else if __istype(__ct.lb_socketaddr, addr) {
@ -105,7 +108,11 @@ impl lb_netlib {
/// socket address string. Both IPv4 and IPv6 addresses are supported. /// socket address string. Both IPv4 and IPv6 addresses are supported.
/// ///
/// If `port` is not specified, `0` is used as the default. /// If `port` is not specified, `0` is used as the default.
pub extern "Lua" fn socketaddr(&self, addr: any, port: any) -> Result<lb_socketaddr> { pub extern "Lua" fn socketaddr(
&self,
addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>,
port: Option<u16>,
) -> Result<lb_socketaddr> {
if port != () { if port != () {
self.__new_skaddr(self.ipaddr(addr), port) self.__new_skaddr(self.ipaddr(addr), port)
} else { } else {
@ -145,7 +152,11 @@ impl lb_netlib {
Ok(Some(TcpSocket::new_v6()?).into()) Ok(Some(TcpSocket::new_v6()?).into())
} }
pub async extern "Lua" fn bind_tcp(&self, addr: any, port: any) -> Result<lb_tcpsocket> { pub async extern "Lua" fn bind_tcp(
&self,
addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>,
port: Option<u16>,
) -> Result<lb_tcpsocket> {
let addr = self.socketaddr(addr, port); let addr = self.socketaddr(addr, port);
let socket; let socket;
if addr.ip().is_v6() { if addr.ip().is_v6() {
@ -157,7 +168,11 @@ impl lb_netlib {
socket socket
} }
pub async extern "Lua" fn connect_tcp(&self, addr: any, port: any) -> Result<lb_tcpstream> { pub async extern "Lua" fn connect_tcp(
&self,
addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>,
port: Option<u16>,
) -> Result<lb_tcpstream> {
let addr = self.socketaddr(addr, port); let addr = self.socketaddr(addr, port);
let socket; let socket;
if addr.ip().is_v6() { if addr.ip().is_v6() {
@ -168,7 +183,11 @@ impl lb_netlib {
socket.connect(addr) socket.connect(addr)
} }
pub async extern "Lua" fn listen_tcp(&self, addr: any, port: any) -> Result<lb_tcplistener> { pub async extern "Lua" fn listen_tcp(
&self,
addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>,
port: Option<u16>,
) -> Result<lb_tcplistener> {
self.bind_tcp(addr, port).listen(1024) self.bind_tcp(addr, port).listen(1024)
} }
} }
@ -208,7 +227,7 @@ impl lb_ipaddr {
} }
/// Returns the string `"v4"` if this is an IPv4 address or `"v6"` if this is an IPv6 address. /// Returns the string `"v4"` if this is an IPv4 address or `"v6"` if this is an IPv6 address.
pub extern "Lua" fn family(&self) -> string { pub extern "Lua" fn family(&self) -> String {
if self.is_v6() { "v6" } else { "v4" } if self.is_v6() { "v6" } else { "v4" }
} }
@ -313,7 +332,10 @@ impl lb_socketaddr {
/// Sets the IP part of this address. /// Sets the IP part of this address.
/// ///
/// This function accepts the same arguments as [`ipaddr`](lb_netlib::ipaddr). /// This function accepts the same arguments as [`ipaddr`](lb_netlib::ipaddr).
pub extern "Lua" fn set_ip(&mut self, addr: any) -> &mut Self { pub extern "Lua" fn set_ip(
&mut self,
addr: OneOf<(&lb_ipaddr, &lb_socketaddr, &str)>,
) -> &mut Self {
if __istype(__ct.lb_ipaddr, addr) { if __istype(__ct.lb_ipaddr, addr) {
self.__set_ip(addr); self.__set_ip(addr);
} else if __istype(__ct.lb_socketaddr, addr) { } else if __istype(__ct.lb_socketaddr, addr) {
@ -338,7 +360,7 @@ impl lb_socketaddr {
} }
/// Sets the port part of this address. /// Sets the port part of this address.
pub extern "Lua" fn set_port(&mut self, port: integer) -> &mut Self { pub extern "Lua" fn set_port(&mut self, port: u16) -> &mut Self {
self.__set_port(port); self.__set_port(port);
self self
} }

View File

@ -7,7 +7,11 @@
//! //!
//! See [`lb_tasklib`] for items exported by this library. //! See [`lb_tasklib`] for items exported by this library.
use crate::runtime::spawn; use crate::runtime::spawn;
use luaffi::{cdef, metatype}; use luaffi::{
cdef,
marker::{function, many},
metatype,
};
use luajit::{LUA_MULTRET, Type}; use luajit::{LUA_MULTRET, Type};
use std::{ffi::c_int, time::Duration}; use std::{ffi::c_int, time::Duration};
use tokio::{task::JoinHandle, time::sleep}; use tokio::{task::JoinHandle, time::sleep};

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
__internal::{display, type_id}, __internal::{display, type_id},
Cdef, CdefBuilder, FfiReturnConvention, IntoFfi, Metatype, MetatypeBuilder, Type, TypeBuilder, Cdef, CdefBuilder, ExternCFn, FfiReturnConvention, IntoFfi, Metatype, MetatypeBuilder, Type,
TypeType, UnsafeExternCFn, TypeBuilder, TypeType,
}; };
use luaify::luaify; use luaify::luaify;
use std::{ use std::{
@ -152,8 +152,8 @@ unsafe impl<F: Future<Output: IntoFfi> + 'static> Type for lua_future<F> {
unsafe impl<F: Future<Output: IntoFfi> + 'static> Cdef for lua_future<F> { unsafe impl<F: Future<Output: IntoFfi> + 'static> Cdef for lua_future<F> {
fn build(s: &mut CdefBuilder) { fn build(s: &mut CdefBuilder) {
s.field_opaque(mem::offset_of!(Self, take)) // opaque .sig, .poll and .state s.field_opaque(mem::offset_of!(Self, take)) // opaque .sig, .poll and .state
.field::<UnsafeExternCFn<(&mut Self,), <F::Output as IntoFfi>::Into>>("__take") .field::<ExternCFn<(&mut Self,), <F::Output as IntoFfi>::Into>>("__take")
.field::<UnsafeExternCFn<(&mut Self,), ()>>("__drop"); .field::<ExternCFn<(&mut Self,), ()>>("__drop");
} }
} }

View File

@ -6,24 +6,6 @@ use std::{
hash::{Hash, Hasher}, hash::{Hash, Hasher},
}; };
#[allow(non_camel_case_types)]
pub mod stub_types {
pub struct any;
pub struct many;
pub struct nil;
pub type boolean = bool;
pub struct lightuserdata;
pub struct number;
pub struct integer;
pub type string = String;
pub struct table;
pub struct function;
pub struct userdata;
pub struct thread;
pub struct cdata;
pub struct variadic;
}
pub fn type_id<T: 'static>() -> u64 { pub fn type_id<T: 'static>() -> u64 {
let mut hash = FxHasher::default(); let mut hash = FxHasher::default();
TypeId::of::<T>().hash(&mut hash); TypeId::of::<T>().hash(&mut hash);

View File

@ -15,6 +15,7 @@ use std::{
#[path = "./internal.rs"] #[path = "./internal.rs"]
pub mod __internal; pub mod __internal;
pub mod future; pub mod future;
pub mod marker;
pub mod option; pub mod option;
pub mod result; pub mod result;
pub mod string; pub mod string;
@ -136,9 +137,9 @@ pub struct Registry {
impl Registry { impl Registry {
pub fn new() -> Self { pub fn new() -> Self {
let mut s = Self::default(); let mut s = Self::default();
s.declare::<UnsafeExternCFn<(*const c_void,), ()>>(KEEP_FN); s.declare::<ExternCFn<(*const c_void,), ()>>(KEEP_FN);
s.declare::<UnsafeExternCFn<(*const u8, usize), bool>>(IS_UTF8_FN); s.declare::<ExternCFn<(*const u8, usize), bool>>(IS_UTF8_FN);
s.declare::<UnsafeExternCFn<(*mut lua_buffer,), ()>>(DROP_BUFFER_FN); s.declare::<ExternCFn<(*mut lua_buffer,), ()>>(DROP_BUFFER_FN);
s s
} }
@ -603,6 +604,10 @@ impl<'r, 'm> MetatypeFunctionBuilder<'r, 'm> {
} }
} }
pub trait Annotation {
fn annotation() -> impl Display;
}
// //
// SAFETY: Unit type return maps to a C void return, which is a nil return in lua. There is no // 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. // equivalent to passing a unit type as an argument in C.
@ -636,7 +641,7 @@ impl_void!(());
impl_void!(c_void); impl_void!(c_void);
macro_rules! impl_primitive { macro_rules! impl_primitive {
($rty:ty, $cty:expr) => { ($rty:ty, $cty:expr, $lty:expr) => {
unsafe impl Type for $rty { unsafe impl Type for $rty {
fn name() -> impl Display { fn name() -> impl Display {
$cty $cty
@ -652,22 +657,28 @@ macro_rules! impl_primitive {
fn build(_b: &mut TypeBuilder) {} fn build(_b: &mut TypeBuilder) {}
} }
impl Annotation for $rty {
fn annotation() -> impl Display {
$lty
}
}
}; };
} }
impl_primitive!(bool, "bool"); impl_primitive!(bool, "bool", "boolean");
impl_primitive!(u8, "uint8_t"); impl_primitive!(u8, "uint8_t", "number");
impl_primitive!(u16, "uint16_t"); impl_primitive!(u16, "uint16_t", "number");
impl_primitive!(u32, "uint32_t"); impl_primitive!(u32, "uint32_t", "number");
impl_primitive!(u64, "uint64_t"); impl_primitive!(u64, "uint64_t", "number");
impl_primitive!(usize, "uintptr_t"); impl_primitive!(usize, "uintptr_t", "number");
impl_primitive!(i8, "int8_t"); impl_primitive!(i8, "int8_t", "number");
impl_primitive!(i16, "int16_t"); impl_primitive!(i16, "int16_t", "number");
impl_primitive!(i32, "int32_t"); impl_primitive!(i32, "int32_t", "number");
impl_primitive!(i64, "int64_t"); impl_primitive!(i64, "int64_t", "number");
impl_primitive!(isize, "intptr_t"); impl_primitive!(isize, "intptr_t", "number");
impl_primitive!(c_float, "float"); impl_primitive!(c_float, "float", "number");
impl_primitive!(c_double, "double"); impl_primitive!(c_double, "double", "number");
unsafe impl FromFfi for bool { unsafe impl FromFfi for bool {
type From = bool; type From = bool;
@ -772,11 +783,19 @@ impl_bigint_intoabi!(usize);
#[cfg(target_pointer_width = "64")] #[cfg(target_pointer_width = "64")]
impl_bigint_intoabi!(isize); impl_bigint_intoabi!(isize);
macro_rules! impl_const_ptr { macro_rules! impl_ptr {
($ty:ty) => { ($ty:ty, $mutable:expr) => {
unsafe impl<T: Type> Type for $ty { unsafe impl<T> Type for $ty
where
T: Type,
{
fn name() -> impl Display { fn name() -> impl Display {
display!("const_{}_ptr", T::name()) disp(|f| {
if !$mutable {
write!(f, "const_")?;
}
write!(f, "{}_ptr", T::name())
})
} }
fn ty() -> TypeType { fn ty() -> TypeType {
@ -784,7 +803,12 @@ macro_rules! impl_const_ptr {
} }
fn cdecl(name: impl Display) -> impl Display { fn cdecl(name: impl Display) -> impl Display {
T::cdecl(display!("const *{name}")) T::cdecl(disp(move |f| {
if !$mutable {
write!(f, "const ")?;
}
write!(f, "*{name}")
}))
} }
fn build(b: &mut TypeBuilder) { fn build(b: &mut TypeBuilder) {
@ -794,35 +818,44 @@ macro_rules! impl_const_ptr {
}; };
} }
impl_const_ptr!(*const T); impl_ptr!(&T, false);
impl_const_ptr!(&T); impl_ptr!(&mut T, true);
impl_const_ptr!(Option<&T>); impl_ptr!(*const T, false);
impl_ptr!(*mut T, true);
impl_ptr!(Option<&T>, false);
impl_ptr!(Option<&mut T>, true);
macro_rules! impl_mut_ptr { macro_rules! impl_ptr_annotation {
($ty:ty) => { ($ty:ty) => {
unsafe impl<T: Type> Type for $ty { impl<T> Annotation for $ty
fn name() -> impl Display { where
display!("{}_ptr", T::name()) T: Annotation,
} {
fn annotation() -> impl Display {
fn ty() -> TypeType { "lightuserdata"
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_ptr_annotation!(*const T);
impl_mut_ptr!(&mut T); impl_ptr_annotation!(*mut T);
impl_mut_ptr!(Option<&mut T>);
macro_rules! impl_ref_annotation {
($ty:ty) => {
impl<T> Annotation for $ty
where
T: Annotation,
{
fn annotation() -> impl Display {
display!("{}", T::annotation())
}
}
};
}
impl_ref_annotation!(&T);
impl_ref_annotation!(&mut T);
// //
// SAFETY: Pass by value for pointers, which maps to a `cdata` argument in lua containing either: // SAFETY: Pass by value for pointers, which maps to a `cdata` argument in lua containing either:
@ -835,7 +868,10 @@ impl_mut_ptr!(Option<&mut T>);
// //
macro_rules! impl_ptr_fromabi { macro_rules! impl_ptr_fromabi {
($ty:ty) => { ($ty:ty) => {
unsafe impl<T: Type> FromFfi for $ty { unsafe impl<T> FromFfi for $ty
where
T: Type,
{
type From = Self; type From = Self;
fn convert(from: Self::From) -> Self { fn convert(from: Self::From) -> Self {
@ -857,7 +893,10 @@ impl_ptr_fromabi!(Option<&mut T>);
// //
macro_rules! impl_ptr_intoabi { macro_rules! impl_ptr_intoabi {
($ty:ty) => { ($ty:ty) => {
unsafe impl<T: Type> IntoFfi for $ty { unsafe impl<T> IntoFfi for $ty
where
T: Type,
{
type Into = Self; type Into = Self;
fn convert(self) -> Self::Into { fn convert(self) -> Self::Into {
@ -898,7 +937,10 @@ impl_ptr_intoabi!(Option<&'static mut T>);
// //
macro_rules! impl_ref_fromabi { macro_rules! impl_ref_fromabi {
($ty:ty) => { ($ty:ty) => {
unsafe impl<'s, T: Type> FromFfi for $ty { unsafe impl<'s, T> FromFfi for $ty
where
T: Type,
{
type From = Option<$ty>; type From = Option<$ty>;
fn prelude(arg: &str) -> impl Display { fn prelude(arg: &str) -> impl Display {
@ -928,7 +970,10 @@ impl_ref_fromabi!(&'s mut T);
// //
macro_rules! impl_ref_intoabi { macro_rules! impl_ref_intoabi {
($ty:ty) => { ($ty:ty) => {
unsafe impl<T: Type> IntoFfi for $ty { unsafe impl<T> IntoFfi for $ty
where
T: Type,
{
type Into = Self; type Into = Self;
fn convert(self) -> Self::Into { fn convert(self) -> Self::Into {
@ -947,7 +992,10 @@ impl_ref_intoabi!(&'static mut T);
// //
// TODO: we could automatically convert them to tables and vice-versa // TODO: we could automatically convert them to tables and vice-versa
// //
unsafe impl<T: Type> Type for [T] { unsafe impl<T> Type for [T]
where
T: Type,
{
fn name() -> impl Display { fn name() -> impl Display {
display!("{}_arr", T::name()) display!("{}_arr", T::name())
} }
@ -965,7 +1013,19 @@ unsafe impl<T: Type> Type for [T] {
} }
} }
unsafe impl<T: Type, const N: usize> Type for [T; N] { impl<T> Annotation for [T]
where
T: Annotation,
{
fn annotation() -> impl Display {
display!("{}[]", T::annotation())
}
}
unsafe impl<T, const N: usize> Type for [T; N]
where
T: Type,
{
fn name() -> impl Display { fn name() -> impl Display {
display!("{}_arr{N}", T::name()) display!("{}_arr{N}", T::name())
} }
@ -983,14 +1043,23 @@ unsafe impl<T: Type, const N: usize> Type for [T; N] {
} }
} }
pub struct UnsafeExternCFn<In, Out>(PhantomData<unsafe extern "C" fn(In) -> Out>); impl<T, const N: usize> Annotation for [T; N]
where
T: Annotation,
{
fn annotation() -> impl Display {
display!("{}[]", T::annotation())
}
}
macro_rules! impl_function { pub struct ExternCFn<I, O>(PhantomData<extern "C" fn(I) -> O>);
(fn($($arg:tt),*) -> $ret:tt) => {
impl_function!(UnsafeExternCFn, fn($($arg),*) -> $ret); macro_rules! impl_externcfn {
(fn($($arg:ident),*) -> $ret:ident) => {
impl_externcfn!(ExternCFn, fn($($arg),*) -> $ret);
}; };
($ty:tt, fn($($arg:tt),*) -> $ret:tt) => { ($ty:ident, fn($($arg:ident),*) -> $ret:ident) => {
// //
// SAFETY: No `FromFfi` for function pointers because of borrow safety invariants (see above // SAFETY: No `FromFfi` for function pointers because of borrow safety invariants (see above
// in `&mut T`). // in `&mut T`).
@ -998,7 +1067,11 @@ macro_rules! impl_function {
// We also can't implement `IntoFfi` because we can't call `FromFfi` and `IntoFfi` for the // We also can't implement `IntoFfi` because we can't call `FromFfi` and `IntoFfi` for the
// function's respective argument and return values. // function's respective argument and return values.
// //
unsafe impl<$($arg: Type,)* $ret: Type> Type for $ty<($($arg,)*), $ret> { unsafe impl<$($arg,)* $ret> Type for $ty<($($arg,)*), $ret>
where
$($arg: Type,)*
$ret: Type,
{
fn name() -> impl Display { fn name() -> impl Display {
disp(|f| Ok({ disp(|f| Ok({
write!(f, "fn_{}", $ret::name())?; write!(f, "fn_{}", $ret::name())?;
@ -1012,8 +1085,8 @@ macro_rules! impl_function {
fn cdecl(name: impl Display) -> impl Display { fn cdecl(name: impl Display) -> impl Display {
$ret::cdecl(disp(move |f| Ok({ $ret::cdecl(disp(move |f| Ok({
let mut _n = 0;
write!(f, "(*{name})(")?; write!(f, "(*{name})(")?;
let mut _n = 0;
$(if _n != 0 { write!(f, ", ")?; } write!(f, "{}", $arg::cdecl(""))?; _n += 1;)* $(if _n != 0 { write!(f, ", ")?; } write!(f, "{}", $arg::cdecl(""))?; _n += 1;)*
write!(f, ")")?; write!(f, ")")?;
}))) })))
@ -1022,8 +1095,8 @@ macro_rules! impl_function {
fn extern_cdecl(name: impl Display) -> impl Display { fn extern_cdecl(name: impl Display) -> impl Display {
$ret::cdecl(disp(move |f| Ok({ $ret::cdecl(disp(move |f| Ok({
// for top-level function declarations in cdef // for top-level function declarations in cdef
let mut _n = 0;
write!(f, "{name}(")?; write!(f, "{name}(")?;
let mut _n = 0;
$(if _n != 0 { write!(f, ", ")?; } write!(f, "{}", $arg::cdecl(""))?; _n += 1;)* $(if _n != 0 { write!(f, ", ")?; } write!(f, "{}", $arg::cdecl(""))?; _n += 1;)*
write!(f, ")")?; write!(f, ")")?;
}))) })))
@ -1037,11 +1110,25 @@ macro_rules! impl_function {
}; };
} }
impl_function!(fn() -> Z); impl_externcfn!(fn() -> A);
impl_function!(fn(A) -> Z); impl_externcfn!(fn(A) -> B);
impl_function!(fn(A, B) -> Z); impl_externcfn!(fn(A, B) -> C);
impl_function!(fn(A, B, C) -> Z); impl_externcfn!(fn(A, B, C) -> D);
impl_function!(fn(A, B, C, D) -> Z); impl_externcfn!(fn(A, B, C, D) -> E);
impl_function!(fn(A, B, C, D, E) -> Z); impl_externcfn!(fn(A, B, C, D, E) -> F);
impl_function!(fn(A, B, C, D, E, F) -> Z); impl_externcfn!(fn(A, B, C, D, E, F) -> G);
impl_function!(fn(A, B, C, D, E, F, G) -> Z); impl_externcfn!(fn(A, B, C, D, E, F, G) -> H);
impl_externcfn!(fn(A, B, C, D, E, F, G, H) -> I);
impl_externcfn!(fn(A, B, C, D, E, F, G, H, I) -> J);
impl<'s> Annotation for &'s str {
fn annotation() -> impl Display {
"string"
}
}
impl Annotation for String {
fn annotation() -> impl Display {
"string"
}
}

193
crates/luaffi/src/marker.rs Normal file
View File

@ -0,0 +1,193 @@
#![allow(non_camel_case_types)]
use crate::{
__internal::{disp, display},
Annotation,
};
use std::{fmt::Display, marker::PhantomData};
enum Marker {}
pub struct any(Marker);
impl Annotation for any {
fn annotation() -> impl Display {
"any"
}
}
pub struct many(Marker);
impl Annotation for many {
fn annotation() -> impl Display {
"..."
}
}
pub struct nil(Marker);
impl Annotation for nil {
fn annotation() -> impl Display {
"nil"
}
}
pub struct lightuserdata(Marker);
impl Annotation for lightuserdata {
fn annotation() -> impl Display {
"lightuserdata"
}
}
pub struct table<K, V>(Marker, PhantomData<*mut [(K, V)]>);
impl<K, V> Annotation for table<K, V>
where
K: Annotation,
V: Annotation,
{
fn annotation() -> impl Display {
display!("table<{}, {}>", K::annotation(), V::annotation())
}
}
pub struct function(Marker);
impl Annotation for function {
fn annotation() -> impl Display {
"function"
}
}
pub struct fun<I, O>(Marker, PhantomData<fn(I) -> O>);
macro_rules! impl_fun {
(fn($($arg:ident),*)) => {
impl<$($arg,)*> Annotation for fun<($($arg,)*), ()>
where
$($arg: Annotation,)*
{
fn annotation() -> impl Display {
disp(|f| {
write!(f, "fun(")?;
let mut _n = 0;
$(if _n != 0 { write!(f, ", ")?; } write!(f, "{}", $arg::annotation())?; _n += 1;)*
write!(f, ")")
})
}
}
};
(fn($($arg:ident),*) -> $ret:ident) => {
impl<$($arg,)* $ret> Annotation for fun<($($arg,)*), $ret>
where
$($arg: Annotation,)*
$ret: Annotation,
{
fn annotation() -> impl Display {
disp(|f| {
write!(f, "fun(")?;
let mut _n = 0;
$(if _n != 0 { write!(f, ", ")?; } write!(f, "{}", $arg::annotation())?; _n += 1;)*
write!(f, "): {}", $ret::annotation())
})
}
}
impl_fun!(fn($($arg),*));
};
}
impl_fun!(fn() -> A);
impl_fun!(fn(A) -> B);
impl_fun!(fn(A, B) -> C);
impl_fun!(fn(A, B, C) -> D);
impl_fun!(fn(A, B, C, D) -> E);
impl_fun!(fn(A, B, C, D, E) -> F);
impl_fun!(fn(A, B, C, D, E, F) -> G);
impl_fun!(fn(A, B, C, D, E, F, G) -> H);
impl_fun!(fn(A, B, C, D, E, F, G, H) -> I);
impl_fun!(fn(A, B, C, D, E, F, G, H, I) -> J);
pub struct userdata(Marker);
impl Annotation for userdata {
fn annotation() -> impl Display {
"userdata"
}
}
pub struct thread(Marker);
impl Annotation for thread {
fn annotation() -> impl Display {
"thread"
}
}
pub struct cdata(Marker);
impl Annotation for cdata {
fn annotation() -> impl Display {
"cdata"
}
}
pub struct Either<T, U>(Marker, PhantomData<(T, U)>);
impl<T, U> Annotation for Either<T, U>
where
T: Annotation,
U: Annotation,
{
fn annotation() -> impl Display {
display!("({} | {})", T::annotation(), U::annotation())
}
}
pub struct OneOf<X>(Marker, PhantomData<X>);
macro_rules! impl_oneof {
($($ty:ident),+) => {
impl<$($ty),+> Annotation for OneOf<($($ty,)+)>
where
$($ty: Annotation),+
{
fn annotation() -> impl Display {
disp(|f| {
write!(f, "(")?;
let mut _n = 0;
$(if _n != 0 { write!(f, " | ")?; } write!(f, "{}", $ty::annotation())?; _n += 1;)*
write!(f, ")")
})
}
}
};
}
impl_oneof!(A, B);
impl_oneof!(A, B, C);
impl_oneof!(A, B, C, D);
impl_oneof!(A, B, C, D, E);
impl_oneof!(A, B, C, D, E, F);
impl_oneof!(A, B, C, D, E, F, G);
impl_oneof!(A, B, C, D, E, F, G, H);
impl_oneof!(A, B, C, D, E, F, G, H, I);
impl<T> Annotation for Option<T>
where
T: Annotation,
{
fn annotation() -> impl Display {
display!("{}?", T::annotation())
}
}
impl<T, E> Annotation for Result<T, E>
where
T: Annotation,
{
fn annotation() -> impl Display {
display!("{}", T::annotation())
}
}

View File

@ -32,7 +32,7 @@ pub fn transform(args: Args, mut item: Item) -> Result<TokenStream> {
let mod_name = format_ident!("__{name}_cdef"); let mod_name = format_ident!("__{name}_cdef");
Ok(quote_spanned!(name.span() => Ok(quote!(
#[repr(C)] #[repr(C)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#item #item
@ -51,10 +51,9 @@ pub fn transform(args: Args, mut item: Item) -> Result<TokenStream> {
fn generate_type(ty: &Ident) -> Result<TokenStream> { fn generate_type(ty: &Ident) -> Result<TokenStream> {
let ffi = ffi_crate(); let ffi = ffi_crate();
let span = ty.span(); let name = ty.unraw().to_string();
let name = LitStr::new(&ty.unraw().to_string(), span);
Ok(quote_spanned!(span => Ok(quote!(
unsafe impl #ffi::Type for #ty { unsafe impl #ffi::Type for #ty {
fn name() -> impl ::std::fmt::Display { #name } fn name() -> impl ::std::fmt::Display { #name }
@ -71,6 +70,10 @@ fn generate_type(ty: &Ident) -> Result<TokenStream> {
} }
} }
impl #ffi::Annotation for #ty {
fn annotation() -> impl ::std::fmt::Display { #name }
}
// SAFETY: we can always implement `IntoFfi` because it transfers ownership from Rust to Lua // SAFETY: we can always implement `IntoFfi` because it transfers ownership from Rust to Lua
unsafe impl #ffi::IntoFfi for #ty { unsafe impl #ffi::IntoFfi for #ty {
type Into = Self; type Into = Self;
@ -82,7 +85,7 @@ fn generate_type(ty: &Ident) -> Result<TokenStream> {
fn generate_module(name: &str, ty: &Ident) -> Result<TokenStream> { fn generate_module(name: &str, ty: &Ident) -> Result<TokenStream> {
let ffi = ffi_crate(); let ffi = ffi_crate();
Ok(quote_spanned!(ty.span() => Ok(quote!(
impl #ffi::Module for #ty { impl #ffi::Module for #ty {
fn name() -> impl ::std::fmt::Display { #name } fn name() -> impl ::std::fmt::Display { #name }
} }
@ -98,10 +101,9 @@ fn generate_cdef_structure(str: &mut ItemStruct) -> Result<TokenStream> {
let ffi = ffi_crate(); let ffi = ffi_crate();
let ty = &str.ident; let ty = &str.ident;
let span = ty.span();
let build = generate_cdef_build(&get_cfields(&mut str.fields)?)?; let build = generate_cdef_build(&get_cfields(&mut str.fields)?)?;
Ok(quote_spanned!(span => Ok(quote!(
unsafe impl #ffi::Cdef for #ty { unsafe impl #ffi::Cdef for #ty {
fn build(b: &mut #ffi::CdefBuilder) { #build } fn build(b: &mut #ffi::CdefBuilder) { #build }
} }
@ -117,18 +119,16 @@ fn generate_cdef_enum(enu: &mut ItemEnum) -> Result<TokenStream> {
let ffi = ffi_crate(); let ffi = ffi_crate();
let ty = &enu.ident; let ty = &enu.ident;
let span = ty.span();
let build = enu let build = enu
.variants .variants
.iter_mut() .iter_mut()
.map(|variant| { .map(|variant| {
let span = variant.span();
let build = generate_cdef_build(&get_cfields(&mut variant.fields)?)?; let build = generate_cdef_build(&get_cfields(&mut variant.fields)?)?;
Ok(quote_spanned!(span => b.inner_struct(|b| { #build }))) Ok(quote!(b.inner_struct(|b| { #build })))
}) })
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
Ok(quote_spanned!(span => Ok(quote!(
unsafe impl #ffi::Cdef for #ty { unsafe impl #ffi::Cdef for #ty {
fn build(b: &mut #ffi::CdefBuilder) { fn build(b: &mut #ffi::CdefBuilder) {
b.field::<::std::ffi::c_int>("__tag").inner_union(|b| { #(#build;)* }); b.field::<::std::ffi::c_int>("__tag").inner_union(|b| { #(#build;)* });
@ -201,7 +201,7 @@ fn generate_cdef_build(fields: &[CField]) -> Result<TokenStream> {
for (i, field) in fields.iter().enumerate() { for (i, field) in fields.iter().enumerate() {
let ty = &field.ty; let ty = &field.ty;
let offset = offset(i); let offset = offset(i);
body.push(quote_spanned!(ty.span() => body.push(quote!(
// round up current offset to the alignment of field type for field offset // round up current offset to the alignment of field type for field offset
offset = (offset + #align_of::<#ty>() - 1) & !(#align_of::<#ty>() - 1); offset = (offset + #align_of::<#ty>() - 1) & !(#align_of::<#ty>() - 1);
align = #max(align, #align_of::<#ty>()); align = #max(align, #align_of::<#ty>());

View File

@ -21,7 +21,7 @@ pub fn transform(args: Args, mut imp: ItemImpl) -> Result<TokenStream> {
let impls = generate_impls(&args, &mut imp)?; let impls = generate_impls(&args, &mut imp)?;
let mod_name = format_ident!("__{}_metatype", ty_name(&imp.self_ty)?); let mod_name = format_ident!("__{}_metatype", ty_name(&imp.self_ty)?);
Ok(quote_spanned!(imp.self_ty.span() => Ok(quote!(
#imp #imp
#[doc(hidden)] #[doc(hidden)]
@ -119,7 +119,7 @@ fn generate_impls(_args: &Args, imp: &mut ItemImpl) -> Result<TokenStream> {
let build = &registry.build; let build = &registry.build;
let exports = generate_ffi_exports(&registry)?; let exports = generate_ffi_exports(&registry)?;
Ok(quote_spanned!(ty.span() => Ok(quote!(
impl #ty { #(#shims)* } impl #ty { #(#shims)* }
unsafe impl #ffi::Metatype for #ty { unsafe impl #ffi::Metatype for #ty {
@ -219,10 +219,10 @@ impl ToTokens for Metamethod {
struct FfiFunction { struct FfiFunction {
name: Ident, name: Ident,
is_async: bool, params: Vec<(Ident, Type)>,
params: Vec<PatType>,
ret: Type, ret: Type,
attrs: FfiFunctionAttrs, attrs: FfiFunctionAttrs,
is_async: bool,
} }
#[derive(Default)] #[derive(Default)]
@ -251,30 +251,31 @@ fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
.sig .sig
.inputs .inputs
.iter() .iter()
.map(|arg| match arg { .map(|arg| {
FnArg::Receiver(recv) => { Ok(match arg {
let ty = &recv.ty; FnArg::Receiver(recv) => {
parse_quote_spanned!(ty.span() => self: #ty) (Ident::new("self", recv.span()), (*recv.ty).clone())
} }
FnArg::Typed(ty) => ty.clone(), FnArg::Typed(ty) => (pat_ident(&ty.pat)?.clone(), (*ty.ty).clone()),
})
}) })
.collect(); .collect::<Result<_>>()?;
let ret = match func.sig.output { let ret = match func.sig.output {
ReturnType::Default => parse_quote!(()), ReturnType::Default => parse_quote!(()),
ReturnType::Type(_, ref ty) => (**ty).clone(), ReturnType::Type(_, ref ty) => (**ty).clone(),
}; };
for param in params.iter() { for (name, ty) in params.iter() {
// double underscores are reserved for generated glue code // double underscores are reserved for glue code
syn_assert!( syn_assert!(
!pat_ident(&param.pat)?.to_string().starts_with("__"), !name.to_string().starts_with("__"),
param.pat, name,
"parameter names should not start with `__`" "parameter names should not start with `__`"
); );
// lifetime should be determined by the caller (lua) // lifetime should be determined by the caller (which is lua)
if let Type::Reference(ref ty) = *param.ty { if let Type::Reference(ty) = ty {
syn_assert!( syn_assert!(
ty.lifetime.is_none(), ty.lifetime.is_none(),
ty.lifetime, ty.lifetime,
@ -290,10 +291,10 @@ fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
funcs.push(FfiFunction { funcs.push(FfiFunction {
name: func.sig.ident.clone(), name: func.sig.ident.clone(),
is_async: func.sig.asyncness.is_some(),
params, params,
ret, ret,
attrs, attrs,
is_async: func.sig.asyncness.is_some(),
}); });
} }
} }
@ -387,16 +388,8 @@ fn add_ffi_function(registry: &mut Registry, func: &FfiFunction) -> Result<()> {
let func_params = &func.params; // target function parameters let func_params = &func.params; // target function parameters
let func_ret = &func.ret; // target function return type let func_ret = &func.ret; // target function return type
let mut func_args = vec![]; // target function arguments let mut func_args = vec![]; // target function arguments
let mut shim_params = vec![]; // shim function parameters let mut shim_params = vec![]; // shim function parameters
let mut shim_ret = if func.is_async { let mut asserts = vec![]; // compile-time asserts
// shim function return type
quote_spanned!(func_ret.span() => #ffi::future::lua_future<impl ::std::future::Future<Output = #func_ret>>)
} else {
quote_spanned!(func_ret.span() => <#func_ret as #ffi::IntoFfi>::Into)
};
let mut asserts = vec![]; // compile-time builder asserts
let mut build = vec![]; // ffi builder body let mut build = vec![]; // ffi builder body
// for __new metamethods, ignore the first argument (ctype of self, for which there is no // for __new metamethods, ignore the first argument (ctype of self, for which there is no
@ -407,99 +400,102 @@ fn add_ffi_function(registry: &mut Registry, func: &FfiFunction) -> Result<()> {
)); ));
} }
for (i, param) in func_params.iter().enumerate() { for (i, (name, func_param)) in func_params.iter().enumerate() {
let func_param = &param.ty; let span = func_param.span();
let name = name.unraw().to_string();
let shim_param = format_ident!("arg{i}"); let shim_param = format_ident!("arg{i}");
let name = pat_ident(&param.pat)?.unraw().to_string();
match get_ffi_param_type(func_param) { match get_ffi_param_type(func_param) {
FfiParameterType::Default => { FfiParameterType::Default => {
shim_params.push(quote_spanned!(func_param.span() => shim_params.push(quote_spanned!(span =>
#shim_param: <#func_param as #ffi::FromFfi>::From #shim_param: <#func_param as #ffi::FromFfi>::From
)); ));
func_args.push(quote_spanned!(param.pat.span() => func_args.push(quote_spanned!(span =>
<#func_param as #ffi::FromFfi>::convert(#shim_param) <#func_param as #ffi::FromFfi>::convert(#shim_param)
)); ));
build.push(quote_spanned!(param.pat.span() => build.push(quote_spanned!(span =>
b.param::<#func_param>(#name); b.param::<#func_param>(#name);
)); ));
} }
ty @ (FfiParameterType::StringLike(str) | FfiParameterType::OptionStringLike(str)) => { ty @ (FfiParameterType::StringLike(str) | FfiParameterType::OptionStringLike(str)) => {
let shim_param_len = format_ident!("arg{i}_len"); let shim_param_len = format_ident!("arg{i}_len");
shim_params.push(quote_spanned!(func_param.span() => shim_params.push(quote_spanned!(span =>
#shim_param: ::std::option::Option<&::std::primitive::u8>, #shim_param: ::std::option::Option<&::std::primitive::u8>,
#shim_param_len: ::std::primitive::usize #shim_param_len: ::std::primitive::usize
)); ));
let allow_nil = matches!(ty, FfiParameterType::OptionStringLike(_)); let allow_nil = matches!(ty, FfiParameterType::OptionStringLike(_));
let check_utf8 = matches!(str, StringLike::Str); let check_utf8 = matches!(str, StringLike::Str);
let mut func_arg = quote_spanned!(func_param.span() => let mut func_arg = quote_spanned!(span =>
#shim_param.map(|s| ::std::slice::from_raw_parts(s, #shim_param_len)) #shim_param.map(|s| ::std::slice::from_raw_parts(s, #shim_param_len))
); );
func_arg = match str { func_arg = match str {
StringLike::SliceU8 => func_arg, StringLike::SliceU8 => func_arg,
StringLike::BStr => { StringLike::BStr => {
quote_spanned!(func_param.span() => #func_arg.map(::bstr::BStr::new)) quote_spanned!(span => #func_arg.map(::bstr::BStr::new))
} }
StringLike::Str => { StringLike::Str => {
quote_spanned!(func_param.span() => #func_arg.map(|s| { quote_spanned!(span => #func_arg.map(|s| {
::std::debug_assert!(::std::str::from_utf8(s).is_ok()); ::std::debug_assert!(::std::str::from_utf8(s).is_ok());
::std::str::from_utf8_unchecked(s) ::std::str::from_utf8_unchecked(s)
})) }))
} }
}; };
if !allow_nil { if !allow_nil {
func_arg = quote_spanned!(func_param.span() => { func_arg = quote_spanned!(span => {
let arg = #func_arg; let arg = #func_arg;
::std::debug_assert!(arg.is_some()); ::std::debug_assert!(arg.is_some());
arg.unwrap_unchecked() arg.unwrap_unchecked()
}); });
} }
func_args.push(func_arg); func_args.push(func_arg);
build.push(quote_spanned!(param.pat.span() => build.push(quote_spanned!(span =>
b.param_str(#name, #allow_nil, #check_utf8); b.param_str(#name, #allow_nil, #check_utf8);
)); ));
} }
} }
} }
// shim function body let func_call = quote!(Self::#func_name(#(#func_args),*)); // target function call
let mut shim_body = if func.is_async {
// for async functions, wrapped the returned future in lua_future
quote_spanned!(func_name.span() => #ffi::future::lua_future::new(Self::#func_name(#(#func_args),*)))
} else {
quote_spanned!(func_name.span() => <#func_ret as #ffi::IntoFfi>::convert(Self::#func_name(#(#func_args),*)))
};
if !func.is_async { // shim function body and return type
let (shim_body, shim_ret) = if func.is_async {
let span = func_ret.span();
(
// for async functions, wrapped the returned future in lua_future
quote_spanned!(span => #ffi::future::lua_future::new(#func_call)),
quote_spanned!(span => #ffi::future::lua_future<impl ::std::future::Future<Output = #func_ret>>),
)
} else {
let span = func_ret.span();
match get_ffi_ret_type(&func_ret) { match get_ffi_ret_type(&func_ret) {
FfiReturnType::Void => { FfiReturnType::Void => {
asserts.push(quote_spanned!(func_ret.span() => asserts.push(quote_spanned!(span =>
<<#func_ret as #ffi::IntoFfi>::Into as #ffi::Type>::ty() == #ffi::TypeType::Void <<#func_ret as #ffi::IntoFfi>::Into as #ffi::Type>::ty() == #ffi::TypeType::Void
)); ));
(func_call, quote_spanned!(span => ()))
} }
FfiReturnType::ByValue => { FfiReturnType::ByValue => {
asserts.push(quote_spanned!(func_ret.span() => asserts.push(quote_spanned!(span =>
<#func_ret as #ffi::IntoFfi>::convention() == #ffi::FfiReturnConvention::ByValue <#func_ret as #ffi::IntoFfi>::convention() == #ffi::FfiReturnConvention::ByValue
)); ));
(
quote_spanned!(span => <#func_ret as #ffi::IntoFfi>::convert(#func_call)),
quote_spanned!(span => <#func_ret as #ffi::IntoFfi>::Into),
)
} }
FfiReturnType::ByOutParam => { FfiReturnType::ByOutParam => {
asserts.push(quote_spanned!(func_ret.span() => asserts.push(quote_spanned!(span =>
<#func_ret as #ffi::IntoFfi>::convention() == #ffi::FfiReturnConvention::ByOutParam <#func_ret as #ffi::IntoFfi>::convention() == #ffi::FfiReturnConvention::ByOutParam
)); ));
let out = quote_spanned!(span => out: *mut <#func_ret as #ffi::IntoFfi>::Into);
shim_params.insert(0, quote_spanned!(func_ret.span() => out: *mut #shim_ret)); shim_params.insert(0, out);
(
(shim_body, shim_ret) = ( quote_spanned!(span => ::std::ptr::write(out, <#func_ret as #ffi::IntoFfi>::convert(#func_call))),
quote_spanned!(func_ret.span() => ::std::ptr::write(out, #shim_body)), quote_spanned!(span => ()),
quote_spanned!(func_ret.span() => ()), )
);
} }
}; }
} };
// build.push(quote_spanned!(func_name.span() =>
// b.call_inferred(#c_name, Self::#func_name);
// ));
build.push({ build.push({
let infer_args = iter::repeat_n(quote!(::std::unreachable!()), func_params.len()); let infer_args = iter::repeat_n(quote!(::std::unreachable!()), func_params.len());
@ -508,23 +504,16 @@ fn add_ffi_function(registry: &mut Registry, func: &FfiFunction) -> Result<()> {
} else { } else {
quote!(|| Self::#func_name(#(#infer_args),*)) quote!(|| Self::#func_name(#(#infer_args),*))
}; };
quote_spanned!(func_name.span() => b.call_inferred(#c_name, #infer);) quote!(b.call_inferred(#c_name, #infer);)
}); });
registry.build.push(quote_spanned!(func_name.span() => registry.build.push(quote!(#(::std::assert!(#asserts);)*));
#(::std::assert!(#asserts);)*
));
registry.build.push(match func.attrs.metamethod { registry.build.push(match func.attrs.metamethod {
Some(ref mm) => quote_spanned!(func_name.span() => Some(ref mm) => quote!(b.metatable(#mm, |b| { #(#build)* });),
b.metatable(#mm, |b| { #(#build)* }); None => quote!(b.index(#lua_name, |b| { #(#build)* });),
),
None => quote_spanned!(func_name.span() =>
b.index(#lua_name, |b| { #(#build)* });
),
}); });
registry.shims.push(parse_quote_spanned!(func_name.span() => registry.shims.push(parse_quote!(
#[unsafe(export_name = #c_name)] #[unsafe(export_name = #c_name)]
unsafe extern "C" fn #shim_name(#(#shim_params),*) -> #shim_ret { unsafe { #shim_body } } unsafe extern "C" fn #shim_name(#(#shim_params),*) -> #shim_ret { unsafe { #shim_body } }
)); ));
@ -536,7 +525,7 @@ fn generate_ffi_exports(registry: &Registry) -> Result<TokenStream> {
let ty = &registry.ty; let ty = &registry.ty;
let names = registry.shims.iter().map(|f| &f.sig.ident); let names = registry.shims.iter().map(|f| &f.sig.ident);
Ok(quote_spanned!(ty.span() => Ok(quote!(
// this ensures ffi function symbol exports are actually present in the resulting binary, // 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 // otherwise they may get dead code-eliminated before it reaches the linker
#[used] #[used]
@ -583,18 +572,14 @@ fn get_lua_functions(imp: &mut ItemImpl) -> Result<Vec<LuaFunction>> {
.sig .sig
.inputs .inputs
.iter() .iter()
.map(|arg| { .map(|arg| match arg {
Ok(match arg { FnArg::Receiver(recv) => parse_quote_spanned!(recv.span() => self),
FnArg::Receiver(recv) => Pat::Type(parse_quote_spanned!(recv.span() => FnArg::Typed(ty) => (*ty.pat).clone(), // ignore parameter type (only used for documentation purposes)
self: cdata
)),
FnArg::Typed(ty) => Pat::Type(ty.clone()),
})
}) })
.collect::<Result<_>>()?; .collect();
if let Some(ref variadic) = func.sig.variadic { if let Some(ref variadic) = func.sig.variadic {
params.push(parse_quote_spanned!(variadic.span() => variadic!())); params.push(parse_quote_spanned!(variadic.span() => variadic!())); // luaify builtin macro
} }
let attrs = parse_lua_function_attrs(&mut func.attrs)?; let attrs = parse_lua_function_attrs(&mut func.attrs)?;
@ -625,62 +610,45 @@ fn stub_lua_function(func: &mut ImplItemFn) -> Result<()> {
func.attrs.push(parse_quote!(#[allow(unused)])); func.attrs.push(parse_quote!(#[allow(unused)]));
func.block.stmts.clear(); func.block.stmts.clear();
func.block.stmts.push(parse_quote!( func.block.stmts.push(parse_quote!(
::std::unreachable!("cannot call lua function from rust"); const fn has_annotation<T: #ffi::Annotation>() {}
)); ));
let inputs = &mut func.sig.inputs; // convert `...` variadic to a `rest: luaffi::marker::Many` parameter
let output = &mut func.sig.output;
for input in inputs.iter_mut() {
if let FnArg::Typed(pat) = input
&& let Ok(stub) = stub_lua_type(&pat.ty)
{
pat.ty = stub.into();
}
}
if let ReturnType::Type(_, ty) = output
&& let Ok(stub) = stub_lua_type(ty)
{
*ty = stub.into();
}
if let Some(ref variadic) = func.sig.variadic { if let Some(ref variadic) = func.sig.variadic {
let ty = quote_spanned!(variadic.span() => variadic); let param = Ident::new("rest", variadic.span());
inputs.push(parse_quote!(rest: #ffi::__internal::stub_types::#ty)); let ty = quote_spanned!(variadic.span() => many);
func.sig.variadic = None; func.sig.variadic = None;
func.sig
.inputs
.push(parse_quote!(#param: #ffi::marker::#ty));
} }
for param in func.sig.inputs.iter() {
let ty = match param {
FnArg::Receiver(recv) => &recv.ty,
FnArg::Typed(ty) => &ty.ty,
};
// temporary assertion until we implement annotation generation
func.block.stmts.push(parse_quote!(
has_annotation::<#ty>();
));
}
// temporary assertion until we implement annotation generation
if let ReturnType::Type(_, ref ty) = func.sig.output {
func.block.stmts.push(parse_quote!(
has_annotation::<#ty>();
));
}
func.block.stmts.push(parse_quote!(
::std::unreachable!(r#"cannot call extern "Lua" function from rust"#);
));
Ok(()) Ok(())
} }
fn stub_lua_type(ty: &Type) -> Result<Type> {
let ffi = ffi_crate();
let span = ty.span();
let ty = if let Type::Infer(_) = ty {
quote_spanned!(span => any)
} else {
match ty_name(ty)?.to_string().as_str() {
"any" => quote_spanned!(span => any),
"many" => quote_spanned!(span => many),
"nil" => quote_spanned!(span => nil),
"boolean" => quote_spanned!(span => boolean),
"lightuserdata" => quote_spanned!(span => lightuserdata),
"number" => quote_spanned!(span => number),
"integer" => quote_spanned!(span => integer),
"string" => quote_spanned!(span => string),
"table" => quote_spanned!(span => table),
"function" => quote_spanned!(span => function),
"userdata" => quote_spanned!(span => userdata),
"thread" => quote_spanned!(span => thread),
"cdata" => quote_spanned!(span => cdata),
_ => syn_error!(ty, "unknown lua type"),
}
};
Ok(parse_quote!(#ffi::__internal::stub_types::#ty))
}
fn parse_lua_function_attrs(attrs: &mut Vec<Attribute>) -> Result<LuaFunctionAttrs> { fn parse_lua_function_attrs(attrs: &mut Vec<Attribute>) -> Result<LuaFunctionAttrs> {
let mut parsed = LuaFunctionAttrs::default(); let mut parsed = LuaFunctionAttrs::default();
let mut i = 0; let mut i = 0;
@ -707,12 +675,8 @@ fn add_lua_function(registry: &mut Registry, func: &LuaFunction) -> Result<()> {
let name = func_name.unraw().to_string(); let name = func_name.unraw().to_string();
registry.build.push(match func.attrs.metamethod { registry.build.push(match func.attrs.metamethod {
Some(ref mm) => quote_spanned!(func_name.span() => Some(ref mm) => quote!(b.metatable_raw(#mm, #luaify(|#(#params),*| #body));),
b.metatable_raw(#mm, #luaify(|#(#params),*| #body)); None => quote!(b.index_raw(#name, #luaify(|#(#params),*| #body));),
),
None => quote_spanned!(func_name.span() =>
b.index_raw(#name, #luaify(|#(#params),*| #body));
),
}); });
Ok(()) Ok(())
@ -747,46 +711,36 @@ fn inject_merged_drop(registry: &mut Registry, lua: Option<&LuaFunction>) -> Res
"finaliser must take exactly one parameter" "finaliser must take exactly one parameter"
); );
match lua.params[0] { let param = pat_ident(&lua.params[0])?;
// should be `self: cdata` PatType
Pat::Type(ref ty) => {
syn_assert!(
pat_ident(&ty.pat)? == "self",
lua.params[0],
"finaliser parameter must be `self`"
);
}
_ => syn_error!(lua.params[0], "finaliser parameter must be `self`"),
}
let params = &lua.params;
let body = &lua.body; let body = &lua.body;
registry.build.push(quote_spanned!(ty.span() => syn_assert!(param == "self", param, "finaliser parameter must be `self`");
registry.build.push(quote!(
if ::std::mem::needs_drop::<Self>() { if ::std::mem::needs_drop::<Self>() {
// if we have both a lua-side finaliser and a rust drop, then merge the finalisers // if we have both a lua-side finaliser and a rust drop, then merge the finalisers
// by doing the lua part first then drop rust // by doing the lua part first then drop rust
b.declare::<#ffi::UnsafeExternCFn<(*mut Self,), ()>>(#c_name_str); b.declare::<#ffi::ExternCFn<(*mut Self,), ()>>(#c_name_str);
b.metatable_raw("gc", #luaify(|self| { b.metatable_raw("gc", #luaify(|self| {
raw!(#luaify(#body)); // embed the lua part inside a do block raw!(#luaify(#body)); // embed the lua part inside a do block
__C::#c_name(self); __C::#c_name(self);
})); }));
} else { } else {
// we only have a lua-side finaliser // we only have a lua-side finaliser
b.metatable_raw("gc", #luaify(|#(#params),*| #body)); b.metatable_raw("gc", #luaify(|self| #body));
} }
)); ));
} else { } else {
registry.build.push(quote_spanned!(ty.span() => registry.build.push(quote!(
if ::std::mem::needs_drop::<Self>() { if ::std::mem::needs_drop::<Self>() {
// we only have a rust drop // we only have a rust drop
b.declare::<#ffi::UnsafeExternCFn<(*mut Self,), ()>>(#c_name_str); b.declare::<#ffi::ExternCFn<(*mut Self,), ()>>(#c_name_str);
b.metatable_raw("gc", ::std::format_args!("__C.{}", #c_name_str)); b.metatable_raw("gc", ::std::format_args!("__C.{}", #c_name_str));
} }
)); ));
} }
registry.shims.push(parse_quote_spanned!(ty.span() => registry.shims.push(parse_quote!(
#[unsafe(export_name = #c_name_str)] #[unsafe(export_name = #c_name_str)]
unsafe extern "C" fn #shim_name(ptr: *mut Self) { unsafe extern "C" fn #shim_name(ptr: *mut Self) {
unsafe { ::std::ptr::drop_in_place(ptr) } unsafe { ::std::ptr::drop_in_place(ptr) }