Compare commits
No commits in common. "e71d618d10c7097c99b243de9540c050ec461eec" and "2352ba66d4aa7e048f5bdfdbf867d552cf270f29" have entirely different histories.
e71d618d10
...
2352ba66d4
@ -1,3 +0,0 @@
|
|||||||
[build]
|
|
||||||
rustflags = ["--cfg", "tokio_unstable"]
|
|
||||||
rustdocflags = ["--cfg", "tokio_unstable"]
|
|
59
Cargo.lock
generated
59
Cargo.lock
generated
@ -250,12 +250,6 @@ version = "1.10.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "camino"
|
|
||||||
version = "1.1.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.27"
|
version = "1.2.27"
|
||||||
@ -376,15 +370,6 @@ dependencies = [
|
|||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "convert_case"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-segmentation",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
@ -444,28 +429,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive_more"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
|
||||||
dependencies = [
|
|
||||||
"derive_more-impl",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive_more-impl"
|
|
||||||
version = "2.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
|
||||||
dependencies = [
|
|
||||||
"convert_case",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
"unicode-xid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
@ -795,12 +758,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||||||
name = "lb"
|
name = "lb"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"camino",
|
|
||||||
"derive_more",
|
|
||||||
"luaffi",
|
"luaffi",
|
||||||
"luaify",
|
"luaify",
|
||||||
"luajit",
|
"luajit",
|
||||||
"sysexits",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -912,7 +872,6 @@ dependencies = [
|
|||||||
"luajit",
|
"luajit",
|
||||||
"mimalloc",
|
"mimalloc",
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
"sysexits",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
@ -1405,12 +1364,6 @@ version = "1.0.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sysexits"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "198f60d1f7f003f168507691e42d082df109ef0f05c6fd006e22528371a5f1b4"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.12"
|
version = "2.0.12"
|
||||||
@ -1643,18 +1596,6 @@ version = "1.0.18"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-segmentation"
|
|
||||||
version = "1.12.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-xid"
|
|
||||||
version = "0.2.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -24,7 +24,6 @@ lb = { version = "0.1.0", path = "crates/lb" }
|
|||||||
luajit = { version = "0.1.0", path = "crates/luajit", features = ["runtime"] }
|
luajit = { version = "0.1.0", path = "crates/luajit", features = ["runtime"] }
|
||||||
mimalloc = "0.1.47"
|
mimalloc = "0.1.47"
|
||||||
owo-colors = "4.2.1"
|
owo-colors = "4.2.1"
|
||||||
sysexits = "0.9.0"
|
|
||||||
tokio = { version = "1.45.1", features = ["full", "tracing"] }
|
tokio = { version = "1.45.1", features = ["full", "tracing"] }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
tracing-subscriber = "0.3.19"
|
tracing-subscriber = "0.3.19"
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
[jobs.test]
|
[jobs.test]
|
||||||
command = ["cargo", "test", "--workspace"]
|
command = ["cargo", "test", "--all"]
|
||||||
need_stdout = true
|
need_stdout = true
|
||||||
|
|
||||||
[jobs.doc]
|
[jobs.doc]
|
||||||
command = ["cargo", "doc", "--workspace"]
|
command = ["cargo", "doc", "--all", "--no-deps"]
|
||||||
|
|
||||||
[jobs.doc-open]
|
[jobs.doc-open]
|
||||||
command = ["cargo", "doc", "--workspace", "--open"]
|
command = ["cargo", "doc", "--all", "--no-deps", "--open"]
|
||||||
on_success = "back"
|
on_success = "back"
|
||||||
|
@ -4,12 +4,9 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
camino = "1.1.10"
|
|
||||||
derive_more = { version = "2.0.1", features = ["full"] }
|
|
||||||
luaffi = { version = "0.1.0", path = "../luaffi" }
|
luaffi = { version = "0.1.0", path = "../luaffi" }
|
||||||
luajit = { version = "0.1.0", path = "../luajit" }
|
luajit = { version = "0.1.0", path = "../luajit" }
|
||||||
sysexits = "0.9.0"
|
tokio = { version = "1.45.1", features = ["rt", "time", "fs"] }
|
||||||
tokio = { version = "1.45.1", features = ["rt", "time", "fs", "net", "process", "signal"] }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
luaify = { path = "../luaify" }
|
luaify = { path = "../luaify" }
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
// use flume::{Receiver, Sender};
|
|
||||||
use luaffi::{cdef, metatype};
|
|
||||||
|
|
||||||
#[cdef]
|
|
||||||
pub struct lb_libchannel;
|
|
||||||
|
|
||||||
#[metatype]
|
|
||||||
impl lb_libchannel {
|
|
||||||
#[new]
|
|
||||||
extern "Lua-C" fn new() -> Self {
|
|
||||||
Self
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "Lua" fn unbounded(self) {
|
|
||||||
let (send, recv) = (__new(__ct.lb_sender), __new(__ct.lb_receiver));
|
|
||||||
self.__unbounded(send, recv);
|
|
||||||
(send, recv)
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "Lua" fn bounded(self, cap: number) {
|
|
||||||
assert(cap >= 0, "channel capacity must be nonnegative");
|
|
||||||
let (send, recv) = (__new(__ct.lb_sender), __new(__ct.lb_receiver));
|
|
||||||
self.__bounded(cap, send, recv);
|
|
||||||
(send, recv)
|
|
||||||
}
|
|
||||||
|
|
||||||
// extern "Lua-C" fn __unbounded(&self, s: *mut lb_sender, r: *mut lb_receiver) {
|
|
||||||
// let (send, recv) = flume::unbounded();
|
|
||||||
// unsafe {
|
|
||||||
// ptr::write(s, lb_sender { send });
|
|
||||||
// ptr::write(r, lb_receiver { recv });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// extern "Lua-C" fn __bounded(&self, cap: usize, s: *mut lb_sender, r: *mut lb_receiver) {
|
|
||||||
// let (send, recv) = flume::bounded(cap);
|
|
||||||
// unsafe {
|
|
||||||
// ptr::write(s, lb_sender { send });
|
|
||||||
// ptr::write(r, lb_receiver { recv });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[cdef]
|
|
||||||
// pub struct lb_sender {
|
|
||||||
// #[opaque]
|
|
||||||
// send: Sender<c_int>,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[metatype]
|
|
||||||
// impl lb_sender {
|
|
||||||
// extern "Lua" fn send(self, value: _) {
|
|
||||||
// let key = __ref(value);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[cdef]
|
|
||||||
// pub struct lb_receiver {
|
|
||||||
// #[opaque]
|
|
||||||
// recv: Receiver<c_int>,
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #[metatype]
|
|
||||||
// impl lb_receiver {}
|
|
@ -1,32 +0,0 @@
|
|||||||
//! The `lb:fs` module provides utilities for interacting with the file system asynchronously.
|
|
||||||
//!
|
|
||||||
//! See [`lb_libfs`] for items exported by this module.
|
|
||||||
use luaffi::{cdef, metatype};
|
|
||||||
use std::io;
|
|
||||||
use tokio::fs;
|
|
||||||
|
|
||||||
/// Items exported by the `lb:fs` module.
|
|
||||||
///
|
|
||||||
/// This module can be obtained by calling `require` in Lua.
|
|
||||||
///
|
|
||||||
/// ```lua
|
|
||||||
/// local fs = require("lb:fs");
|
|
||||||
/// ```
|
|
||||||
#[cdef]
|
|
||||||
pub struct lb_libfs;
|
|
||||||
|
|
||||||
#[metatype]
|
|
||||||
impl lb_libfs {
|
|
||||||
#[new]
|
|
||||||
extern "Lua-C" fn new() -> Self {
|
|
||||||
Self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern "Lua" fn read(&self, path: string) -> string {
|
|
||||||
self.__read(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
async extern "Lua-C" fn __read(&self, path: &str) -> io::Result<Vec<u8>> {
|
|
||||||
fs::read(path).await
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,2 @@
|
|||||||
pub mod channel;
|
pub mod rt;
|
||||||
pub mod fs;
|
|
||||||
pub mod net;
|
|
||||||
pub mod runtime;
|
|
||||||
pub mod task;
|
pub mod task;
|
||||||
|
@ -1,345 +0,0 @@
|
|||||||
//! The `lb:net` module provides an asynchronous network API for creating TCP or UDP servers and
|
|
||||||
//! clients.
|
|
||||||
//!
|
|
||||||
//! See [`lb_libnet`] for items exported by this module.
|
|
||||||
use derive_more::{From, FromStr};
|
|
||||||
use luaffi::{cdef, metatype};
|
|
||||||
use std::{
|
|
||||||
io,
|
|
||||||
net::{AddrParseError, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
|
||||||
};
|
|
||||||
use tokio::net::{TcpListener, TcpSocket, TcpStream};
|
|
||||||
|
|
||||||
/// Items exported by the `lb:net` module.
|
|
||||||
///
|
|
||||||
/// This module can be obtained by calling `require` in Lua.
|
|
||||||
///
|
|
||||||
/// ```lua
|
|
||||||
/// local net = require("lb:net");
|
|
||||||
/// ```
|
|
||||||
#[cdef]
|
|
||||||
pub struct lb_libnet;
|
|
||||||
|
|
||||||
#[metatype]
|
|
||||||
impl lb_libnet {
|
|
||||||
#[new]
|
|
||||||
extern "Lua-C" fn new() -> Self {
|
|
||||||
Self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`Ipv4Addr::LOCALHOST`].
|
|
||||||
pub extern "Lua-C" fn localhost_v4(&self) -> lb_ipaddr {
|
|
||||||
lb_ipaddr(Ipv4Addr::LOCALHOST.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`Ipv6Addr::LOCALHOST`].
|
|
||||||
pub extern "Lua-C" fn localhost_v6(&self) -> lb_ipaddr {
|
|
||||||
lb_ipaddr(Ipv6Addr::LOCALHOST.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`Ipv4Addr::UNSPECIFIED`].
|
|
||||||
pub extern "Lua-C" fn unspecified_v4(&self) -> lb_ipaddr {
|
|
||||||
lb_ipaddr(Ipv4Addr::UNSPECIFIED.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`Ipv6Addr::UNSPECIFIED`].
|
|
||||||
pub extern "Lua-C" fn unspecified_v6(&self) -> lb_ipaddr {
|
|
||||||
lb_ipaddr(Ipv6Addr::UNSPECIFIED.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`Ipv4Addr::BROADCAST`].
|
|
||||||
pub extern "Lua-C" fn broadcast_v4(&self) -> lb_ipaddr {
|
|
||||||
lb_ipaddr(Ipv4Addr::BROADCAST.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an [`lb_ipaddr`] from the given input.
|
|
||||||
///
|
|
||||||
/// 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
|
|
||||||
/// `s` as an IP address string. Both IPv4 or IPv6 addresses are supported.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Throws if `s` cannot be parsed as an IP address.
|
|
||||||
pub extern "Lua" fn ipaddr(&self, s: any) -> lb_ipaddr {
|
|
||||||
if __istype(__ct.lb_ipaddr, s) {
|
|
||||||
__new(__ct.lb_ipaddr, s) // copy constructor
|
|
||||||
} else if __istype(__ct.lb_socketaddr, s) {
|
|
||||||
s.ip()
|
|
||||||
} else {
|
|
||||||
self.__parse_ipaddr(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "Lua-C" fn __parse_ipaddr(&self, s: &str) -> Result<lb_ipaddr, AddrParseError> {
|
|
||||||
s.parse()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates an [`lb_socketaddr`] from the given input.
|
|
||||||
///
|
|
||||||
/// A socket address is an IP address with a port number.
|
|
||||||
///
|
|
||||||
/// If `s` is an [`lb_socketaddr`], a copy of that value is returned. If `s` is an
|
|
||||||
/// [`lb_ipaddr`], a socket address with that IP address is returned. Otherwise, parses `s` as a
|
|
||||||
/// socket address string. Both IPv4 and IPv6 addresses are supported.
|
|
||||||
///
|
|
||||||
/// If `port` is not specified, `0` is used as the default.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Throws if `s` cannot be parsed as an IP or socket address.
|
|
||||||
pub extern "Lua" fn socketaddr(&self, s: any, port: any) -> lb_socketaddr {
|
|
||||||
if port != () {
|
|
||||||
self.__new_socketaddr(self.ipaddr(s), port)
|
|
||||||
} else {
|
|
||||||
if __istype(__ct.lb_socketaddr, s) {
|
|
||||||
__new(__ct.lb_socketaddr, s) // copy constructor
|
|
||||||
} else if __istype(__ct.lb_ipaddr, s) {
|
|
||||||
self.__new_socketaddr(s, 0) // default port 0
|
|
||||||
} else {
|
|
||||||
self.__parse_socketaddr(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "Lua-C" fn __new_socketaddr(&self, ip: &lb_ipaddr, port: u16) -> lb_socketaddr {
|
|
||||||
SocketAddr::new(ip.0, port).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "Lua-C" fn __parse_socketaddr(&self, s: &str) -> Result<lb_socketaddr, AddrParseError> {
|
|
||||||
s.parse()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new TCP socket configured for IPv4.
|
|
||||||
///
|
|
||||||
/// See [`TcpSocket::new_v4`].
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Throws if an error was encountered during the socket creation.
|
|
||||||
pub extern "Lua" fn tcp_v4(&self) -> lb_tcpsocket {
|
|
||||||
self.__new_tcp_v4()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new TCP socket configured for IPv6.
|
|
||||||
///
|
|
||||||
/// See [`TcpSocket::new_v6`].
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Throws if an error was encountered during the socket creation.
|
|
||||||
pub extern "Lua" fn tcp_v6(&self) -> lb_tcpsocket {
|
|
||||||
self.__new_tcp_v6()
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "Lua-C" fn __new_tcp_v4(&self) -> io::Result<lb_tcpsocket> {
|
|
||||||
TcpSocket::new_v4().map(lb_tcpsocket)
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "Lua-C" fn __new_tcp_v6(&self) -> io::Result<lb_tcpsocket> {
|
|
||||||
TcpSocket::new_v6().map(lb_tcpsocket)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An IP address, either IPv4 or IPv6.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// This example creates an [`lb_ipaddr`] by parsing an IP address string.
|
|
||||||
///
|
|
||||||
/// ```lua
|
|
||||||
/// local net = require("lb:net");
|
|
||||||
/// local addr = net:ipaddr("127.0.0.1"); -- ipv4 loopback address
|
|
||||||
///
|
|
||||||
/// assert(addr:is_v4());
|
|
||||||
/// assert(addr:is_loopback());
|
|
||||||
/// ```
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, From, FromStr)]
|
|
||||||
#[cdef]
|
|
||||||
pub struct lb_ipaddr(#[opaque] IpAddr);
|
|
||||||
|
|
||||||
#[metatype]
|
|
||||||
impl lb_ipaddr {
|
|
||||||
/// See [`IpAddr::is_unspecified`].
|
|
||||||
pub extern "Lua-C" fn is_unspecified(&self) -> bool {
|
|
||||||
self.0.is_unspecified()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`IpAddr::is_loopback`].
|
|
||||||
pub extern "Lua-C" fn is_loopback(&self) -> bool {
|
|
||||||
self.0.is_loopback()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`IpAddr::is_multicast`].
|
|
||||||
pub extern "Lua-C" fn is_multicast(&self) -> bool {
|
|
||||||
self.0.is_multicast()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 {
|
|
||||||
if self.is_v6() { "v6" } else { "v4" }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if this is an IPv4 address.
|
|
||||||
pub extern "Lua-C" fn is_v4(&self) -> bool {
|
|
||||||
self.0.is_ipv4()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`Ipv4Addr::is_private`].
|
|
||||||
pub extern "Lua-C" fn is_v4_private(&self) -> bool {
|
|
||||||
match self.0 {
|
|
||||||
IpAddr::V4(v4) => v4.is_private(),
|
|
||||||
IpAddr::V6(_) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`Ipv4Addr::is_link_local`].
|
|
||||||
pub extern "Lua-C" fn is_v4_link_local(&self) -> bool {
|
|
||||||
match self.0 {
|
|
||||||
IpAddr::V4(v4) => v4.is_link_local(),
|
|
||||||
IpAddr::V6(_) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`Ipv4Addr::is_broadcast`].
|
|
||||||
pub extern "Lua-C" fn is_v4_broadcast(&self) -> bool {
|
|
||||||
match self.0 {
|
|
||||||
IpAddr::V4(v4) => v4.is_broadcast(),
|
|
||||||
IpAddr::V6(_) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`Ipv4Addr::is_documentation`].
|
|
||||||
pub extern "Lua-C" fn is_v4_documentation(&self) -> bool {
|
|
||||||
match self.0 {
|
|
||||||
IpAddr::V4(v4) => v4.is_documentation(),
|
|
||||||
IpAddr::V6(_) => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if this is an IPv6 address.
|
|
||||||
pub extern "Lua-C" fn is_v6(&self) -> bool {
|
|
||||||
self.0.is_ipv6()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`Ipv6Addr::is_unique_local`].
|
|
||||||
pub extern "Lua-C" fn is_v6_unique_local(&self) -> bool {
|
|
||||||
match self.0 {
|
|
||||||
IpAddr::V4(_) => false,
|
|
||||||
IpAddr::V6(v6) => v6.is_unique_local(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`Ipv6Addr::is_unicast_link_local`].
|
|
||||||
pub extern "Lua-C" fn is_v6_unicast_link_local(&self) -> bool {
|
|
||||||
match self.0 {
|
|
||||||
IpAddr::V4(_) => false,
|
|
||||||
IpAddr::V6(v6) => v6.is_unicast_link_local(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`Ipv4Addr::to_ipv6_compatible`].
|
|
||||||
pub extern "Lua-C" fn to_v6_compat(&self) -> Self {
|
|
||||||
match self.0 {
|
|
||||||
IpAddr::V4(v4) => Self(v4.to_ipv6_compatible().into()),
|
|
||||||
IpAddr::V6(_) => *self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`Ipv4Addr::to_ipv6_mapped`].
|
|
||||||
pub extern "Lua-C" fn to_v6_mapped(&self) -> Self {
|
|
||||||
match self.0 {
|
|
||||||
IpAddr::V4(v4) => Self(v4.to_ipv6_mapped().into()),
|
|
||||||
IpAddr::V6(_) => *self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// See [`IpAddr::to_canonical`].
|
|
||||||
pub extern "Lua-C" fn canonical(&self) -> Self {
|
|
||||||
self.0.to_canonical().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the string representation of this address.
|
|
||||||
#[tostring]
|
|
||||||
pub extern "Lua-C" fn tostring(&self) -> String {
|
|
||||||
self.0.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A socket address, which is an IP address with a port number.
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, From, FromStr)]
|
|
||||||
#[cdef]
|
|
||||||
pub struct lb_socketaddr(#[opaque] SocketAddr);
|
|
||||||
|
|
||||||
#[metatype]
|
|
||||||
impl lb_socketaddr {
|
|
||||||
/// Returns the IP part of this address.
|
|
||||||
pub extern "Lua-C" fn ip(&self) -> lb_ipaddr {
|
|
||||||
self.0.ip().into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the IP part of this address.
|
|
||||||
///
|
|
||||||
/// This function accepts the same arguments as [`ipaddr`](lb_libnet::ipaddr).
|
|
||||||
pub extern "Lua" fn set_ip(&mut self, s: any) -> &mut Self {
|
|
||||||
if __istype(__ct.lb_ipaddr, s) {
|
|
||||||
self.__set_ip(s);
|
|
||||||
} else if __istype(__ct.lb_socketaddr, s) {
|
|
||||||
self.__set_ip(s.ip());
|
|
||||||
} else {
|
|
||||||
self.__set_ip_parse(s);
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "Lua-C" fn __set_ip(&mut self, ip: &lb_ipaddr) {
|
|
||||||
self.0.set_ip(ip.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "Lua-C" fn __set_ip_parse(&mut self, s: &str) -> Result<(), AddrParseError> {
|
|
||||||
s.parse().map(|ip| self.0.set_ip(ip))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the port part of this address.
|
|
||||||
pub extern "Lua-C" fn port(&self) -> u16 {
|
|
||||||
self.0.port()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the port part of this address.
|
|
||||||
pub extern "Lua" fn set_port(&mut self, port: number) -> &mut Self {
|
|
||||||
self.__set_port(port);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "Lua-C" fn __set_port(&mut self, port: u16) {
|
|
||||||
self.0.set_port(port)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the string representation of this address.
|
|
||||||
#[tostring]
|
|
||||||
pub extern "Lua-C" fn tostring(&self) -> String {
|
|
||||||
self.0.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A TCP socket which has not yet been converted to a [`lb_tcpstream`] or [`lb_tcplistener`].
|
|
||||||
#[derive(Debug, From)]
|
|
||||||
#[cdef]
|
|
||||||
pub struct lb_tcpsocket(#[opaque] TcpSocket);
|
|
||||||
|
|
||||||
#[metatype]
|
|
||||||
impl lb_tcpsocket {}
|
|
||||||
|
|
||||||
#[derive(Debug, From)]
|
|
||||||
#[cdef]
|
|
||||||
pub struct lb_tcpstream(#[opaque] TcpStream);
|
|
||||||
|
|
||||||
#[metatype]
|
|
||||||
impl lb_tcpstream {}
|
|
||||||
|
|
||||||
#[derive(Debug, From)]
|
|
||||||
#[cdef]
|
|
||||||
pub struct lb_tcplistener(#[opaque] TcpListener);
|
|
||||||
|
|
||||||
#[metatype]
|
|
||||||
impl lb_tcplistener {}
|
|
@ -1,5 +0,0 @@
|
|||||||
local task = require("lb:task")
|
|
||||||
|
|
||||||
function spawn(f, ...)
|
|
||||||
return task:spawn(f, ...)
|
|
||||||
end
|
|
@ -1,85 +0,0 @@
|
|||||||
use crate::{channel::lb_libchannel, fs::lb_libfs, net::lb_libnet, task::lb_libtask};
|
|
||||||
use derive_more::{Deref, DerefMut};
|
|
||||||
use luaffi::{Registry, Type};
|
|
||||||
use luajit::{Chunk, State};
|
|
||||||
use std::fmt::Display;
|
|
||||||
use tokio::{
|
|
||||||
task::{JoinHandle, LocalSet, futures::TaskLocalFuture, spawn_local},
|
|
||||||
task_local,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct Builder {
|
|
||||||
registry: Registry,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Builder {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let mut registry = Registry::new();
|
|
||||||
|
|
||||||
registry
|
|
||||||
.preload::<lb_libtask>("lb:task")
|
|
||||||
.preload::<lb_libchannel>("lb:channel")
|
|
||||||
.preload::<lb_libfs>("lb:fs")
|
|
||||||
.preload::<lb_libnet>("lb:net");
|
|
||||||
|
|
||||||
Self { registry }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn module<T: Type>(&mut self, name: impl Display) -> &mut Self {
|
|
||||||
self.registry.preload::<T>(name);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn registry(&self) -> &Registry {
|
|
||||||
&self.registry
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build(&self) -> luajit::Result<Runtime> {
|
|
||||||
Ok(Runtime {
|
|
||||||
state: {
|
|
||||||
let mut s = State::new()?;
|
|
||||||
let mut chunk = Chunk::new(self.registry.done());
|
|
||||||
chunk.extend(include_bytes!("./runtime.lua"));
|
|
||||||
s.eval(chunk.path("[luby]"), 0, 0)?;
|
|
||||||
s
|
|
||||||
},
|
|
||||||
tasks: LocalSet::new(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deref, DerefMut)]
|
|
||||||
pub struct Runtime {
|
|
||||||
#[deref]
|
|
||||||
#[deref_mut]
|
|
||||||
state: State,
|
|
||||||
tasks: LocalSet,
|
|
||||||
}
|
|
||||||
|
|
||||||
task_local! {
|
|
||||||
static STATE: State;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Runtime {
|
|
||||||
pub fn spawn<T: 'static>(
|
|
||||||
&self,
|
|
||||||
f: impl AsyncFnOnce(&mut State) -> T + 'static,
|
|
||||||
) -> JoinHandle<T> {
|
|
||||||
self.tasks
|
|
||||||
.spawn_local(async move { f(&mut STATE.with(|s| s.new_thread())).await })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn spawn<T: 'static>(f: impl AsyncFnOnce(&mut State) -> T + 'static) -> JoinHandle<T> {
|
|
||||||
spawn_local(async move { f(&mut STATE.with(|s| s.new_thread())).await })
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoFuture for Runtime {
|
|
||||||
type Output = ();
|
|
||||||
type IntoFuture = TaskLocalFuture<State, LocalSet>;
|
|
||||||
|
|
||||||
fn into_future(self) -> Self::IntoFuture {
|
|
||||||
STATE.scope(self.state, self.tasks)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
use crate::runtime::spawn;
|
|
||||||
use luaffi::{cdef, metatype};
|
|
||||||
use std::{ffi::c_int, process};
|
|
||||||
use tokio::task::JoinHandle;
|
|
||||||
|
|
||||||
#[cdef]
|
|
||||||
pub struct lb_libtask;
|
|
||||||
|
|
||||||
#[metatype]
|
|
||||||
impl lb_libtask {
|
|
||||||
#[new]
|
|
||||||
extern "Lua-C" fn new() -> Self {
|
|
||||||
Self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern "Lua" fn spawn(self, f: function, ...) {
|
|
||||||
// pack the function and its arguments into a table and pass its ref to rust
|
|
||||||
self.__spawn(__ref(__tpack(f, variadic!())))
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "Lua-C" fn __spawn(&self, key: c_int) -> lb_task {
|
|
||||||
let handle = spawn(async move |s| {
|
|
||||||
// SAFETY: key is always unique, created by __ref above
|
|
||||||
let arg = unsafe { s.new_ref_unchecked(key) };
|
|
||||||
s.resize(0);
|
|
||||||
s.push(arg);
|
|
||||||
let narg = s.unpack(1, 1, None) - 1;
|
|
||||||
println!("{s:?}");
|
|
||||||
if let Err(_err) = s.call_async(narg, 0).await {
|
|
||||||
process::exit(1)
|
|
||||||
}
|
|
||||||
println!("{s:?}");
|
|
||||||
});
|
|
||||||
|
|
||||||
lb_task { handle }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cdef]
|
|
||||||
pub struct lb_task {
|
|
||||||
#[opaque]
|
|
||||||
handle: JoinHandle<()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[metatype]
|
|
||||||
impl lb_task {}
|
|
@ -1,32 +0,0 @@
|
|||||||
use lb::runtime;
|
|
||||||
use luaify::luaify;
|
|
||||||
use luajit::{Chunk, LoadMode};
|
|
||||||
use tokio::test;
|
|
||||||
|
|
||||||
async fn run_lua(s: &'static str) {
|
|
||||||
let rt = runtime::Builder::new().build().unwrap();
|
|
||||||
let task = rt.spawn(async move |state| {
|
|
||||||
println!("executing test chunk: {s}");
|
|
||||||
|
|
||||||
state
|
|
||||||
.load(Chunk::new(s).mode(LoadMode::TEXT))
|
|
||||||
.unwrap_or_else(|err| panic!("{err}"));
|
|
||||||
|
|
||||||
state
|
|
||||||
.call_async(0, 0)
|
|
||||||
.await
|
|
||||||
.unwrap_or_else(|err| panic!("{err}"));
|
|
||||||
});
|
|
||||||
|
|
||||||
rt.await;
|
|
||||||
task.await.unwrap_or_else(|err| panic!("{err}"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
async fn ipaddr() {
|
|
||||||
run_lua(luaify!({
|
|
||||||
let net = require("lb:net");
|
|
||||||
print(net.ipaddr("127.0.0.1"));
|
|
||||||
}))
|
|
||||||
.await
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
use lb::runtime;
|
|
||||||
use luaify::luaify;
|
|
||||||
use luajit::{Chunk, LoadMode};
|
|
||||||
use tokio::test;
|
|
||||||
|
|
||||||
async fn run_lua(s: &'static str) {
|
|
||||||
let rt = runtime::Builder::new().build().unwrap();
|
|
||||||
let task = rt.spawn(async move |state| {
|
|
||||||
println!("executing test chunk: {s}");
|
|
||||||
|
|
||||||
state
|
|
||||||
.load(Chunk::new(s).mode(LoadMode::TEXT))
|
|
||||||
.unwrap_or_else(|err| panic!("{err}"));
|
|
||||||
|
|
||||||
state
|
|
||||||
.call_async(0, 0)
|
|
||||||
.await
|
|
||||||
.unwrap_or_else(|err| panic!("{err}"));
|
|
||||||
});
|
|
||||||
|
|
||||||
rt.await;
|
|
||||||
task.await.unwrap_or_else(|err| panic!("{err}"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
async fn task_test() {
|
|
||||||
run_lua(luaify!({
|
|
||||||
let thing = spawn(|| {
|
|
||||||
print("spawn callback!!!!!!!!!!!!!");
|
|
||||||
});
|
|
||||||
print("thing is", thing);
|
|
||||||
//
|
|
||||||
}))
|
|
||||||
.await
|
|
||||||
}
|
|
@ -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, FfiReturnConvention, Metatype, MetatypeBuilder, ToFfi, Type, TypeBuilder,
|
||||||
TypeType, UnsafeExternCFn,
|
UnsafeExternCFn,
|
||||||
};
|
};
|
||||||
use luaify::luaify;
|
use luaify::luaify;
|
||||||
use std::{
|
use std::{
|
||||||
@ -21,7 +21,7 @@ const SIGNATURE: Signature = Signature::from_ne_bytes(*b"\x00lb_poll");
|
|||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub struct lua_future<F: Future<Output: IntoFfi>> {
|
pub struct lua_future<F: Future<Output: ToFfi>> {
|
||||||
//
|
//
|
||||||
// SAFETY: LuaJIT guarantees that cdata payloads, which are GC-managed, are never relocated
|
// SAFETY: LuaJIT guarantees that cdata payloads, which are GC-managed, are never relocated
|
||||||
// (i.e. pinned). We can safely assume that we are pinned and poll the future inside this
|
// (i.e. pinned). We can safely assume that we are pinned and poll the future inside this
|
||||||
@ -43,7 +43,7 @@ pub struct lua_future<F: Future<Output: IntoFfi>> {
|
|||||||
sig: Signature,
|
sig: Signature,
|
||||||
poll: fn(Pin<&mut Self>, cx: &mut Context) -> Poll<()>,
|
poll: fn(Pin<&mut Self>, cx: &mut Context) -> Poll<()>,
|
||||||
state: State<F>,
|
state: State<F>,
|
||||||
take: unsafe extern "C" fn(&mut Self) -> <F::Output as IntoFfi>::Into,
|
take: unsafe extern "C" fn(&mut Self) -> <F::Output as ToFfi>::To,
|
||||||
drop: unsafe extern "C" fn(&mut Self),
|
drop: unsafe extern "C" fn(&mut Self),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ enum State<F: Future> {
|
|||||||
Complete,
|
Complete,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: Future<Output: IntoFfi>> lua_future<F> {
|
impl<F: Future<Output: ToFfi>> lua_future<F> {
|
||||||
pub fn new(fut: F) -> Self {
|
pub fn new(fut: F) -> Self {
|
||||||
Self {
|
Self {
|
||||||
sig: SIGNATURE,
|
sig: SIGNATURE,
|
||||||
@ -94,7 +94,7 @@ impl<F: Future<Output: IntoFfi>> lua_future<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn take(&mut self) -> <F::Output as IntoFfi>::Into {
|
unsafe extern "C" fn take(&mut self) -> <F::Output as ToFfi>::To {
|
||||||
// `fut:__take()` returns the fulfilled value by-value (not by out-param) because if we
|
// `fut:__take()` returns the fulfilled value by-value (not by out-param) because if we
|
||||||
// preallocate a cdata for the out-param and the thread for some reason gets dropped and
|
// preallocate a cdata for the out-param and the thread for some reason gets dropped and
|
||||||
// never resumed, the GC could call the destructor on an uninitialised cdata.
|
// never resumed, the GC could call the destructor on an uninitialised cdata.
|
||||||
@ -131,17 +131,13 @@ impl Future for lua_pollable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<F: Future<Output: IntoFfi> + 'static> Type for lua_future<F> {
|
unsafe impl<F: Future<Output: ToFfi> + 'static> Type for lua_future<F> {
|
||||||
fn name() -> impl Display {
|
fn name() -> impl Display {
|
||||||
display!("future__{:x}", type_id::<F>())
|
display!("future__{:x}", type_id::<F>())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ty() -> TypeType {
|
|
||||||
TypeType::Aggregate
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display {
|
fn cdecl(name: impl Display) -> impl Display {
|
||||||
display!("struct {} {name}", Self::name())
|
display!("struct future__{:x} {name}", type_id::<F>())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(s: &mut TypeBuilder) {
|
fn build(s: &mut TypeBuilder) {
|
||||||
@ -149,15 +145,15 @@ 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: ToFfi> + '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::<UnsafeExternCFn<(&mut Self,), <F::Output as ToFfi>::To>>("__take")
|
||||||
.field::<UnsafeExternCFn<(&mut Self,), ()>>("__drop");
|
.field::<UnsafeExternCFn<(&mut Self,), ()>>("__drop");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<F: Future<Output: IntoFfi> + 'static> Metatype for lua_future<F> {
|
unsafe impl<F: Future<Output: ToFfi> + 'static> Metatype for lua_future<F> {
|
||||||
type Target = Self;
|
type Target = Self;
|
||||||
|
|
||||||
fn build(s: &mut MetatypeBuilder) {
|
fn build(s: &mut MetatypeBuilder) {
|
||||||
@ -165,36 +161,30 @@ unsafe impl<F: Future<Output: IntoFfi> + 'static> Metatype for lua_future<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<F: Future<Output: IntoFfi> + 'static> IntoFfi for lua_future<F> {
|
unsafe impl<F: Future<Output: ToFfi> + 'static> ToFfi for lua_future<F> {
|
||||||
type Into = lua_future<F>;
|
type To = lua_future<F>;
|
||||||
|
|
||||||
fn convention() -> FfiReturnConvention {
|
fn convert(self) -> Self::To {
|
||||||
// futures are always returned by-value due to rust type inference limitations
|
|
||||||
FfiReturnConvention::ByValue
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert(self) -> Self::Into {
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn postlude(ret: &str) -> impl Display {
|
fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display {
|
||||||
// When returning a future from Rust to Lua, yield it immediately to the runtime which will
|
// When returning a future from Rust to Lua, yield it immediately to the runtime which will
|
||||||
// poll it to completion in the background, then take the fulfilled value once the thread
|
// poll it to completion in the background, then take the fulfilled value once the thread
|
||||||
// gets resumed. Lua user code should never to worry about awaiting futures.
|
// gets resumed. Lua user code should never to worry about awaiting futures.
|
||||||
//
|
//
|
||||||
// Once the current thread gets resumed and we take the future's fulfilled value, we clear
|
// Once the current thread gets resumed and we take the future's fulfilled value, we clear
|
||||||
// the finaliser on the future and forget it (there is nothing to drop once the value is
|
// the finaliser on the future and forget it (there is nothing to call drop on).
|
||||||
// taken).
|
|
||||||
//
|
//
|
||||||
// `coroutine.yield` is cached as `__yield` and `ffi.gc` as `__gc` in locals (see lib.rs)
|
// `coroutine.yield` is cached as `yield` and `ffi.gc` as `gc` in locals (see lib.rs)
|
||||||
display!(
|
display!(
|
||||||
"__yield({ret}); {ret} = __gc({ret}, nil):__take(); {}",
|
"yield({ret}); {ret} = gc({ret}, nil):__take(); {}",
|
||||||
<F::Output as IntoFfi>::postlude(ret)
|
<F::Output as ToFfi>::postlude(ret, FfiReturnConvention::ByValue)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F: IntoFuture<Output: IntoFfi>> From<F> for lua_future<F::IntoFuture> {
|
impl<F: IntoFuture<Output: ToFfi>> From<F> for lua_future<F::IntoFuture> {
|
||||||
fn from(value: F) -> Self {
|
fn from(value: F) -> Self {
|
||||||
Self::new(value.into_future())
|
Self::new(value.into_future())
|
||||||
}
|
}
|
||||||
|
@ -6,23 +6,6 @@ use std::{
|
|||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
pub mod stub_types {
|
|
||||||
pub struct any;
|
|
||||||
pub struct nil;
|
|
||||||
pub struct boolean;
|
|
||||||
pub struct lightuserdata;
|
|
||||||
pub struct number;
|
|
||||||
pub struct integer;
|
|
||||||
pub struct 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);
|
||||||
|
@ -1,34 +1,38 @@
|
|||||||
use crate::{
|
use crate::__internal::{disp, display, export, write_sep};
|
||||||
__internal::{disp, display, export, write_sep},
|
|
||||||
string::{DROP_BUFFER_FN, IS_UTF8_FN, lua_buffer},
|
|
||||||
};
|
|
||||||
pub use luaffi_impl::*;
|
pub use luaffi_impl::*;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
ffi::{c_double, c_float, c_void},
|
ffi::{c_double, c_float, c_void},
|
||||||
fmt::{self, Display, Formatter, Write},
|
fmt::{self, Display, Formatter, Write},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
mem,
|
mem, slice,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod future;
|
pub mod future;
|
||||||
|
// pub mod option;
|
||||||
pub mod string;
|
pub mod string;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[path = "./internal.rs"]
|
#[path = "./internal.rs"]
|
||||||
pub mod __internal;
|
pub mod __internal;
|
||||||
pub mod result;
|
|
||||||
|
const KEEP_FN: &str = "luaffi_keep";
|
||||||
|
const IS_UTF8_FN: &str = "luaffi_is_utf8";
|
||||||
|
|
||||||
// Dummy function to ensure that strings passed to Rust via wrapper objects will not be
|
// 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
|
// garbage-collected until the end of the function. This shall exist until LuaJIT one day implements
|
||||||
// going through the slow-path). This shall exist until LuaJIT one day implements something like
|
// something like `ffi.keep(obj)`.
|
||||||
// `ffi.keep(obj)`.
|
|
||||||
//
|
//
|
||||||
// https://github.com/LuaJIT/LuaJIT/issues/1167
|
// https://github.com/LuaJIT/LuaJIT/issues/1167
|
||||||
pub(crate) const KEEP_FN: &str = "luaffi_keep";
|
|
||||||
#[unsafe(export_name = "luaffi_keep")]
|
#[unsafe(export_name = "luaffi_keep")]
|
||||||
extern "C" fn __keep(_ptr: *const c_void) {}
|
extern "C" fn __keep(_ptr: *const c_void) {}
|
||||||
export![__keep];
|
|
||||||
|
#[unsafe(export_name = "luaffi_is_utf8")]
|
||||||
|
unsafe extern "C" fn __is_utf8(ptr: *const u8, len: usize) -> bool {
|
||||||
|
simdutf8::basic::from_utf8(unsafe { slice::from_raw_parts(ptr, len) }).is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
export![__keep, __is_utf8];
|
||||||
|
|
||||||
const CACHE_LIBS: &[(&str, &str)] = &[
|
const CACHE_LIBS: &[(&str, &str)] = &[
|
||||||
("table", "table"),
|
("table", "table"),
|
||||||
@ -78,20 +82,20 @@ const CACHE_LOCALS: &[(&str, &str)] = &[
|
|||||||
("__tunpack", "table.unpack"),
|
("__tunpack", "table.unpack"),
|
||||||
// string
|
// string
|
||||||
("__slen", "string.len"),
|
("__slen", "string.len"),
|
||||||
("__sprintf", "string.format"),
|
("__sformat", "string.format"),
|
||||||
("__ssub", "string.sub"),
|
("__ssub", "string.sub"),
|
||||||
("__sgsub", "string.gsub"),
|
("__sgsub", "string.gsub"),
|
||||||
("__sgmatch", "string.gmatch"),
|
("__sgmatch", "string.gmatch"),
|
||||||
("__sdump", "string.dump"),
|
("__sdump", "string.dump"),
|
||||||
// math (used in luaify! macro)
|
// math
|
||||||
("__fmod", "math.fmod"),
|
("__fmod", "math.fmod"),
|
||||||
// coroutine (used in future.rs)
|
// coroutine
|
||||||
("__yield", "coroutine.yield"),
|
("__yield", "coroutine.yield"),
|
||||||
// package
|
// package
|
||||||
("__preload", "package.preload"),
|
("__preload", "package.preload"),
|
||||||
// debug
|
// debug
|
||||||
("__traceback", "debug.traceback"),
|
("__traceback", "debug.traceback"),
|
||||||
("__registry", "debug.getregistry()"), // (used in lib.lua)
|
("__registry", "debug.getregistry()"),
|
||||||
// ffi
|
// ffi
|
||||||
("__C", "ffi.C"),
|
("__C", "ffi.C"),
|
||||||
("__ct", "{}"),
|
("__ct", "{}"),
|
||||||
@ -104,8 +108,8 @@ const CACHE_LOCALS: &[(&str, &str)] = &[
|
|||||||
("__gc", "ffi.gc"),
|
("__gc", "ffi.gc"),
|
||||||
("__sizeof", "ffi.sizeof"),
|
("__sizeof", "ffi.sizeof"),
|
||||||
("__alignof", "ffi.alignof"),
|
("__alignof", "ffi.alignof"),
|
||||||
("__intern", "ffi.string"), // (used in string.rs)
|
("__intern", "ffi.string"),
|
||||||
// bit (used in luaify! macro)
|
// bit
|
||||||
("__bnot", "bit.bnot"),
|
("__bnot", "bit.bnot"),
|
||||||
("__band", "bit.band"),
|
("__band", "bit.band"),
|
||||||
("__bor", "bit.bor"),
|
("__bor", "bit.bor"),
|
||||||
@ -139,7 +143,6 @@ impl Registry {
|
|||||||
let mut s = Self::default();
|
let mut s = Self::default();
|
||||||
s.declare::<UnsafeExternCFn<(*const c_void,), ()>>(KEEP_FN);
|
s.declare::<UnsafeExternCFn<(*const c_void,), ()>>(KEEP_FN);
|
||||||
s.declare::<UnsafeExternCFn<(*const u8, usize), bool>>(IS_UTF8_FN);
|
s.declare::<UnsafeExternCFn<(*const u8, usize), bool>>(IS_UTF8_FN);
|
||||||
s.declare::<UnsafeExternCFn<(*mut lua_buffer,), ()>>(DROP_BUFFER_FN);
|
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +154,6 @@ impl Registry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn declare<T: Type>(&mut self, name: impl Display) -> &mut 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>()
|
self.include::<T>()
|
||||||
.funcs
|
.funcs
|
||||||
.insert(name.to_string())
|
.insert(name.to_string())
|
||||||
@ -160,12 +162,11 @@ impl Registry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn preload<T: Type>(&mut self, name: impl Display) -> &mut 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>();
|
self.include::<T>();
|
||||||
let ct = T::name();
|
|
||||||
writeln!(
|
writeln!(
|
||||||
self.lua,
|
self.lua,
|
||||||
r#"__preload["{name}"] = function(...) return __ct.{ct}(...); end;"#,
|
r#"__preload["{name}"] = function(...) return __ct.{}(...); end;"#,
|
||||||
|
T::name()
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self
|
self
|
||||||
@ -191,8 +192,6 @@ impl Display for Registry {
|
|||||||
|
|
||||||
pub unsafe trait Type {
|
pub unsafe trait Type {
|
||||||
fn name() -> impl Display;
|
fn name() -> impl Display;
|
||||||
fn ty() -> TypeType;
|
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display;
|
fn cdecl(name: impl Display) -> impl Display;
|
||||||
fn extern_cdecl(name: impl Display) -> impl Display {
|
fn extern_cdecl(name: impl Display) -> impl Display {
|
||||||
Self::cdecl(name)
|
Self::cdecl(name)
|
||||||
@ -201,13 +200,6 @@ pub unsafe trait Type {
|
|||||||
fn build(b: &mut TypeBuilder);
|
fn build(b: &mut TypeBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
pub enum TypeType {
|
|
||||||
Void,
|
|
||||||
Primitive,
|
|
||||||
Aggregate,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TypeBuilder<'r> {
|
pub struct TypeBuilder<'r> {
|
||||||
registry: &'r mut Registry,
|
registry: &'r mut Registry,
|
||||||
@ -264,7 +256,6 @@ impl<'r> CdefBuilder<'r> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn field<T: Type>(&mut self, name: impl Display) -> &mut Self {
|
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.registry.include::<T>();
|
||||||
self.field_raw(T::cdecl(name))
|
self.field_raw(T::cdecl(name))
|
||||||
}
|
}
|
||||||
@ -318,7 +309,7 @@ pub unsafe trait Metatype {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MetatypeBuilder<'r> {
|
pub struct MetatypeBuilder<'r> {
|
||||||
registry: &'r mut Registry,
|
registry: &'r mut Registry,
|
||||||
ct: String,
|
name: String,
|
||||||
cdef: String,
|
cdef: String,
|
||||||
lua: String,
|
lua: String,
|
||||||
}
|
}
|
||||||
@ -327,7 +318,7 @@ impl<'r> MetatypeBuilder<'r> {
|
|||||||
fn new<T: Metatype>(registry: &'r mut Registry) -> Self {
|
fn new<T: Metatype>(registry: &'r mut Registry) -> Self {
|
||||||
Self {
|
Self {
|
||||||
registry,
|
registry,
|
||||||
ct: T::Target::name().to_string(),
|
name: T::Target::name().to_string(),
|
||||||
cdef: String::new(),
|
cdef: String::new(),
|
||||||
lua: r#"do local __mt, __idx = {}, {}; __mt.__index = __idx; "#.into(),
|
lua: r#"do local __mt, __idx = {}, {}; __mt.__index = __idx; "#.into(),
|
||||||
}
|
}
|
||||||
@ -375,7 +366,7 @@ impl<'r> Drop for MetatypeBuilder<'r> {
|
|||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let Self {
|
let Self {
|
||||||
registry,
|
registry,
|
||||||
ct,
|
name,
|
||||||
cdef,
|
cdef,
|
||||||
lua,
|
lua,
|
||||||
..
|
..
|
||||||
@ -383,18 +374,13 @@ impl<'r> Drop for MetatypeBuilder<'r> {
|
|||||||
|
|
||||||
registry.cdef.push_str(cdef);
|
registry.cdef.push_str(cdef);
|
||||||
registry.lua.push_str(lua);
|
registry.lua.push_str(lua);
|
||||||
writeln!(registry.lua, r#"__metatype(__ct.{ct}, __mt); end;"#).unwrap();
|
writeln!(registry.lua, r#"__metatype(__ct.{name}, __mt); end;"#).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum FfiReturnConvention {
|
|
||||||
ByValue,
|
|
||||||
ByOutParam,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe trait FromFfi: Sized {
|
pub unsafe trait FromFfi: Sized {
|
||||||
type From: Type + Sized;
|
type From: Type + Sized;
|
||||||
|
type FromArg: Type + Sized;
|
||||||
|
|
||||||
fn require_keepalive() -> bool {
|
fn require_keepalive() -> bool {
|
||||||
false
|
false
|
||||||
@ -405,31 +391,32 @@ pub unsafe trait FromFfi: Sized {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self;
|
fn convert(from: Self::From) -> Self;
|
||||||
|
fn convert_arg(from: Self::FromArg) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe trait IntoFfi: Sized {
|
pub unsafe trait ToFfi: Sized {
|
||||||
type Into: Type + Sized;
|
type To: Type + Sized;
|
||||||
|
|
||||||
fn convention() -> FfiReturnConvention {
|
fn postlude(_ret: &str, _conv: FfiReturnConvention) -> impl Display {
|
||||||
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;
|
fn convert(self) -> Self::To;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum FfiReturnConvention {
|
||||||
|
Void,
|
||||||
|
#[default]
|
||||||
|
ByValue,
|
||||||
|
ByOutParam,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MetatypeMethodBuilder<'r, 'm> {
|
pub struct MetatypeMethodBuilder<'r, 'm> {
|
||||||
metatype: &'m mut MetatypeBuilder<'r>,
|
metatype: &'m mut MetatypeBuilder<'r>,
|
||||||
lparams: String, // parameters to the lua function
|
params: String, // parameters to the lua function
|
||||||
cparams: String, // parameters to the lua function
|
args: String, // arguments to the C call
|
||||||
cargs: String, // arguments to the C call
|
|
||||||
prelude: String, // function body prelude
|
prelude: String, // function body prelude
|
||||||
postlude: String, // function body postlude
|
postlude: String, // function body postlude
|
||||||
}
|
}
|
||||||
@ -438,46 +425,26 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
|
|||||||
pub fn new(metatype: &'m mut MetatypeBuilder<'r>) -> Self {
|
pub fn new(metatype: &'m mut MetatypeBuilder<'r>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
metatype,
|
metatype,
|
||||||
lparams: String::new(),
|
params: String::new(),
|
||||||
cparams: String::new(),
|
args: String::new(),
|
||||||
cargs: String::new(),
|
|
||||||
prelude: String::new(),
|
prelude: String::new(),
|
||||||
postlude: String::new(),
|
postlude: String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn param<T: FromFfi>(&mut self, name: impl Display) -> &mut Self {
|
pub fn param<T: FromFfi>(&mut self, name: impl Display) -> &mut Self {
|
||||||
assert!(
|
(!self.params.is_empty()).then(|| self.params.push_str(", "));
|
||||||
T::From::ty() != TypeType::Void,
|
(!self.args.is_empty()).then(|| self.args.push_str(", "));
|
||||||
"cannot declare void parameter"
|
write!(self.params, "{name}").unwrap();
|
||||||
);
|
write!(self.args, "{name}").unwrap();
|
||||||
|
|
||||||
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() {
|
if T::require_keepalive() {
|
||||||
write!(prelude, "local __keep_{name} = {name}; ").unwrap();
|
write!(self.prelude, "local __keep_{name} = {name}; ").unwrap();
|
||||||
write!(postlude, "__C.{KEEP_FN}(__keep_{name}); ").unwrap();
|
write!(self.postlude, "__C.{KEEP_FN}(__keep_{name}); ").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(prelude, "{}", T::prelude(&name.to_string())).unwrap();
|
let name = name.to_string();
|
||||||
|
write!(self.prelude, "{}", T::prelude(&name)).unwrap();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -487,27 +454,13 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
|
|||||||
// this passes one lua `string` argument as two C `const uint8_t *ptr` and `uintptr_t len`
|
// 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
|
// arguments, bypassing the slower generic `&[u8]: FromFfi` path which constructs a
|
||||||
// temporary cdata to pass the string and its length in one argument
|
// temporary cdata to pass the string and its length in one argument
|
||||||
let Self {
|
(!self.params.is_empty()).then(|| self.params.push_str(", "));
|
||||||
lparams,
|
(!self.args.is_empty()).then(|| self.args.push_str(", "));
|
||||||
cparams,
|
write!(self.params, "{name}").unwrap();
|
||||||
cargs,
|
write!(self.args, "{name}, __{name}_len").unwrap();
|
||||||
prelude,
|
write!(self.prelude, "local __{name}_len = 0; ").unwrap();
|
||||||
..
|
|
||||||
} = 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!(
|
write!(
|
||||||
prelude,
|
self.prelude,
|
||||||
r#"if {name} ~= nil then assert(type({name}) == "string", "string expected in argument '{name}', got " .. type({name})); __{name}_len = #{name}; end; "#
|
r#"if {name} ~= nil then assert(type({name}) == "string", "string expected in argument '{name}', got " .. type({name})); __{name}_len = #{name}; end; "#
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -515,110 +468,57 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn param_ignored(&mut self) -> &mut Self {
|
pub fn param_ignored(&mut self) -> &mut Self {
|
||||||
(!self.lparams.is_empty()).then(|| self.lparams.push_str(", "));
|
(!self.params.is_empty()).then(|| self.params.push_str(", "));
|
||||||
write!(self.lparams, "_").unwrap();
|
write!(self.params, "_").unwrap();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call<T: IntoFfi>(&mut self, func: impl Display) {
|
pub fn call<T: ToFfi>(&mut self, func: impl Display, ret: FfiReturnConvention) {
|
||||||
let Self {
|
let Self {
|
||||||
metatype:
|
metatype,
|
||||||
MetatypeBuilder {
|
params,
|
||||||
registry,
|
args,
|
||||||
cdef,
|
|
||||||
lua,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
lparams,
|
|
||||||
cparams,
|
|
||||||
cargs,
|
|
||||||
prelude,
|
prelude,
|
||||||
postlude,
|
postlude,
|
||||||
..
|
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
registry.include::<T::Into>();
|
let lua = &mut metatype.lua;
|
||||||
write!(lua, "function({lparams}) {prelude}").unwrap();
|
write!(lua, "function({params}) {prelude}").unwrap();
|
||||||
|
|
||||||
match T::convention() {
|
match ret {
|
||||||
FfiReturnConvention::ByValue => {
|
FfiReturnConvention::Void => {
|
||||||
if T::Into::ty() == TypeType::Void {
|
write!(lua, "__C.{func}({args}); {postlude}end").unwrap();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
FfiReturnConvention::ByValue => {
|
||||||
writeln!(cdef, "{};", T::Into::cdecl(display!("{func}({cparams})"))).unwrap();
|
let check = T::postlude("__res", ret);
|
||||||
|
write!(
|
||||||
|
lua,
|
||||||
|
"local __res = __C.{func}({args}); {check}{postlude}return __res; end"
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
FfiReturnConvention::ByOutParam => {
|
FfiReturnConvention::ByOutParam => {
|
||||||
let ct = T::Into::name();
|
let ct = T::To::name();
|
||||||
let check = T::postlude("__res");
|
let check = T::postlude("__res", ret);
|
||||||
write!(lua, "local __res = __new(__ct.{ct}); __C.{func}(__res").unwrap();
|
write!(lua, "local __res = __new(__ct.{ct}); __C.{func}(__res").unwrap();
|
||||||
if !cargs.is_empty() {
|
if !args.is_empty() {
|
||||||
write!(lua, ", {cargs}").unwrap();
|
write!(lua, ", {args}").unwrap();
|
||||||
}
|
}
|
||||||
write!(lua, "); {check}{postlude}return __res; end").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 {
|
macro_rules! impl_primitive {
|
||||||
($rty:ty, $cty:expr) => {
|
($rtype:ty, $ctype:expr) => {
|
||||||
unsafe impl Type for $rty {
|
unsafe impl Type for $rtype {
|
||||||
fn name() -> impl Display {
|
fn name() -> impl Display {
|
||||||
$cty
|
$ctype
|
||||||
}
|
|
||||||
|
|
||||||
fn ty() -> TypeType {
|
|
||||||
TypeType::Primitive
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display {
|
fn cdecl(name: impl Display) -> impl Display {
|
||||||
display!("{} {name}", $cty)
|
display!("{} {name}", $ctype)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(_b: &mut TypeBuilder) {}
|
fn build(_b: &mut TypeBuilder) {}
|
||||||
@ -626,122 +526,87 @@ macro_rules! impl_primitive {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_primitive!(bool, "bool");
|
impl_primitive!((), "void");
|
||||||
impl_primitive!(u8, "uint8_t");
|
impl_primitive!(c_void, "void");
|
||||||
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 {
|
unsafe impl ToFfi for () {
|
||||||
type From = bool;
|
//
|
||||||
|
// 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. `c_void` cannot be returned from rust
|
||||||
|
// so it should return the unit type instead.
|
||||||
|
//
|
||||||
|
type To = ();
|
||||||
|
fn convert(self) -> Self::To {}
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl Display {
|
fn postlude(_ret: &str, conv: FfiReturnConvention) -> impl Display {
|
||||||
display!(
|
assert!(
|
||||||
r#"assert(type({arg}) == "boolean", "boolean expected in argument '{arg}', got " .. type({arg})); "#
|
conv == FfiReturnConvention::Void,
|
||||||
)
|
"void type cannot be instantiated"
|
||||||
}
|
);
|
||||||
|
""
|
||||||
fn convert(from: Self::From) -> Self {
|
|
||||||
from
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl IntoFfi for bool {
|
macro_rules! impl_primitive_abi {
|
||||||
type Into = bool;
|
($rtype:ty, $ctype:expr, $ltype:expr $(, $unwrap:expr)?) => {
|
||||||
|
impl_primitive!($rtype, $ctype);
|
||||||
|
|
||||||
fn convert(self) -> Self::Into {
|
//
|
||||||
self
|
// SAFETY: Primitive types are always copyable so we can pass and return them by value.
|
||||||
}
|
//
|
||||||
}
|
unsafe impl FromFfi for $rtype {
|
||||||
|
|
||||||
macro_rules! impl_number_fromabi {
|
|
||||||
($rty:ty) => {
|
|
||||||
unsafe impl FromFfi for $rty {
|
|
||||||
type From = Self;
|
type From = Self;
|
||||||
|
type FromArg = Self;
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl Display {
|
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; "#)
|
display!(r#"assert(type({arg}) == "{0}", "{0} expected in argument '{arg}', got " .. type({arg})); "#, $ltype)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
from
|
from
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_number_fromabi!(u8);
|
fn convert_arg(from: Self::FromArg) -> Self {
|
||||||
impl_number_fromabi!(u16);
|
from
|
||||||
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);
|
unsafe impl ToFfi for $rtype {
|
||||||
impl_number_intoabi!(u16);
|
type To = Self;
|
||||||
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 {
|
fn convert(self) -> Self::To {
|
||||||
($rty:ty) => {
|
|
||||||
unsafe impl IntoFfi for $rty {
|
|
||||||
type Into = Self;
|
|
||||||
|
|
||||||
fn convert(self) -> Self::Into {
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn postlude(ret: &str) -> impl Display {
|
#[allow(unused)]
|
||||||
// this isn't "correct" per se, but it's much more ergonomic to work with numbers in
|
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
|
||||||
// lua than with long longs wrapped in cdata. we gracefully accept the loss of
|
disp(move |f| Ok({
|
||||||
// precision here and that 53 bits of precision for big integers are enough. (the
|
match conv {
|
||||||
// vain of Lua 5.3 integer subtype ;D )
|
FfiReturnConvention::Void => unreachable!(),
|
||||||
display!("{ret} = tonumber({ret}); ")
|
FfiReturnConvention::ByValue => {},
|
||||||
|
// if a primitive type for some reason gets returned by out-param, unwrap
|
||||||
|
// the cdata containing the value to convert it to the equivalent lua value
|
||||||
|
FfiReturnConvention::ByOutParam => { $(write!(f, "{ret} = {}; ", $unwrap(ret))?;)? },
|
||||||
|
}
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_bigint_intoabi!(u64);
|
impl_primitive_abi!(bool, "bool", "boolean", |n| display!("{n} ~= 0"));
|
||||||
impl_bigint_intoabi!(i64);
|
impl_primitive_abi!(u8, "uint8_t", "number", |n| display!("tonumber({n})"));
|
||||||
#[cfg(target_pointer_width = "64")]
|
impl_primitive_abi!(u16, "uint16_t", "number", |n| display!("tonumber({n})"));
|
||||||
impl_bigint_intoabi!(usize);
|
impl_primitive_abi!(u32, "uint32_t", "number", |n| display!("tonumber({n})"));
|
||||||
#[cfg(target_pointer_width = "64")]
|
impl_primitive_abi!(u64, "uint64_t", "number");
|
||||||
impl_bigint_intoabi!(isize);
|
impl_primitive_abi!(usize, "uintptr_t", "number");
|
||||||
|
impl_primitive_abi!(i8, "int8_t", "number", |n| display!("tonumber({n})"));
|
||||||
|
impl_primitive_abi!(i16, "int16_t", "number", |n| display!("tonumber({n})"));
|
||||||
|
impl_primitive_abi!(i32, "int32_t", "number", |n| display!("tonumber({n})"));
|
||||||
|
impl_primitive_abi!(i64, "int64_t", "number");
|
||||||
|
impl_primitive_abi!(isize, "intptr_t", "number");
|
||||||
|
impl_primitive_abi!(c_float, "float", "number", |n| display!("tonumber({n})"));
|
||||||
|
impl_primitive_abi!(c_double, "double", "number", |n| display!("tonumber({n})"));
|
||||||
|
|
||||||
macro_rules! impl_const_ptr {
|
macro_rules! impl_const_ptr {
|
||||||
($ty:ty) => {
|
($ty:ty) => {
|
||||||
@ -750,10 +615,6 @@ macro_rules! impl_const_ptr {
|
|||||||
display!("const_{}_ptr", T::name())
|
display!("const_{}_ptr", T::name())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ty() -> TypeType {
|
|
||||||
TypeType::Primitive
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display {
|
fn cdecl(name: impl Display) -> impl Display {
|
||||||
T::cdecl(display!("const *{name}"))
|
T::cdecl(display!("const *{name}"))
|
||||||
}
|
}
|
||||||
@ -776,10 +637,6 @@ macro_rules! impl_mut_ptr {
|
|||||||
display!("{}_ptr", T::name())
|
display!("{}_ptr", T::name())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ty() -> TypeType {
|
|
||||||
TypeType::Primitive
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display {
|
fn cdecl(name: impl Display) -> impl Display {
|
||||||
T::cdecl(display!("*{name}"))
|
T::cdecl(display!("*{name}"))
|
||||||
}
|
}
|
||||||
@ -808,10 +665,15 @@ macro_rules! impl_ptr_fromabi {
|
|||||||
($ty:ty) => {
|
($ty:ty) => {
|
||||||
unsafe impl<T: Type> FromFfi for $ty {
|
unsafe impl<T: Type> FromFfi for $ty {
|
||||||
type From = Self;
|
type From = Self;
|
||||||
|
type FromArg = Self;
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
from
|
from
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn convert_arg(from: Self::FromArg) -> Self {
|
||||||
|
from
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -821,97 +683,99 @@ impl_ptr_fromabi!(*mut T);
|
|||||||
impl_ptr_fromabi!(Option<&T>);
|
impl_ptr_fromabi!(Option<&T>);
|
||||||
impl_ptr_fromabi!(Option<&mut T>);
|
impl_ptr_fromabi!(Option<&mut T>);
|
||||||
|
|
||||||
//
|
unsafe impl<'s, T: Type> FromFfi for &'s T {
|
||||||
// SAFETY: Return by value for pointers, which maps to a `cdata` return in lua containing the
|
type From = Option<&'s T>;
|
||||||
// pointer (`T *`). We also map null pointers to `nil` for convenience (otherwise it's still a cdata
|
type FromArg = Option<&'s T>;
|
||||||
// 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 {
|
fn prelude(arg: &str) -> impl Display {
|
||||||
display!(r#"assert({arg} ~= nil, "argument '{arg}' cannot be nil"); "#)
|
display!(r#"assert({arg} ~= nil, "argument '{arg}' cannot be nil"); "#)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
// SAFETY: we already checked that the reference is nonnull from the lua side
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
from.is_some(),
|
from.is_some(),
|
||||||
"<{}>::convert() called on a null reference when it was checked to be nonnull",
|
"<&T>::convert() called on a null reference when it was checked to be non-null"
|
||||||
stringify!($ty),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
unsafe { from.unwrap_unchecked() }
|
unsafe { from.unwrap_unchecked() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn convert_arg(from: Self::FromArg) -> Self {
|
||||||
|
FromFfi::convert(from)
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_ref_fromabi!(&'s T);
|
unsafe impl<'s, T: Type> FromFfi for &'s mut T {
|
||||||
impl_ref_fromabi!(&'s 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).
|
||||||
|
//
|
||||||
|
type From = Option<&'s mut T>;
|
||||||
|
type FromArg = Option<&'s mut T>;
|
||||||
|
|
||||||
|
fn prelude(arg: &str) -> impl Display {
|
||||||
|
display!(r#"assert({arg} ~= nil, "argument '{arg}' cannot be nil"); "#)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert(from: Self::From) -> Self {
|
||||||
|
debug_assert!(
|
||||||
|
from.is_some(),
|
||||||
|
"<&mut T>::convert() called on a null reference when it was checked to be non-null"
|
||||||
|
);
|
||||||
|
|
||||||
|
unsafe { from.unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_arg(from: Self::FromArg) -> Self {
|
||||||
|
FromFfi::convert(from)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// SAFETY: `IntoFfi` only for 'static references because we cannot guarantee that the pointer will
|
// SAFETY: Return by value for pointers, which maps to a `cdata` return in lua containing the
|
||||||
// not outlive the pointee otherwise.
|
// 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_ref_intoabi {
|
macro_rules! impl_ptr_toabi {
|
||||||
($ty:ty) => {
|
($ty:ty) => {
|
||||||
unsafe impl<T: Type> IntoFfi for $ty {
|
unsafe impl<T: Type> ToFfi for $ty {
|
||||||
type Into = Self;
|
type To = Self;
|
||||||
|
|
||||||
fn convert(self) -> Self::Into {
|
fn convert(self) -> Self::To {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display {
|
||||||
|
display!("if {ret} == nil then {ret} = nil; end; ")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_ref_intoabi!(&'static T);
|
impl_ptr_toabi!(*const T);
|
||||||
impl_ref_intoabi!(&'static mut T);
|
impl_ptr_toabi!(*mut T);
|
||||||
|
impl_ptr_toabi!(&'static T);
|
||||||
|
impl_ptr_toabi!(&'static mut T);
|
||||||
|
impl_ptr_toabi!(Option<&'static T>);
|
||||||
|
impl_ptr_toabi!(Option<&'static mut T>);
|
||||||
|
|
||||||
//
|
//
|
||||||
// SAFETY: No `FromFfi` and `IntoFfi` for arrays because passing or returning them by value is not a
|
// SAFETY: No `FromFfi` and `ToFfi` for arrays because passing or returning them by value is not a
|
||||||
// thing in C (they are just pointers).
|
// thing in C (they are just pointers).
|
||||||
//
|
//
|
||||||
// TODO: we could automatically convert them to tables and vice-versa
|
// TODO: we could automatically convert them to tables and vice-versa
|
||||||
@ -921,10 +785,6 @@ unsafe impl<T: Type> Type for [T] {
|
|||||||
display!("{}_arr", T::name())
|
display!("{}_arr", T::name())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ty() -> TypeType {
|
|
||||||
TypeType::Aggregate
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display {
|
fn cdecl(name: impl Display) -> impl Display {
|
||||||
display!("{name}[]")
|
display!("{name}[]")
|
||||||
}
|
}
|
||||||
@ -939,10 +799,6 @@ unsafe impl<T: Type, const N: usize> Type for [T; N] {
|
|||||||
display!("{}_arr{N}", T::name())
|
display!("{}_arr{N}", T::name())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ty() -> TypeType {
|
|
||||||
TypeType::Aggregate
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display {
|
fn cdecl(name: impl Display) -> impl Display {
|
||||||
display!("{name}[{N}]")
|
display!("{name}[{N}]")
|
||||||
}
|
}
|
||||||
@ -964,21 +820,17 @@ macro_rules! impl_function {
|
|||||||
// 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`).
|
||||||
//
|
//
|
||||||
// We also can't implement `IntoFfi` because we can't call `FromFfi` and `IntoFfi` for the
|
// We also can't implement `ToFfi` because we can't call `FromFfi` and `ToFfi` 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: Type,)* $ret: Type> Type for $ty<($($arg,)*), $ret> {
|
||||||
fn name() -> impl Display {
|
fn name() -> impl Display {
|
||||||
disp(|f| Ok({
|
disp(|f| Ok({
|
||||||
write!(f, "fn_{}", $ret::name())?;
|
write!(f, "fn_{}", $ret::name())?;
|
||||||
$(write!(f, "__{}", $arg::name())?;)*
|
$(write!(f, "_{}", $arg::name())?;)*
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ty() -> TypeType {
|
|
||||||
TypeType::Primitive
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
let mut _n = 0;
|
||||||
|
78
crates/luaffi/src/option.rs
Normal file
78
crates/luaffi/src/option.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use crate::{Cdef, CdefBuilder, FfiReturnConvention, FromFfi, ToFfi, Type, TypeBuilder, display};
|
||||||
|
use std::{ffi::c_int, fmt::Display, ptr};
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum lua_option<T> {
|
||||||
|
None, // __tag = 0
|
||||||
|
Some(T), // __tag = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: Type> Type for lua_option<T> {
|
||||||
|
fn name() -> impl Display {
|
||||||
|
display!("option__{}", T::name())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cdecl(name: impl Display) -> impl Display {
|
||||||
|
display!("struct option__{} {name}", T::name())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build(b: &mut TypeBuilder) {
|
||||||
|
b.include::<T>().cdef::<Self>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: Type> Cdef for lua_option<T> {
|
||||||
|
fn build(b: &mut CdefBuilder) {
|
||||||
|
b.field::<c_int>("__tag").field::<T>("__value");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: FromFfi> FromFfi for Option<T> {
|
||||||
|
type From = lua_option<T::From>;
|
||||||
|
type FromArg = *mut Self::From; // pass by-ref
|
||||||
|
|
||||||
|
fn require_keepalive() -> bool {
|
||||||
|
T::require_keepalive()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prelude(arg: &str) -> impl Display {
|
||||||
|
let ct = Self::From::name();
|
||||||
|
display!(
|
||||||
|
"if {arg} == nil then {arg} = __new(__ct.{ct}); else {}{arg} = __new(__ct.{ct}, 1, {arg}); end; ",
|
||||||
|
T::prelude(arg)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert(from: Self::From) -> Self {
|
||||||
|
match from {
|
||||||
|
lua_option::Some(value) => Some(T::convert(value)),
|
||||||
|
lua_option::None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_arg(from: Self::FromArg) -> Self {
|
||||||
|
debug_assert!(!from.is_null());
|
||||||
|
Self::convert(unsafe { ptr::replace(from, lua_option::None) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T: ToFfi> ToFfi for Option<T> {
|
||||||
|
type To = lua_option<T::To>;
|
||||||
|
|
||||||
|
fn convert(self) -> Self::To {
|
||||||
|
match self {
|
||||||
|
Some(value) => lua_option::Some(value.convert()),
|
||||||
|
None => lua_option::None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display {
|
||||||
|
// if we don't have a value, return nil. otherwise copy out the inner value immediately,
|
||||||
|
// forget the option cdata, then call postlude on the inner value.
|
||||||
|
display!(
|
||||||
|
"if {ret}.__tag == 0 then {ret} = nil; else {ret} = {ret}.__value; {}end; ",
|
||||||
|
T::postlude(ret, FfiReturnConvention::ByValue)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,72 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
__internal::{disp, display},
|
|
||||||
Cdef, CdefBuilder, IntoFfi, Type, TypeBuilder, TypeType,
|
|
||||||
string::{DROP_BUFFER_FN, lua_buffer},
|
|
||||||
};
|
|
||||||
use std::{ffi::c_int, fmt::Display};
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
pub enum lua_result<T> {
|
|
||||||
Err(lua_buffer), // __tag = 0
|
|
||||||
Ok(T), // __tag = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: Type> Type for lua_result<T> {
|
|
||||||
fn name() -> impl Display {
|
|
||||||
display!("result__{}", T::name())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ty() -> TypeType {
|
|
||||||
TypeType::Aggregate
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display {
|
|
||||||
display!("struct {} {name}", Self::name())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build(b: &mut TypeBuilder) {
|
|
||||||
b.cdef::<Self>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: Type> Cdef for lua_result<T> {
|
|
||||||
fn build(b: &mut CdefBuilder) {
|
|
||||||
b.field::<c_int>("__tag").inner_union(|b| {
|
|
||||||
(T::ty() != TypeType::Void).then(|| b.field::<T>("__value"));
|
|
||||||
b.field::<lua_buffer>("__err");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: IntoFfi, E: Display> IntoFfi for Result<T, E> {
|
|
||||||
type Into = lua_result<T::Into>;
|
|
||||||
|
|
||||||
fn convert(self) -> Self::Into {
|
|
||||||
match self {
|
|
||||||
Ok(value) => lua_result::Ok(T::convert(value)),
|
|
||||||
Err(err) => lua_result::Err(lua_buffer::new(err.to_string())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn postlude(ret: &str) -> impl Display {
|
|
||||||
disp(move |f| {
|
|
||||||
let ct = T::Into::name();
|
|
||||||
write!(f, "if {ret}.__tag ~= 0 then ")?;
|
|
||||||
match T::Into::ty() {
|
|
||||||
TypeType::Void => write!(f, "{ret} = nil; "), // for void results, we don't have a __value
|
|
||||||
TypeType::Primitive => write!(f, "{ret} = {ret}.__value; "),
|
|
||||||
TypeType::Aggregate => write!(f, "{ret} = __new(__ct.{ct}, {ret}.__value); "),
|
|
||||||
}?;
|
|
||||||
write!(f, "{}", T::postlude(ret))?;
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"else \
|
|
||||||
local __{ret}_msg = __intern({ret}.__err.__ptr, {ret}.__err.__len); \
|
|
||||||
__C.{DROP_BUFFER_FN}({ret}.__err); \
|
|
||||||
return error(__{ret}_msg); \
|
|
||||||
end; "
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +1,6 @@
|
|||||||
use crate::{
|
use crate::{__internal::disp, FromFfi, IS_UTF8_FN, Type};
|
||||||
__internal::{disp, display, export},
|
|
||||||
FromFfi, IntoFfi,
|
|
||||||
};
|
|
||||||
use bstr::{BStr, BString};
|
|
||||||
use luaffi_impl::{cdef, metatype};
|
use luaffi_impl::{cdef, metatype};
|
||||||
use std::{fmt::Display, mem::ManuallyDrop, ptr, slice};
|
use std::{fmt, ptr, slice};
|
||||||
|
|
||||||
pub(crate) const IS_UTF8_FN: &str = "luaffi_is_utf8";
|
|
||||||
pub(crate) const DROP_BUFFER_FN: &str = "luaffi_drop_buffer";
|
|
||||||
|
|
||||||
#[unsafe(export_name = "luaffi_is_utf8")]
|
|
||||||
unsafe extern "C" fn __is_utf8(ptr: *const u8, len: usize) -> bool {
|
|
||||||
debug_assert!(!ptr.is_null());
|
|
||||||
simdutf8::basic::from_utf8(unsafe { slice::from_raw_parts(ptr, len) }).is_ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(export_name = "luaffi_drop_buffer")]
|
|
||||||
unsafe extern "C" fn __drop_buffer(buf: *mut lua_buffer) {
|
|
||||||
debug_assert!(!buf.is_null());
|
|
||||||
debug_assert!(!unsafe { (*buf).__ptr.is_null() });
|
|
||||||
drop(unsafe { Vec::from_raw_parts((*buf).__ptr, (*buf).__len, (*buf).__cap) })
|
|
||||||
}
|
|
||||||
|
|
||||||
export![__is_utf8, __drop_buffer];
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
#[cdef]
|
#[cdef]
|
||||||
@ -32,94 +10,52 @@ pub struct lua_buf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[metatype]
|
#[metatype]
|
||||||
impl lua_buf {
|
impl lua_buf {}
|
||||||
// this takes a slice and decomposes it into its raw parts. caller should ensure the result is
|
|
||||||
// used only as long as the original buffer is still alive.
|
|
||||||
pub(crate) fn new(s: &[u8]) -> Self {
|
|
||||||
Self {
|
|
||||||
__ptr: s.as_ptr(),
|
|
||||||
__len: s.len(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn null() -> Self {
|
unsafe impl FromFfi for *const [u8] {
|
||||||
Self {
|
type From = lua_buf;
|
||||||
__ptr: ptr::null(),
|
type FromArg = *const Self::From;
|
||||||
__len: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
#[cdef]
|
|
||||||
pub struct lua_buffer {
|
|
||||||
__ptr: *mut u8,
|
|
||||||
__len: usize,
|
|
||||||
__cap: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[metatype]
|
|
||||||
impl lua_buffer {
|
|
||||||
// this takes ownership of the Vec and decomposes it into its raw parts. the result must be
|
|
||||||
// dropped by `__drop_buffer` (see [`DROP_BUFFER_FN`]).
|
|
||||||
pub(crate) fn new(s: impl Into<Vec<u8>>) -> Self {
|
|
||||||
let s = s.into();
|
|
||||||
Self {
|
|
||||||
__cap: s.capacity(),
|
|
||||||
__len: s.len(),
|
|
||||||
__ptr: ManuallyDrop::new(s).as_mut_ptr(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn null() -> Self {
|
|
||||||
Self {
|
|
||||||
__ptr: ptr::null_mut(),
|
|
||||||
__len: 0,
|
|
||||||
__cap: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<'s> FromFfi for &'s [u8] {
|
|
||||||
type From = Option<&'s lua_buf>;
|
|
||||||
|
|
||||||
fn require_keepalive() -> bool {
|
fn require_keepalive() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl Display {
|
fn prelude(arg: &str) -> impl fmt::Display {
|
||||||
// this converts string arguments to a `lua_buf` with a pointer to the string and its length
|
// this converts string arguments to a `lua_buf` with a pointer to the string and its length
|
||||||
disp(move |f| {
|
disp(move |f| {
|
||||||
|
let ct = Self::From::name();
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
r#"assert(type({arg}) == "string", "string expected in argument '{arg}', got " .. type({arg})); "#
|
r#"if {arg} ~= nil then assert(type({arg}) == "string", "string expected in argument '{arg}', got " .. type({arg})); "#
|
||||||
)?;
|
)?;
|
||||||
// SAFETY: the lua_buf is only valid for as long as the string is alive. we've ensured
|
write!(f, "{arg} = __new(__ct.{ct}, {arg}, #{arg}); end; ")
|
||||||
// that it is alive for at least the duration of the ffi call via `require_keepalive()`.
|
|
||||||
write!(f, "{arg} = __new(__ct.lua_buf, {arg}, #{arg}); end; ")
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
// SAFETY: we already checked that the string is nonnull from the lua side
|
ptr::slice_from_raw_parts(from.__ptr, from.__len)
|
||||||
debug_assert!(from.is_some());
|
}
|
||||||
let from = unsafe { from.unwrap_unchecked() };
|
|
||||||
debug_assert!(!from.__ptr.is_null());
|
fn convert_arg(from: Self::FromArg) -> Self {
|
||||||
unsafe { slice::from_raw_parts(from.__ptr, from.__len) }
|
if from.is_null() {
|
||||||
|
ptr::slice_from_raw_parts(ptr::null(), 0)
|
||||||
|
} else {
|
||||||
|
Self::convert(unsafe { *from })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'s> FromFfi for &'s str {
|
unsafe impl FromFfi for &str {
|
||||||
type From = Option<&'s lua_buf>;
|
type From = lua_buf;
|
||||||
|
type FromArg = *const Self::From;
|
||||||
|
|
||||||
fn require_keepalive() -> bool {
|
fn require_keepalive() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl Display {
|
fn prelude(arg: &str) -> impl fmt::Display {
|
||||||
// this converts string arguments to a `lua_buf` with a pointer to the string and its length
|
|
||||||
// and ensures that the string is valid utf8
|
|
||||||
disp(move |f| {
|
disp(move |f| {
|
||||||
|
let ct = Self::From::name();
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
r#"assert(type({arg}) == "string", "string expected in argument '{arg}', got " .. type({arg})); "#
|
r#"assert(type({arg}) == "string", "string expected in argument '{arg}', got " .. type({arg})); "#
|
||||||
@ -128,144 +64,25 @@ unsafe impl<'s> FromFfi for &'s str {
|
|||||||
f,
|
f,
|
||||||
r#"assert(__C.{IS_UTF8_FN}({arg}, #{arg}), "argument '{arg}' must be a valid utf-8 string"); "#
|
r#"assert(__C.{IS_UTF8_FN}({arg}, #{arg}), "argument '{arg}' must be a valid utf-8 string"); "#
|
||||||
)?;
|
)?;
|
||||||
write!(f, "{arg} = __new(__ct.lua_buf, {arg}, #{arg}); ")
|
write!(f, "{arg} = __new(__ct.{ct}, {arg}, #{arg}); ")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
// SAFETY: we already checked that the string is nonnull and valid utf8 from the lua side
|
// SAFETY: we already checked that the string is nonnull and valid utf8 from the lua side
|
||||||
debug_assert!(from.is_some());
|
|
||||||
let from = unsafe { from.unwrap_unchecked() };
|
|
||||||
debug_assert!(!from.__ptr.is_null());
|
debug_assert!(!from.__ptr.is_null());
|
||||||
let from = unsafe { slice::from_raw_parts(from.__ptr, from.__len) };
|
let s = unsafe { slice::from_raw_parts(from.__ptr, from.__len) };
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
std::str::from_utf8(from).is_ok(),
|
std::str::from_utf8(s).is_ok(),
|
||||||
"<&str>::convert() called on an invalid utf8 string when it was checked to be valid"
|
"<&str>::convert() called on an invalid utf8 string when it was checked to be valid"
|
||||||
);
|
);
|
||||||
unsafe { std::str::from_utf8_unchecked(from) }
|
|
||||||
|
unsafe { std::str::from_utf8_unchecked(s) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_arg(from: Self::FromArg) -> Self {
|
||||||
|
debug_assert!(!from.is_null());
|
||||||
|
unsafe { Self::convert(*from) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl IntoFfi for &'static [u8] {
|
|
||||||
type Into = lua_buf;
|
|
||||||
|
|
||||||
fn convert(self) -> Self::Into {
|
|
||||||
// SAFETY: the slice is 'static so the resulting lua_buf is always valid
|
|
||||||
lua_buf::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn postlude(ret: &str) -> impl Display {
|
|
||||||
display!("{ret} = __intern({ret}.__ptr, {ret}.__len)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl IntoFfi for Vec<u8> {
|
|
||||||
type Into = lua_buffer;
|
|
||||||
|
|
||||||
fn convert(self) -> Self::Into {
|
|
||||||
lua_buffer::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn postlude(ret: &str) -> impl Display {
|
|
||||||
display!(
|
|
||||||
"do local __{ret} = {ret}; {ret} = __intern({ret}.__ptr, {ret}.__len); __C.{DROP_BUFFER_FN}(__{ret}); end; "
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_from_via {
|
|
||||||
($ty:ty, $via:ty) => {
|
|
||||||
unsafe impl<'s> FromFfi for $ty {
|
|
||||||
type From = <$via as FromFfi>::From;
|
|
||||||
|
|
||||||
fn require_keepalive() -> bool {
|
|
||||||
<$via as FromFfi>::require_keepalive()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl Display {
|
|
||||||
<$via as FromFfi>::prelude(arg)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
|
||||||
<$via as FromFfi>::convert(from).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_from_via!(&'s BStr, &'s [u8]);
|
|
||||||
|
|
||||||
macro_rules! impl_into_via {
|
|
||||||
($ty:ty, $via:ty) => {
|
|
||||||
unsafe impl IntoFfi for $ty {
|
|
||||||
type Into = <$via as IntoFfi>::Into;
|
|
||||||
|
|
||||||
fn convert(self) -> Self::Into {
|
|
||||||
<$via as IntoFfi>::convert(self.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn postlude(ret: &str) -> impl Display {
|
|
||||||
<$via as IntoFfi>::postlude(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_into_via!(&'static BStr, &'static [u8]);
|
|
||||||
impl_into_via!(&'static str, &'static BStr);
|
|
||||||
impl_into_via!(BString, Vec<u8>);
|
|
||||||
impl_into_via!(String, BString);
|
|
||||||
|
|
||||||
macro_rules! impl_optional_from {
|
|
||||||
($ty:ty) => {
|
|
||||||
unsafe impl<'s> FromFfi for Option<$ty> {
|
|
||||||
type From = <$ty as FromFfi>::From;
|
|
||||||
|
|
||||||
fn require_keepalive() -> bool {
|
|
||||||
<$ty as FromFfi>::require_keepalive()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl Display {
|
|
||||||
// just pass a null pointer if argument is nil
|
|
||||||
display!(
|
|
||||||
"if {arg} ~= nil then {}end; ",
|
|
||||||
<$ty as FromFfi>::prelude(arg)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
|
||||||
from.map(|s| <$ty as FromFfi>::convert(Some(s)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_optional_from!(&'s [u8]);
|
|
||||||
impl_optional_from!(&'s BStr);
|
|
||||||
impl_optional_from!(&'s str);
|
|
||||||
|
|
||||||
macro_rules! impl_optional_into {
|
|
||||||
($ty:ty, $null:expr) => {
|
|
||||||
unsafe impl IntoFfi for Option<$ty> {
|
|
||||||
type Into = <$ty as IntoFfi>::Into;
|
|
||||||
|
|
||||||
fn convert(self) -> Self::Into {
|
|
||||||
self.map_or($null, <$ty as IntoFfi>::convert)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn postlude(ret: &str) -> impl Display {
|
|
||||||
display!(
|
|
||||||
"if {ret}.__ptr == nil then {ret} = nil; else {}end; ",
|
|
||||||
<$ty as IntoFfi>::postlude(ret)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_optional_into!(&'static [u8], lua_buf::null());
|
|
||||||
impl_optional_into!(&'static BStr, lua_buf::null());
|
|
||||||
impl_optional_into!(&'static str, lua_buf::null());
|
|
||||||
impl_optional_into!(Vec<u8>, lua_buffer::null());
|
|
||||||
impl_optional_into!(BString, lua_buffer::null());
|
|
||||||
impl_optional_into!(String, lua_buffer::null());
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use crate::utils::{ffi_crate, syn_assert, syn_error};
|
use crate::utils::{ffi_crate, syn_assert, syn_error};
|
||||||
use darling::FromMeta;
|
use darling::FromMeta;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote, quote_spanned};
|
use quote::{format_ident, quote};
|
||||||
use syn::{ext::IdentExt, spanned::Spanned, *};
|
use syn::{ext::IdentExt, *};
|
||||||
|
|
||||||
#[derive(Debug, FromMeta)]
|
#[derive(Debug, FromMeta)]
|
||||||
pub struct Args {}
|
pub struct Args {}
|
||||||
@ -24,39 +24,35 @@ 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!(
|
Ok(quote! {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
#item
|
#item
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[allow(unused, non_snake_case)]
|
#[allow(unused, non_snake_case)]
|
||||||
/// Automatically generated by luaffi.
|
|
||||||
mod #mod_name {
|
mod #mod_name {
|
||||||
use super::*;
|
use super::*;
|
||||||
#impl_type
|
#impl_type
|
||||||
#impl_cdef
|
#impl_cdef
|
||||||
}
|
}
|
||||||
))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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 fmt = quote!(::std::format!);
|
||||||
let name = LitStr::new(&ty.unraw().to_string(), span);
|
let name = LitStr::new(&format!("{}", ty.unraw()), ty.span());
|
||||||
|
let cdecl_fmt = LitStr::new(&format!("struct {} {{}}", ty.unraw()), ty.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 {
|
fn name() -> impl ::std::fmt::Display {
|
||||||
#name
|
#name
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ty() -> #ffi::TypeType {
|
|
||||||
#ffi::TypeType::Aggregate
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cdecl(name: impl ::std::fmt::Display) -> impl ::std::fmt::Display {
|
fn cdecl(name: impl ::std::fmt::Display) -> impl ::std::fmt::Display {
|
||||||
::std::format!("struct {} {name}", #name)
|
#fmt(#cdecl_fmt, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(b: &mut #ffi::TypeBuilder) {
|
fn build(b: &mut #ffi::TypeBuilder) {
|
||||||
@ -64,12 +60,11 @@ fn generate_type(ty: &Ident) -> Result<TokenStream> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: we can always implement `IntoFfi` because it transfers ownership from Rust to Lua
|
unsafe impl #ffi::ToFfi for #ty {
|
||||||
unsafe impl #ffi::IntoFfi for #ty {
|
type To = Self;
|
||||||
type Into = Self;
|
fn convert(self) -> Self::To { self }
|
||||||
fn convert(self) -> Self::Into { self }
|
|
||||||
}
|
}
|
||||||
))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_cdef_structure(str: &mut ItemStruct) -> Result<TokenStream> {
|
fn generate_cdef_structure(str: &mut ItemStruct) -> Result<TokenStream> {
|
||||||
@ -81,14 +76,13 @@ 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_build_cdef(&to_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 }
|
||||||
}
|
}
|
||||||
))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_cdef_enum(enu: &mut ItemEnum) -> Result<TokenStream> {
|
fn generate_cdef_enum(enu: &mut ItemEnum) -> Result<TokenStream> {
|
||||||
@ -100,24 +94,22 @@ 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_build_cdef(&to_cfields(&mut variant.fields)?)?;
|
||||||
let build = generate_cdef_build(&get_cfields(&mut variant.fields)?)?;
|
Ok(quote! { b.inner_struct(|b| { #build }); })
|
||||||
Ok(quote_spanned!(span => 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)* });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CField {
|
struct CField {
|
||||||
@ -131,7 +123,7 @@ struct CFieldAttrs {
|
|||||||
opaque: bool,
|
opaque: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cfields(fields: &mut Fields) -> Result<Vec<CField>> {
|
fn to_cfields(fields: &mut Fields) -> Result<Vec<CField>> {
|
||||||
match fields {
|
match fields {
|
||||||
Fields::Named(fields) => fields.named.iter_mut(),
|
Fields::Named(fields) => fields.named.iter_mut(),
|
||||||
Fields::Unnamed(fields) => fields.unnamed.iter_mut(),
|
Fields::Unnamed(fields) => fields.unnamed.iter_mut(),
|
||||||
@ -141,17 +133,17 @@ fn get_cfields(fields: &mut Fields) -> Result<Vec<CField>> {
|
|||||||
.map(|(i, field)| {
|
.map(|(i, field)| {
|
||||||
Ok(CField {
|
Ok(CField {
|
||||||
name: match field.ident {
|
name: match field.ident {
|
||||||
Some(ref name) => name.unraw().to_string(),
|
Some(ref name) => format!("{}", name.unraw()),
|
||||||
None => format!("__{i}"),
|
None => format!("__{i}"),
|
||||||
},
|
},
|
||||||
ty: field.ty.clone(),
|
ty: field.ty.clone(),
|
||||||
attrs: parse_cfield_attrs(&mut field.attrs)?,
|
attrs: parse_attrs(&mut field.attrs)?,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_cfield_attrs(attrs: &mut Vec<Attribute>) -> Result<CFieldAttrs> {
|
fn parse_attrs(attrs: &mut Vec<Attribute>) -> Result<CFieldAttrs> {
|
||||||
let mut parsed = CFieldAttrs::default();
|
let mut parsed = CFieldAttrs::default();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while let Some(attr) = attrs.get(i) {
|
while let Some(attr) = attrs.get(i) {
|
||||||
@ -168,11 +160,11 @@ fn parse_cfield_attrs(attrs: &mut Vec<Attribute>) -> Result<CFieldAttrs> {
|
|||||||
Ok(parsed)
|
Ok(parsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_cdef_build(fields: &[CField]) -> Result<TokenStream> {
|
fn generate_build_cdef(fields: &[CField]) -> Result<TokenStream> {
|
||||||
let mut body = vec![quote!(
|
let mut body = vec![quote! {
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
let mut align = 1;
|
let mut align = 1;
|
||||||
)];
|
}];
|
||||||
|
|
||||||
fn offset(i: usize) -> Ident {
|
fn offset(i: usize) -> Ident {
|
||||||
format_ident!("offset{i}")
|
format_ident!("offset{i}")
|
||||||
@ -181,41 +173,40 @@ fn generate_cdef_build(fields: &[CField]) -> Result<TokenStream> {
|
|||||||
let max = quote!(::std::cmp::Ord::max);
|
let max = quote!(::std::cmp::Ord::max);
|
||||||
let size_of = quote!(::std::mem::size_of);
|
let size_of = quote!(::std::mem::size_of);
|
||||||
let align_of = quote!(::std::mem::align_of);
|
let align_of = quote!(::std::mem::align_of);
|
||||||
|
|
||||||
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>());
|
||||||
let #offset = offset;
|
let #offset = offset;
|
||||||
offset += #size_of::<#ty>();
|
offset += #size_of::<#ty>();
|
||||||
));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
body.push(quote!(
|
body.push(quote! {
|
||||||
// round up final offset to the total alignment of struct for struct size
|
// round up final offset to the total alignment of struct for struct size
|
||||||
let size = (offset + align - 1) & !(align - 1);
|
let size = (offset + align - 1) & !(align - 1);
|
||||||
));
|
});
|
||||||
|
|
||||||
let len = fields.len();
|
let len = fields.len();
|
||||||
for (i, field) in fields.iter().enumerate() {
|
for (i, field) in fields.iter().enumerate() {
|
||||||
let name = &field.name;
|
let name = &field.name;
|
||||||
let ty = &field.ty;
|
let ty = &field.ty;
|
||||||
body.push(if field.attrs.opaque {
|
if field.attrs.opaque {
|
||||||
if i == len - 1 {
|
body.push(if i == len - 1 {
|
||||||
let a = offset(i);
|
let a = offset(i);
|
||||||
quote_spanned!(ty.span() => b.field_opaque(size - #a);) // last field
|
quote! { b.field_opaque(size - #a); } // last field
|
||||||
} else {
|
} else {
|
||||||
let a = offset(i);
|
let a = offset(i);
|
||||||
let b = offset(i + 1);
|
let b = offset(i + 1);
|
||||||
quote_spanned!(ty.span() => b.field_opaque(#b - #a);)
|
quote! { b.field_opaque(#b - #a); }
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote_spanned!(ty.span() => b.field::<#ty>(#name);)
|
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
body.push(quote! { b.field::<#ty>(#name); });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(quote!(#(#body)*))
|
Ok(quote! { #(#body)* })
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
use crate::utils::{
|
use crate::utils::{ffi_crate, is_primitive, is_unit, pat_ident, syn_assert, ty_name};
|
||||||
ffi_crate, is_primitivelike, is_unit, pat_ident, syn_assert, syn_error, ty_name,
|
|
||||||
};
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{ToTokens, format_ident, quote, quote_spanned};
|
use quote::{format_ident, quote};
|
||||||
use std::{collections::HashSet, fmt, iter};
|
use syn::{ext::IdentExt, *};
|
||||||
use syn::{ext::IdentExt, spanned::Spanned, *};
|
|
||||||
|
|
||||||
pub fn transform(mut imp: ItemImpl) -> Result<TokenStream> {
|
pub fn transform(mut imp: ItemImpl) -> Result<TokenStream> {
|
||||||
syn_assert!(
|
syn_assert!(
|
||||||
@ -16,76 +13,94 @@ pub fn transform(mut imp: ItemImpl) -> Result<TokenStream> {
|
|||||||
let impls = generate_impls(&mut imp)?;
|
let impls = generate_impls(&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!(
|
Ok(quote! {
|
||||||
#imp
|
#imp
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[allow(unused, non_snake_case)]
|
#[allow(unused, non_snake_case)]
|
||||||
/// Automatically generated by luaffi.
|
|
||||||
mod #mod_name {
|
mod #mod_name {
|
||||||
use super::*;
|
use super::*;
|
||||||
#impls
|
#impls
|
||||||
}
|
}
|
||||||
))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
|
fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
|
||||||
let ffi = ffi_crate();
|
|
||||||
let ty = imp.self_ty.clone();
|
let ty = imp.self_ty.clone();
|
||||||
let ty_name = ty_name(&ty)?;
|
let ty_name = ty_name(&ty)?;
|
||||||
let mut ffi_funcs = FfiRegistry::new(ty_name.clone());
|
|
||||||
let mut lua_funcs = LuaRegistry::new(ty_name.clone());
|
|
||||||
let mut mms = HashSet::new();
|
|
||||||
let mut lua_drop = None;
|
|
||||||
|
|
||||||
for func in get_ffi_functions(imp)? {
|
let ffi = ffi_crate();
|
||||||
if let Some(mm) = func.attrs.metamethod {
|
let ffi_funcs = get_ffi_functions(imp)?;
|
||||||
syn_assert!(
|
|
||||||
mms.insert(mm),
|
// wrapper extern "C" functions that call the actual implementation
|
||||||
func.name,
|
let ffi_wrappers: Vec<_> = ffi_funcs
|
||||||
"metamethod `{mm}` already defined"
|
.iter()
|
||||||
);
|
.map(generate_ffi_wrapper)
|
||||||
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
|
// ffi function registration code
|
||||||
|
let ffi_register: Vec<_> = ffi_funcs
|
||||||
|
.iter()
|
||||||
|
.map(generate_ffi_register)
|
||||||
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
|
let ffi_register_new = match ffi_funcs
|
||||||
|
.iter()
|
||||||
|
.find(|f| f.attrs.metatable.as_deref() == Some("new"))
|
||||||
|
{
|
||||||
|
Some(_) => None,
|
||||||
|
None => Some({
|
||||||
|
// fallback error constructor to prevent creating uninitialised ctypes
|
||||||
|
let err = format!(r#"function() error("type '{ty_name}' has no constructor"); end"#);
|
||||||
|
quote! { b.metatable_raw("new", #err); }
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ffi_drop_rname = format_ident!("__ffi_drop");
|
||||||
|
let ffi_drop_cname = format!("{ty_name}_drop");
|
||||||
|
let ffi_wrapper_drop = quote! {
|
||||||
|
#[unsafe(export_name = #ffi_drop_cname)]
|
||||||
|
unsafe extern "C" fn #ffi_drop_rname(ptr: *mut Self) {
|
||||||
|
unsafe { ::std::ptr::drop_in_place(ptr) }
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
add_ffi_function(&mut ffi_funcs, &func)?;
|
let ffi_register_drop = quote! {
|
||||||
|
if ::std::mem::needs_drop::<Self>() {
|
||||||
|
b.declare::<#ffi::UnsafeExternCFn<(*mut Self,), ()>>(#ffi_drop_cname);
|
||||||
|
b.metatable_raw("gc", ::std::format_args!("__C.{}", #ffi_drop_cname));
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
for func in get_lua_functions(imp)? {
|
// ffi function symbol export code
|
||||||
if let Some(mm) = func.attrs.metamethod {
|
let ffi_exports = {
|
||||||
syn_assert!(
|
let mut names = vec![&ffi_drop_rname];
|
||||||
mms.insert(mm),
|
names.extend(ffi_funcs.iter().map(|f| &f.rust_name));
|
||||||
func.name,
|
generate_ffi_exports(&ty, names.into_iter())?
|
||||||
"metamethod `{mm}` already defined"
|
};
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if func.attrs.metamethod == Some(Metamethod::Gc) {
|
// lua function registration code
|
||||||
lua_drop = Some(func);
|
let lua_funcs = get_lua_functions(imp)?;
|
||||||
} else {
|
let lua_register: Vec<_> = lua_funcs
|
||||||
add_lua_function(&mut lua_funcs, &func)?;
|
.iter()
|
||||||
}
|
.map(generate_lua_register)
|
||||||
}
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
if !mms.contains(&Metamethod::New) {
|
|
||||||
inject_fallback_new(&mut lua_funcs)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
inject_merged_drop(&mut ffi_funcs, lua_drop.as_ref())?;
|
|
||||||
|
|
||||||
let ffi_shims = &ffi_funcs.shims;
|
|
||||||
let ffi_build = &ffi_funcs.build;
|
|
||||||
let lua_build = &lua_funcs.build;
|
|
||||||
let ffi_exports = generate_ffi_exports(&ffi_funcs)?;
|
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
impl #ty { #(#ffi_shims)* }
|
impl #ty {
|
||||||
|
#(#ffi_wrappers)*
|
||||||
|
#ffi_wrapper_drop
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl #ffi::Metatype for #ty {
|
unsafe impl #ffi::Metatype for #ty {
|
||||||
type Target = Self;
|
type Target = Self;
|
||||||
|
|
||||||
fn build(b: &mut #ffi::MetatypeBuilder) {
|
fn build(b: &mut #ffi::MetatypeBuilder) {
|
||||||
#(#ffi_build)*
|
#(#ffi_register)*
|
||||||
#(#lua_build)*
|
#(#lua_register)*
|
||||||
|
|
||||||
|
#ffi_register_new
|
||||||
|
#ffi_register_drop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,103 +108,20 @@ fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
||||||
enum Metamethod {
|
|
||||||
// known luajit metamethods (see lj_obj.h)
|
|
||||||
// index, newindex, mode and metatable are not included
|
|
||||||
Gc,
|
|
||||||
Eq,
|
|
||||||
Len,
|
|
||||||
Lt,
|
|
||||||
Le,
|
|
||||||
Concat,
|
|
||||||
Call,
|
|
||||||
Add,
|
|
||||||
Sub,
|
|
||||||
Mul,
|
|
||||||
Div,
|
|
||||||
Mod,
|
|
||||||
Pow,
|
|
||||||
Unm,
|
|
||||||
ToString,
|
|
||||||
New,
|
|
||||||
Pairs,
|
|
||||||
Ipairs,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<&Ident> for Metamethod {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(value: &Ident) -> Result<Self> {
|
|
||||||
Ok(match value.to_string().as_str() {
|
|
||||||
"gc" => Self::Gc,
|
|
||||||
"eq" => Self::Eq,
|
|
||||||
"len" => Self::Len,
|
|
||||||
"lt" => Self::Lt,
|
|
||||||
"le" => Self::Le,
|
|
||||||
"concat" => Self::Concat,
|
|
||||||
"call" => Self::Call,
|
|
||||||
"add" => Self::Add,
|
|
||||||
"sub" => Self::Sub,
|
|
||||||
"mul" => Self::Mul,
|
|
||||||
"div" => Self::Div,
|
|
||||||
"mod" => Self::Mod,
|
|
||||||
"pow" => Self::Pow,
|
|
||||||
"unm" => Self::Unm,
|
|
||||||
"tostring" => Self::ToString,
|
|
||||||
"new" => Self::New,
|
|
||||||
"pairs" => Self::Pairs,
|
|
||||||
"ipairs" => Self::Ipairs,
|
|
||||||
_ => syn_error!(value, "unknown metamethod"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Metamethod {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let name = match self {
|
|
||||||
Self::Gc => "gc",
|
|
||||||
Self::Eq => "eq",
|
|
||||||
Self::Len => "len",
|
|
||||||
Self::Lt => "lt",
|
|
||||||
Self::Le => "le",
|
|
||||||
Self::Concat => "concat",
|
|
||||||
Self::Call => "call",
|
|
||||||
Self::Add => "add",
|
|
||||||
Self::Sub => "sub",
|
|
||||||
Self::Mul => "mul",
|
|
||||||
Self::Div => "div",
|
|
||||||
Self::Mod => "mod",
|
|
||||||
Self::Pow => "pow",
|
|
||||||
Self::Unm => "unm",
|
|
||||||
Self::ToString => "tostring",
|
|
||||||
Self::New => "new",
|
|
||||||
Self::Pairs => "pairs",
|
|
||||||
Self::Ipairs => "ipairs",
|
|
||||||
};
|
|
||||||
|
|
||||||
write!(f, "{name}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToTokens for Metamethod {
|
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
||||||
let name = self.to_string();
|
|
||||||
tokens.extend(quote!(#name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FfiFunction {
|
struct FfiFunction {
|
||||||
name: Ident,
|
name: Ident,
|
||||||
is_async: bool,
|
rust_name: Ident,
|
||||||
|
lua_name: String,
|
||||||
|
c_name: String,
|
||||||
params: Vec<PatType>,
|
params: Vec<PatType>,
|
||||||
ret: Type,
|
ret: Type,
|
||||||
|
ret_by_out: bool,
|
||||||
attrs: FfiFunctionAttrs,
|
attrs: FfiFunctionAttrs,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct FfiFunctionAttrs {
|
struct FfiFunctionAttrs {
|
||||||
metamethod: Option<Metamethod>,
|
metatable: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
||||||
@ -203,33 +135,40 @@ fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
|||||||
{
|
{
|
||||||
func.sig.abi = None;
|
func.sig.abi = None;
|
||||||
|
|
||||||
|
// normalise inputs to PatType
|
||||||
let params = func
|
let params = func
|
||||||
.sig
|
.sig
|
||||||
.inputs
|
.inputs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|arg| match arg {
|
.map(|arg| {
|
||||||
|
Ok(match arg {
|
||||||
FnArg::Receiver(recv) => {
|
FnArg::Receiver(recv) => {
|
||||||
let ty = &recv.ty;
|
let ty = &recv.ty;
|
||||||
parse_quote_spanned!(ty.span() => self: #ty)
|
parse_quote! { self: #ty }
|
||||||
}
|
}
|
||||||
FnArg::Typed(ty) => ty.clone(),
|
FnArg::Typed(ty) => ty.clone(),
|
||||||
})
|
})
|
||||||
.collect();
|
})
|
||||||
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
|
// normalise output to Type
|
||||||
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(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let attrs = parse_ffi_function_attrs(&mut func.attrs)?;
|
// whether to use out-param for return values
|
||||||
attrs.metamethod.map(|mm| document_metamethod(func, mm));
|
let ret_by_out = !is_primitive(&ret);
|
||||||
|
|
||||||
funcs.push(FfiFunction {
|
funcs.push(FfiFunction {
|
||||||
name: func.sig.ident.clone(),
|
name: func.sig.ident.clone(),
|
||||||
is_async: func.sig.asyncness.is_some(),
|
rust_name: format_ident!("__ffi_{}", func.sig.ident.unraw()),
|
||||||
|
lua_name: format!("{}", func.sig.ident.unraw()),
|
||||||
|
c_name: format!("{}_{}", ty_name(&imp.self_ty)?, func.sig.ident.unraw()),
|
||||||
params,
|
params,
|
||||||
ret,
|
ret,
|
||||||
attrs,
|
ret_by_out,
|
||||||
|
attrs: parse_ffi_function_attrs(&mut func.attrs)?,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -241,473 +180,209 @@ fn parse_ffi_function_attrs(attrs: &mut Vec<Attribute>) -> Result<FfiFunctionAtt
|
|||||||
let mut parsed = FfiFunctionAttrs::default();
|
let mut parsed = FfiFunctionAttrs::default();
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while let Some(attr) = attrs.get(i) {
|
while let Some(attr) = attrs.get(i) {
|
||||||
if let Some(name) = attr.path().get_ident()
|
if let Some(name) = attr.path().get_ident() {
|
||||||
&& let Ok(method) = Metamethod::try_from(name)
|
if name == "metatable" {
|
||||||
{
|
parsed.metatable = Some(attr.parse_args::<LitStr>()?.value());
|
||||||
match method {
|
|
||||||
Metamethod::Gc => syn_error!(attr, "implement `Drop` instead"),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
parsed.metamethod = Some(method);
|
|
||||||
attrs.remove(i);
|
attrs.remove(i);
|
||||||
} else {
|
continue;
|
||||||
i += 1;
|
} else if name == "new" {
|
||||||
|
parsed.metatable = Some("new".into());
|
||||||
|
attrs.remove(i);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(parsed)
|
Ok(parsed)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum FfiParameterType {
|
#[derive(Debug)]
|
||||||
|
enum FfiArgType {
|
||||||
Default,
|
Default,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ffi_param_type(_ty: &Type) -> FfiParameterType {
|
fn get_ffi_arg_type(_ty: &Type) -> FfiArgType {
|
||||||
FfiParameterType::Default
|
FfiArgType::Default
|
||||||
}
|
}
|
||||||
|
|
||||||
enum FfiReturnType {
|
fn generate_ffi_wrapper(func: &FfiFunction) -> Result<TokenStream> {
|
||||||
Void,
|
|
||||||
ByValue,
|
|
||||||
ByOutParam,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_ffi_ret_type(ty: &Type) -> FfiReturnType {
|
|
||||||
// aggregate type returns use an out-param instead of return by-value.
|
|
||||||
//
|
|
||||||
// out-param isn't strictly necessary (luajit can handle them fine), but luajit doesn't jit
|
|
||||||
// compile aggregate returns yet, so this is more of a performance optimisation.
|
|
||||||
// https://luajit.org/ext_ffi_semantics.html#status
|
|
||||||
//
|
|
||||||
// right now this just heuristically looks for common primitive identifiers like `i32` and
|
|
||||||
// `usize` which has its limitations when it comes to type aliases (proc-macro can't see them),
|
|
||||||
// but the worst thing that can happen with a false detection is an unnecessarily boxed
|
|
||||||
// primitive that gets just unwrapped, or an aggregate suboptimally returned by-value. it should
|
|
||||||
// be correct for 99% of rust code that isn't doing anything weird.
|
|
||||||
//
|
|
||||||
// the builder below has additional assertions to confirm whether our detection was correct.
|
|
||||||
if is_unit(ty) {
|
|
||||||
FfiReturnType::Void
|
|
||||||
} else if is_primitivelike(ty) {
|
|
||||||
FfiReturnType::ByValue
|
|
||||||
} else {
|
|
||||||
FfiReturnType::ByOutParam
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FfiRegistry {
|
|
||||||
ty: Ident,
|
|
||||||
shims: Vec<ImplItemFn>,
|
|
||||||
build: Vec<TokenStream>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FfiRegistry {
|
|
||||||
fn new(ty: Ident) -> Self {
|
|
||||||
Self {
|
|
||||||
ty,
|
|
||||||
shims: vec![],
|
|
||||||
build: vec![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_ffi_function(registry: &mut FfiRegistry, func: &FfiFunction) -> Result<()> {
|
|
||||||
let ffi = ffi_crate();
|
let ffi = ffi_crate();
|
||||||
let ty = ®istry.ty;
|
let name = &func.name;
|
||||||
let func_name = &func.name;
|
let rust_name = &func.rust_name;
|
||||||
let shim_name = format_ident!("__ffi_{}", func_name.unraw());
|
let c_name = &func.c_name;
|
||||||
let lua_name = format!("{}", func_name.unraw());
|
let mut params = vec![];
|
||||||
let c_name = if let Some(priv_name) = lua_name.strip_prefix("__") {
|
let mut args = vec![];
|
||||||
format!("__{}_{priv_name}", ty.unraw())
|
|
||||||
|
for (i, param) in func.params.iter().enumerate() {
|
||||||
|
let name = format_ident!("arg{i}");
|
||||||
|
let ty = ¶m.ty;
|
||||||
|
|
||||||
|
match get_ffi_arg_type(ty) {
|
||||||
|
FfiArgType::Default => {
|
||||||
|
params.push(quote! { #name: <#ty as #ffi::FromFfi>::FromArg });
|
||||||
|
args.push(quote! { <#ty as #ffi::FromFfi>::convert_arg(#name) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (ret, call) = if func.ret_by_out {
|
||||||
|
// make return by out-param the first parameter
|
||||||
|
let ret = &func.ret;
|
||||||
|
params.insert(0, quote! { out: *mut #ret });
|
||||||
|
(
|
||||||
|
quote!(()),
|
||||||
|
quote! { ::std::ptr::write(out, Self::#name(#(#args),*)) },
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
format!("{}_{lua_name}", ty.unraw())
|
let ret = &func.ret;
|
||||||
|
(quote! { #ret }, quote! { Self::#name(#(#args),*) })
|
||||||
};
|
};
|
||||||
|
|
||||||
let func_params = &func.params; // target function parameters
|
Ok(quote! {
|
||||||
let func_ret = &func.ret; // target function return type
|
|
||||||
let mut func_args = vec![]; // target function arguments
|
|
||||||
|
|
||||||
let mut shim_params = vec![]; // shim function parameters
|
|
||||||
let mut shim_ret = if func.is_async {
|
|
||||||
// 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
|
|
||||||
|
|
||||||
// for __new metamethods, ignore the first argument (ctype of self, for which there is no
|
|
||||||
// equivalent in C)
|
|
||||||
if func.attrs.metamethod == Some(Metamethod::New) {
|
|
||||||
build.push(quote!(
|
|
||||||
b.param_ignored();
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i, param) in func_params.iter().enumerate() {
|
|
||||||
let func_param = ¶m.ty;
|
|
||||||
let shim_param = format_ident!("arg{i}");
|
|
||||||
let name = pat_ident(¶m.pat)?.unraw().to_string();
|
|
||||||
|
|
||||||
match get_ffi_param_type(func_param) {
|
|
||||||
FfiParameterType::Default => {
|
|
||||||
shim_params.push(quote_spanned!(func_param.span() =>
|
|
||||||
#shim_param: <#func_param as #ffi::FromFfi>::From
|
|
||||||
));
|
|
||||||
func_args.push(quote_spanned!(param.pat.span() =>
|
|
||||||
<#func_param as #ffi::FromFfi>::convert(#shim_param)
|
|
||||||
));
|
|
||||||
build.push(quote_spanned!(param.pat.span() =>
|
|
||||||
b.param::<#func_param>(#name);
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// shim function body
|
|
||||||
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 {
|
|
||||||
match get_ffi_ret_type(&func_ret) {
|
|
||||||
FfiReturnType::Void => {
|
|
||||||
asserts.push(quote_spanned!(func_ret.span() =>
|
|
||||||
<<#func_ret as #ffi::IntoFfi>::Into as #ffi::Type>::ty() == #ffi::TypeType::Void
|
|
||||||
));
|
|
||||||
}
|
|
||||||
FfiReturnType::ByValue => {
|
|
||||||
asserts.push(quote_spanned!(func_ret.span() =>
|
|
||||||
<#func_ret as #ffi::IntoFfi>::convention() == #ffi::FfiReturnConvention::ByValue
|
|
||||||
));
|
|
||||||
}
|
|
||||||
FfiReturnType::ByOutParam => {
|
|
||||||
asserts.push(quote_spanned!(func_ret.span() =>
|
|
||||||
<#func_ret as #ffi::IntoFfi>::convention() == #ffi::FfiReturnConvention::ByOutParam
|
|
||||||
));
|
|
||||||
|
|
||||||
shim_params.insert(0, quote!(out: *mut #shim_ret));
|
|
||||||
(shim_body, shim_ret) = (quote!(::std::ptr::write(out, #shim_body)), quote!(()));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// build.push(quote_spanned!(func_name.span() =>
|
|
||||||
// b.call_inferred(#c_name, Self::#func_name);
|
|
||||||
// ));
|
|
||||||
|
|
||||||
build.push({
|
|
||||||
let infer_args = iter::repeat_n(quote!(::std::unreachable!()), func_params.len());
|
|
||||||
let infer = if func.is_async {
|
|
||||||
quote!(|| #ffi::future::lua_future::new(Self::#func_name(#(#infer_args),*)))
|
|
||||||
} else {
|
|
||||||
quote!(|| Self::#func_name(#(#infer_args),*))
|
|
||||||
};
|
|
||||||
quote_spanned!(func_name.span() => b.call_inferred(#c_name, #infer);)
|
|
||||||
});
|
|
||||||
|
|
||||||
registry.build.push(quote_spanned!(func_name.span() =>
|
|
||||||
#(::std::assert!(#asserts);)*
|
|
||||||
));
|
|
||||||
|
|
||||||
registry.build.push(match func.attrs.metamethod {
|
|
||||||
Some(ref mm) => quote!(b.metatable(#mm, |b| { #(#build)* });),
|
|
||||||
None => quote!(b.index(#lua_name, |b| { #(#build)* });),
|
|
||||||
});
|
|
||||||
|
|
||||||
registry.shims.push(parse_quote_spanned!(func_name.span() =>
|
|
||||||
#[unsafe(export_name = #c_name)]
|
#[unsafe(export_name = #c_name)]
|
||||||
unsafe extern "C" fn #shim_name(#(#shim_params),*) -> #shim_ret { #shim_body }
|
unsafe extern "C" fn #rust_name(#(#params),*) -> #ret { unsafe { #call } }
|
||||||
));
|
})
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_ffi_exports(registry: &FfiRegistry) -> Result<TokenStream> {
|
fn generate_ffi_register(func: &FfiFunction) -> Result<TokenStream> {
|
||||||
let ty = ®istry.ty;
|
let ffi = ffi_crate();
|
||||||
let names = registry.shims.iter().map(|f| &f.sig.ident);
|
let lua_name = &func.lua_name;
|
||||||
|
let c_name = &func.c_name;
|
||||||
|
|
||||||
Ok(quote_spanned!(ty.span() =>
|
let mut params = vec![];
|
||||||
|
let mut register = vec![];
|
||||||
|
|
||||||
|
// for __new metamethods, ignore the first argument (ctype of self)
|
||||||
|
if func.attrs.metatable.as_deref() == Some("new") {
|
||||||
|
register.push(quote! { b.param_ignored(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
for param in func.params.iter() {
|
||||||
|
let name = format!("{}", pat_ident(¶m.pat)?);
|
||||||
|
let ty = ¶m.ty;
|
||||||
|
|
||||||
|
match get_ffi_arg_type(ty) {
|
||||||
|
FfiArgType::Default => {
|
||||||
|
params.push(quote! { <#ty as #ffi::FromFfi>::FromArg });
|
||||||
|
register.push(quote! { b.param::<#ty>(#name); })
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = &func.ret;
|
||||||
|
let ret_conv = if is_unit(ret) {
|
||||||
|
quote! { #ffi::FfiReturnConvention::Void }
|
||||||
|
} else if func.ret_by_out {
|
||||||
|
quote! { #ffi::FfiReturnConvention::ByOutParam }
|
||||||
|
} else {
|
||||||
|
quote! { #ffi::FfiReturnConvention::ByValue }
|
||||||
|
};
|
||||||
|
|
||||||
|
let declare = if func.ret_by_out {
|
||||||
|
quote! { b.declare::<#ffi::UnsafeExternCFn<(*mut #ret, #(#params,)*), ()>>(#c_name); }
|
||||||
|
} else {
|
||||||
|
quote! { b.declare::<#ffi::UnsafeExternCFn<(#(#params,)*), #ret>>(#c_name); }
|
||||||
|
};
|
||||||
|
|
||||||
|
let register = match func.attrs.metatable {
|
||||||
|
Some(ref mt) => quote! {
|
||||||
|
b.metatable(#mt, |b| {
|
||||||
|
#(#register)*
|
||||||
|
b.call::<#ret>(#c_name, #ret_conv);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
None => quote! {
|
||||||
|
b.index(#lua_name, |b| {
|
||||||
|
#(#register)*
|
||||||
|
b.call::<#ret>(#c_name, #ret_conv);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(quote! { #declare #register })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_ffi_exports<'a>(
|
||||||
|
ty: &Type,
|
||||||
|
names: impl Iterator<Item = &'a Ident>,
|
||||||
|
) -> Result<TokenStream> {
|
||||||
|
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]
|
||||||
static __FFI_EXPORTS: &[fn()] = unsafe {
|
static __FFI_EXPORTS: &[fn()] = unsafe {
|
||||||
&[#(::std::mem::transmute(#ty::#names as *const ())),*]
|
&[#(::std::mem::transmute(#ty::#names as *const ())),*]
|
||||||
};
|
};
|
||||||
))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LuaFunction {
|
struct LuaFunction {
|
||||||
name: Ident,
|
name: String,
|
||||||
params: Vec<Pat>,
|
params: Vec<Pat>,
|
||||||
body: Block,
|
body: Block,
|
||||||
attrs: LuaFunctionAttrs,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct LuaFunctionAttrs {
|
|
||||||
metamethod: Option<Metamethod>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_lua_functions(imp: &mut ItemImpl) -> Result<Vec<LuaFunction>> {
|
fn get_lua_functions(imp: &mut ItemImpl) -> Result<Vec<LuaFunction>> {
|
||||||
let mut funcs = vec![];
|
let mut funcs = vec![];
|
||||||
|
let mut i = 0;
|
||||||
|
|
||||||
for item in imp.items.iter_mut() {
|
while i < imp.items.len() {
|
||||||
if let ImplItem::Fn(func) = item
|
if let ImplItem::Fn(ref mut func) = imp.items[i]
|
||||||
&& let Some(ref abi) = func.sig.abi
|
&& let Some(ref abi) = func.sig.abi
|
||||||
&& let Some(ref abi) = abi.name
|
&& let Some(ref abi) = abi.name
|
||||||
&& abi.value() == "Lua"
|
&& abi.value() == "Lua"
|
||||||
{
|
{
|
||||||
|
// normalise inputs to Pat
|
||||||
let mut params: Vec<_> = func
|
let mut params: Vec<_> = func
|
||||||
.sig
|
.sig
|
||||||
.inputs
|
.inputs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|arg| {
|
.map(|arg| {
|
||||||
Ok(match arg {
|
Ok(match arg {
|
||||||
FnArg::Receiver(recv) => Pat::Type(parse_quote_spanned!(recv.span() =>
|
FnArg::Receiver(recv) => {
|
||||||
self: cdata
|
syn_assert!(ty_name(&recv.ty)? == "Self", recv, "must be `self`");
|
||||||
)),
|
syn_assert!(recv.mutability.is_none(), recv, "cannot be mut");
|
||||||
|
Pat::Type(parse_quote! { self: cdata })
|
||||||
|
}
|
||||||
FnArg::Typed(ty) => Pat::Type(ty.clone()),
|
FnArg::Typed(ty) => Pat::Type(ty.clone()),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Result<_>>()?;
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
if let Some(ref variadic) = func.sig.variadic {
|
if let Some(_) = func.sig.variadic {
|
||||||
params.push(parse_quote_spanned!(variadic.span() => variadic!()));
|
params.push(parse_quote!(variadic!()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let attrs = parse_lua_function_attrs(&mut func.attrs)?;
|
// shouldn't specify an output type
|
||||||
attrs.metamethod.map(|mm| document_metamethod(func, mm));
|
syn_assert!(
|
||||||
|
matches!(func.sig.output, ReturnType::Default),
|
||||||
|
func.sig.output,
|
||||||
|
"cannot have return type"
|
||||||
|
);
|
||||||
|
|
||||||
funcs.push(LuaFunction {
|
funcs.push(LuaFunction {
|
||||||
name: func.sig.ident.clone(),
|
name: format!("{}", func.sig.ident.unraw()),
|
||||||
params,
|
|
||||||
body: func.block.clone(),
|
body: func.block.clone(),
|
||||||
attrs,
|
params,
|
||||||
});
|
});
|
||||||
|
|
||||||
stub_lua_function(func)?;
|
imp.items.remove(i);
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(funcs)
|
Ok(funcs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stub_lua_function(func: &mut ImplItemFn) -> Result<()> {
|
fn generate_lua_register(func: &LuaFunction) -> Result<TokenStream> {
|
||||||
let ffi = ffi_crate();
|
let ffi = ffi_crate();
|
||||||
|
let name = &func.name;
|
||||||
// converts an extern "Lua" function into a regular function with no body to allow for
|
|
||||||
// documentation generation
|
|
||||||
func.sig.abi = None;
|
|
||||||
func.attrs.push(parse_quote!(#[allow(unused)]));
|
|
||||||
func.block = parse_quote!({
|
|
||||||
::std::unreachable!("cannot call lua function from rust");
|
|
||||||
});
|
|
||||||
|
|
||||||
let inputs = &mut func.sig.inputs;
|
|
||||||
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 {
|
|
||||||
let ty = quote_spanned!(variadic.span() => variadic);
|
|
||||||
inputs.push(parse_quote!(rest: #ffi::__internal::stub_types::#ty));
|
|
||||||
func.sig.variadic = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
|
||||||
"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> {
|
|
||||||
let mut parsed = LuaFunctionAttrs::default();
|
|
||||||
let mut i = 0;
|
|
||||||
while let Some(attr) = attrs.get(i) {
|
|
||||||
if let Some(name) = attr.path().get_ident()
|
|
||||||
&& let Ok(method) = Metamethod::try_from(name)
|
|
||||||
{
|
|
||||||
match method {
|
|
||||||
Metamethod::New => syn_error!(attr, r#"cannot be applied to a lua function"#),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
parsed.metamethod = Some(method);
|
|
||||||
attrs.remove(i);
|
|
||||||
} else {
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(parsed)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LuaRegistry {
|
|
||||||
ty: Ident,
|
|
||||||
build: Vec<TokenStream>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LuaRegistry {
|
|
||||||
fn new(ty: Ident) -> Self {
|
|
||||||
Self { ty, build: vec![] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_lua_function(registry: &mut LuaRegistry, func: &LuaFunction) -> Result<()> {
|
|
||||||
let ffi = ffi_crate();
|
|
||||||
let luaify = quote!(#ffi::__internal::luaify!);
|
|
||||||
let name = func.name.unraw().to_string();
|
|
||||||
let params = &func.params;
|
let params = &func.params;
|
||||||
let body = &func.body;
|
let body = &func.body;
|
||||||
|
|
||||||
registry.build.push(match func.attrs.metamethod {
|
Ok(quote! {
|
||||||
Some(ref mm) => quote!(b.metatable_raw(#mm, #luaify(|#(#params),*| #body));),
|
b.index_raw(#name, #ffi::__internal::luaify!(|#(#params),*| #body));
|
||||||
None => quote!(b.index_raw(#name, #luaify(|#(#params),*| #body));),
|
})
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn document_metamethod(func: &mut ImplItemFn, method: Metamethod) {
|
|
||||||
let s = match method {
|
|
||||||
Metamethod::Eq => "This is a metamethod which is called by the `==` operator.".into(),
|
|
||||||
Metamethod::Len => "This is a metamethod which is called by the `#` operator.".into(),
|
|
||||||
Metamethod::Lt => "This is a metamethod which is called by the `<` operator.".into(),
|
|
||||||
Metamethod::Le => "This is a metamethod which is called by the `<=` operator.".into(),
|
|
||||||
Metamethod::Concat => "This is a metamethod which is called by the `..` operator.".into(),
|
|
||||||
Metamethod::Add => "This is a metamethod which is called by the `+` operator.".into(),
|
|
||||||
Metamethod::Sub => "This is a metamethod which is called by the `-` operator.".into(),
|
|
||||||
Metamethod::Mul => "This is a metamethod which is called by the `*` operator.".into(),
|
|
||||||
Metamethod::Div => "This is a metamethod which is called by the `/` operator.".into(),
|
|
||||||
Metamethod::Mod => "This is a metamethod which is called by the `%` operator.".into(),
|
|
||||||
Metamethod::Pow => "This is a metamethod which is called by the `^` operator.".into(),
|
|
||||||
Metamethod::Unm => "This is a metamethod which is called by the `-` operator.".into(),
|
|
||||||
Metamethod::ToString => {
|
|
||||||
"This is a metamethod which can be called by the `tostring` built-in function.".into()
|
|
||||||
}
|
|
||||||
Metamethod::Pairs => {
|
|
||||||
"This is a metamethod which can be called by the `pairs` built-in function.".into()
|
|
||||||
}
|
|
||||||
Metamethod::Ipairs => {
|
|
||||||
"This is a metamethod which can be called by the `ipairs` built-in function.".into()
|
|
||||||
}
|
|
||||||
_ => format!("This is a metamethod and cannot be called directly."),
|
|
||||||
};
|
|
||||||
|
|
||||||
func.attrs.push(parse_quote!(#[doc = ""]));
|
|
||||||
func.attrs.push(parse_quote!(#[doc = #s]));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inject_fallback_new(registry: &mut LuaRegistry) -> Result<()> {
|
|
||||||
let ty = ®istry.ty;
|
|
||||||
let lua = format!(
|
|
||||||
r#"function() error("type '{}' has no constructor"); end"#,
|
|
||||||
ty.unraw(),
|
|
||||||
);
|
|
||||||
|
|
||||||
registry.build.push(quote!(
|
|
||||||
b.metatable_raw("new", #lua);
|
|
||||||
));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inject_merged_drop(registry: &mut FfiRegistry, lua: Option<&LuaFunction>) -> Result<()> {
|
|
||||||
let ffi = ffi_crate();
|
|
||||||
let luaify = quote!(#ffi::__internal::luaify!);
|
|
||||||
let ty = ®istry.ty;
|
|
||||||
let shim_name = format_ident!("__ffi_drop");
|
|
||||||
let c_name = format_ident!("{}_drop", ty.unraw());
|
|
||||||
let c_name_str = c_name.to_string();
|
|
||||||
|
|
||||||
if let Some(lua) = lua {
|
|
||||||
syn_assert!(
|
|
||||||
lua.params.len() == 1,
|
|
||||||
lua.name,
|
|
||||||
"finaliser must take exactly one parameter"
|
|
||||||
);
|
|
||||||
|
|
||||||
syn_assert!(
|
|
||||||
pat_ident(&lua.params[0])? == "self",
|
|
||||||
lua.params[0],
|
|
||||||
"finaliser parameter must be `self`"
|
|
||||||
);
|
|
||||||
|
|
||||||
let params = &lua.params;
|
|
||||||
let body = &lua.body;
|
|
||||||
|
|
||||||
registry.build.push(quote_spanned!(ty.span() =>
|
|
||||||
if ::std::mem::needs_drop::<Self>() {
|
|
||||||
// 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
|
|
||||||
b.declare::<#ffi::UnsafeExternCFn<(*mut Self,), ()>>(#c_name_str);
|
|
||||||
b.metatable_raw("gc", #luaify(|self| {
|
|
||||||
raw!(#luaify(#body)); // embed the lua part inside a do block
|
|
||||||
__C::#c_name(self);
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
// we only have a lua-side finaliser
|
|
||||||
b.metatable_raw("gc", #luaify(|#(#params),*| #body));
|
|
||||||
}
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
registry.build.push(quote_spanned!(ty.span() =>
|
|
||||||
if ::std::mem::needs_drop::<Self>() {
|
|
||||||
// we only have a rust drop
|
|
||||||
b.declare::<#ffi::UnsafeExternCFn<(*mut Self,), ()>>(#c_name_str);
|
|
||||||
b.metatable_raw("gc", ::std::format_args!("__C.{}", #c_name_str));
|
|
||||||
}
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
registry.shims.push(parse_quote_spanned!(ty.span() =>
|
|
||||||
#[unsafe(export_name = #c_name_str)]
|
|
||||||
unsafe extern "C" fn #shim_name(ptr: *mut Self) {
|
|
||||||
unsafe { ::std::ptr::drop_in_place(ptr) }
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ use syn::{spanned::Spanned, *};
|
|||||||
|
|
||||||
macro_rules! syn_error {
|
macro_rules! syn_error {
|
||||||
($src:expr, $($fmt:expr),+) => {{
|
($src:expr, $($fmt:expr),+) => {{
|
||||||
|
use syn::spanned::*;
|
||||||
return Err(syn::Error::new($src.span(), format!($($fmt),*)));
|
return Err(syn::Error::new($src.span(), format!($($fmt),*)));
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
@ -55,11 +56,11 @@ pub fn is_unit(ty: &Type) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_primitivelike(ty: &Type) -> bool {
|
pub fn is_primitive(ty: &Type) -> bool {
|
||||||
match ty {
|
match ty {
|
||||||
Type::Tuple(tuple) if tuple.elems.is_empty() => true, // unit type
|
Type::Tuple(tuple) if tuple.elems.is_empty() => true, // unit type
|
||||||
Type::Reference(_) | Type::Ptr(_) => true,
|
Type::Reference(_) | Type::Ptr(_) => true,
|
||||||
Type::Paren(paren) => is_primitivelike(&paren.elem),
|
Type::Paren(paren) => is_primitive(&paren.elem),
|
||||||
Type::Path(path) => {
|
Type::Path(path) => {
|
||||||
if let Some(name) = path.path.get_ident() {
|
if let Some(name) = path.path.get_ident() {
|
||||||
matches!(
|
matches!(
|
||||||
|
@ -523,7 +523,7 @@ fn generate_expr_tuple(f: &mut Formatter, tuple: &ExprTuple, cx: Context) -> Res
|
|||||||
f.write("nil");
|
f.write("nil");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
_ if cx.is_ret() || cx.is_multi_expr() => generate_punctuated_expr(f, &tuple.elems),
|
_ if cx.is_multi_expr() => generate_punctuated_expr(f, &tuple.elems),
|
||||||
_ => syn_error!(tuple, "expected single-valued expression"),
|
_ => syn_error!(tuple, "expected single-valued expression"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,7 +158,7 @@ impl Visitor {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let tmp = format_ident!("__{ident}");
|
let tmp = format_ident!("_{ident}");
|
||||||
let span = cast.span();
|
let span = cast.span();
|
||||||
*expr = match ty {
|
*expr = match ty {
|
||||||
LuaType::Any => parse_quote_spanned!(span => {}),
|
LuaType::Any => parse_quote_spanned!(span => {}),
|
||||||
|
@ -81,7 +81,7 @@ fn local_fn() {
|
|||||||
fn check(self: string, arg: number) {}
|
fn check(self: string, arg: number) {}
|
||||||
inner
|
inner
|
||||||
}),
|
}),
|
||||||
r#"function()local function check(self,arg)do if type(self)=="number"then self=tostring(self);else assert(type(self)=="string","string expected in \'self\', got "..type(self));end;end;do local __arg=arg;arg=tonumber(arg);assert(arg~=nil,"number expected in \'arg\', got "..type(__arg));end;end;return inner;end"#
|
r#"function()local function check(self,arg)do if type(self)=="number"then self=tostring(self);else assert(type(self)=="string","string expected in \'self\', got "..type(self));end;end;do local _arg=arg;arg=tonumber(arg);assert(arg~=nil,"number expected in \'arg\', got "..type(_arg));end;end;return inner;end"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +220,7 @@ fn type_checks() {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
luaify!(|s| { s as number }),
|
luaify!(|s| { s as number }),
|
||||||
r#"function(s)do local __s=s;s=tonumber(s);assert(s~=nil,"number expected in \'s\', got "..type(__s));end;end"#
|
r#"function(s)do local _s=s;s=tonumber(s);assert(s~=nil,"number expected in \'s\', got "..type(_s));end;end"#
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
luaify!(|s| { s as nil }),
|
luaify!(|s| { s as nil }),
|
||||||
|
@ -214,11 +214,6 @@ fn build_runtime(src_path: &Path) {
|
|||||||
let status = panic_err!(make.status(), "failed to execute make");
|
let status = panic_err!(make.status(), "failed to execute make");
|
||||||
(!status.success()).then(|| panic!("failed to compile luajit: {status}: {make:?}"));
|
(!status.success()).then(|| panic!("failed to compile luajit: {status}: {make:?}"));
|
||||||
|
|
||||||
println!(
|
|
||||||
"cargo::rustc-env=LUAJIT_SYS_JITLIB={}",
|
|
||||||
src_path.join("jit").display(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if feature!("runtime") {
|
if feature!("runtime") {
|
||||||
println!("cargo::rustc-link-search=native={}", src_path.display());
|
println!("cargo::rustc-link-search=native={}", src_path.display());
|
||||||
println!("cargo::rustc-link-lib=static=luajit");
|
println!("cargo::rustc-link-lib=static=luajit");
|
||||||
|
@ -1,53 +1,14 @@
|
|||||||
#![allow(nonstandard_style)]
|
#![allow(nonstandard_style)]
|
||||||
use std::{ffi::*, ptr};
|
use std::{ffi::*, ptr};
|
||||||
|
|
||||||
|
include!(env!("LUAJIT_SYS_BINDGEN"));
|
||||||
|
|
||||||
// #[cfg(all(panic = "abort", feature = "unwind"))]
|
// #[cfg(all(panic = "abort", feature = "unwind"))]
|
||||||
// compile_error!(r#"feature "unwind" cannot be enabled if panic = "abort""#);
|
// compile_error!(r#"feature "unwind" cannot be enabled if panic = "abort""#);
|
||||||
|
|
||||||
// #[cfg(all(panic = "unwind", not(feature = "unwind")))]
|
// #[cfg(all(panic = "unwind", not(feature = "unwind")))]
|
||||||
// compile_error!(r#"feature "unwind" must be enabled if panic = "unwind""#);
|
// compile_error!(r#"feature "unwind" must be enabled if panic = "unwind""#);
|
||||||
|
|
||||||
include!(env!("LUAJIT_SYS_BINDGEN"));
|
|
||||||
|
|
||||||
pub unsafe extern "C" fn luaJIT_openlibs(L: *mut lua_State) {
|
|
||||||
unsafe {
|
|
||||||
lua_getglobal(L, c"package".as_ptr());
|
|
||||||
lua_getfield(L, -1, c"preload".as_ptr());
|
|
||||||
lua_replace(L, -2);
|
|
||||||
macro_rules! load {
|
|
||||||
($n:literal, $f:literal) => {{
|
|
||||||
let n: &'static CStr = $n;
|
|
||||||
let f = include_bytes!(concat!(env!("LUAJIT_SYS_JITLIB"), "/", $f));
|
|
||||||
if luaL_loadbuffer(L, f.as_ptr().cast(), f.len(), n.as_ptr()) == 0 {
|
|
||||||
lua_setfield(L, -2, n.as_ptr());
|
|
||||||
} else {
|
|
||||||
lua_error(L);
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
load!(c"jit.vmdef", "vmdef.lua");
|
|
||||||
load!(c"jit.dis_x86", "dis_x86.lua");
|
|
||||||
load!(c"jit.dis_x64", "dis_x64.lua");
|
|
||||||
load!(c"jit.dis_arm", "dis_arm.lua");
|
|
||||||
load!(c"jit.dis_arm64", "dis_arm64.lua");
|
|
||||||
load!(c"jit.dis_arm64be", "dis_arm64be.lua");
|
|
||||||
load!(c"jit.dis_ppc", "dis_ppc.lua");
|
|
||||||
load!(c"jit.dis_mips", "dis_mips.lua");
|
|
||||||
load!(c"jit.dis_mipsel", "dis_mipsel.lua");
|
|
||||||
load!(c"jit.dis_mips64", "dis_mips64.lua");
|
|
||||||
load!(c"jit.dis_mips64el", "dis_mips64el.lua");
|
|
||||||
load!(c"jit.dis_mips64r6", "dis_mips64r6.lua");
|
|
||||||
load!(c"jit.dis_mips64r6el", "dis_mips64r6el.lua");
|
|
||||||
load!(c"jit.bc", "bc.lua");
|
|
||||||
load!(c"jit.bcsave", "bcsave.lua");
|
|
||||||
load!(c"jit.v", "v.lua");
|
|
||||||
load!(c"jit.p", "p.lua");
|
|
||||||
load!(c"jit.dump", "dump.lua");
|
|
||||||
load!(c"jit.zone", "zone.lua");
|
|
||||||
lua_pop(L, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// constants not exposed by lua.h
|
// constants not exposed by lua.h
|
||||||
pub const LUA_TPROTO: c_int = LUA_TTHREAD + 1;
|
pub const LUA_TPROTO: c_int = LUA_TTHREAD + 1;
|
||||||
pub const LUA_TCDATA: c_int = LUA_TTHREAD + 2;
|
pub const LUA_TCDATA: c_int = LUA_TTHREAD + 2;
|
||||||
|
@ -505,12 +505,10 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn open_cb(L: *mut lua_State) -> c_int {
|
unsafe extern "C" fn open_cb(L: *mut lua_State) -> c_int {
|
||||||
unsafe {
|
// SAFETY: we wrap luaL_openlibs so we can potentially catch any library open errors
|
||||||
luaL_openlibs(L);
|
unsafe { luaL_openlibs(L) }
|
||||||
luaJIT_openlibs(L); // luajit-sys extension to open jitlibs
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new empty thread (coroutine) associated with this state.
|
/// Creates a new empty thread (coroutine) associated with this state.
|
||||||
pub fn new_thread(&self) -> Self {
|
pub fn new_thread(&self) -> Self {
|
||||||
@ -615,7 +613,7 @@ impl Stack {
|
|||||||
///
|
///
|
||||||
/// Equivalent to [`lua_settop`].
|
/// Equivalent to [`lua_settop`].
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if `n` is negative.
|
/// Panics if `n` is negative.
|
||||||
pub fn resize(&mut self, n: c_int) {
|
pub fn resize(&mut self, n: c_int) {
|
||||||
@ -634,7 +632,7 @@ impl Stack {
|
|||||||
///
|
///
|
||||||
/// Equivalent to [`lua_checkstack`].
|
/// Equivalent to [`lua_checkstack`].
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if `n` is negative or reallocation fails.
|
/// Panics if `n` is negative or reallocation fails.
|
||||||
pub fn ensure(&self, n: c_int) {
|
pub fn ensure(&self, n: c_int) {
|
||||||
@ -647,7 +645,7 @@ impl Stack {
|
|||||||
///
|
///
|
||||||
/// Equivalent to [`lua_pop`].
|
/// Equivalent to [`lua_pop`].
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if there are less than `n` values on the stack.
|
/// Panics if there are less than `n` values on the stack.
|
||||||
pub fn pop(&mut self, n: c_int) {
|
pub fn pop(&mut self, n: c_int) {
|
||||||
@ -662,7 +660,7 @@ impl Stack {
|
|||||||
///
|
///
|
||||||
/// Equivalent to [`lua_insert`].
|
/// Equivalent to [`lua_insert`].
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if the stack is empty or the index `idx` is invalid.
|
/// Panics if the stack is empty or the index `idx` is invalid.
|
||||||
pub fn pop_insert(&mut self, idx: c_int) {
|
pub fn pop_insert(&mut self, idx: c_int) {
|
||||||
@ -679,7 +677,7 @@ impl Stack {
|
|||||||
///
|
///
|
||||||
/// Equivalent to [`lua_replace`].
|
/// Equivalent to [`lua_replace`].
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if the stack is empty or the index `idx` is invalid.
|
/// Panics if the stack is empty or the index `idx` is invalid.
|
||||||
pub fn pop_replace(&mut self, idx: c_int) {
|
pub fn pop_replace(&mut self, idx: c_int) {
|
||||||
@ -722,7 +720,7 @@ impl Stack {
|
|||||||
|
|
||||||
/// Handle for the value at index `idx`.
|
/// Handle for the value at index `idx`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if the index `idx` is invalid.
|
/// Panics if the index `idx` is invalid.
|
||||||
pub fn slot<'s>(&'s self, idx: c_int) -> Slot<'s> {
|
pub fn slot<'s>(&'s self, idx: c_int) -> Slot<'s> {
|
||||||
@ -760,7 +758,7 @@ impl Stack {
|
|||||||
///
|
///
|
||||||
/// Equivalent to [`lua_pushcclosure`].
|
/// Equivalent to [`lua_pushcclosure`].
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if the given function pointer is null.
|
/// Panics if the given function pointer is null.
|
||||||
pub fn push_function_raw(&mut self, f: lua_CFunction, upvals: c_int) {
|
pub fn push_function_raw(&mut self, f: lua_CFunction, upvals: c_int) {
|
||||||
@ -776,7 +774,7 @@ impl Stack {
|
|||||||
///
|
///
|
||||||
/// Equivalent to [`lua_rawget`].
|
/// Equivalent to [`lua_rawget`].
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if the value at index `idx` is not a table.
|
/// Panics if the value at index `idx` is not a table.
|
||||||
pub fn get(&mut self, idx: c_int) {
|
pub fn get(&mut self, idx: c_int) {
|
||||||
@ -796,7 +794,7 @@ impl Stack {
|
|||||||
///
|
///
|
||||||
/// Equivalent to [`lua_rawgeti`].
|
/// Equivalent to [`lua_rawgeti`].
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if the value at index `idx` is not a table.
|
/// Panics if the value at index `idx` is not a table.
|
||||||
pub fn geti(&mut self, idx: c_int, n: c_int) {
|
pub fn geti(&mut self, idx: c_int, n: c_int) {
|
||||||
@ -818,7 +816,7 @@ impl Stack {
|
|||||||
///
|
///
|
||||||
/// Equivalent to [`lua_rawset`].
|
/// Equivalent to [`lua_rawset`].
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if the value at index `idx` is not a table.
|
/// Panics if the value at index `idx` is not a table.
|
||||||
pub fn set(&mut self, idx: c_int) {
|
pub fn set(&mut self, idx: c_int) {
|
||||||
@ -840,7 +838,7 @@ impl Stack {
|
|||||||
///
|
///
|
||||||
/// Equivalent to [`lua_rawseti`].
|
/// Equivalent to [`lua_rawseti`].
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if the value at index `idx` is not a table.
|
/// Panics if the value at index `idx` is not a table.
|
||||||
pub fn seti(&mut self, idx: c_int, n: c_int) {
|
pub fn seti(&mut self, idx: c_int, n: c_int) {
|
||||||
@ -865,7 +863,7 @@ impl Stack {
|
|||||||
///
|
///
|
||||||
/// Equivalent to `table.pack(...)`.
|
/// Equivalent to `table.pack(...)`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if `n` is negative, there are not enough values on the stack, or the value at index
|
/// Panics if `n` is negative, there are not enough values on the stack, or the value at index
|
||||||
/// `idx` is not a table.
|
/// `idx` is not a table.
|
||||||
@ -901,7 +899,7 @@ impl Stack {
|
|||||||
///
|
///
|
||||||
/// Equivalent to `table.unpack(list, i, j)`.
|
/// Equivalent to `table.unpack(list, i, j)`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if the value at index `idx` is not a table.
|
/// Panics if the value at index `idx` is not a table.
|
||||||
pub fn unpack(&mut self, idx: c_int, i: c_int, j: Option<c_int>) -> c_int {
|
pub fn unpack(&mut self, idx: c_int, i: c_int, j: Option<c_int>) -> c_int {
|
||||||
@ -932,21 +930,6 @@ impl Stack {
|
|||||||
n
|
n
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes the result of a Lua `require(...)` call onto the stack.
|
|
||||||
///
|
|
||||||
/// Any return values from the library `name` are pushed. Lua libraries are allowed to return
|
|
||||||
/// multiple values. If `nret` is not [`LUA_MULTRET`], then the number of return values pushed
|
|
||||||
/// will be exactly `nret`, filling with nils if necessary. The number values pushed to the
|
|
||||||
/// stack is returned.
|
|
||||||
///
|
|
||||||
/// Equivalent to `require(name)`.
|
|
||||||
pub fn require(&mut self, name: impl AsRef<[u8]>, nret: c_int) -> Result<c_int> {
|
|
||||||
self.push("require");
|
|
||||||
self.get(LUA_GLOBALSINDEX);
|
|
||||||
self.push(name.as_ref());
|
|
||||||
self.call(1, nret)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pushes the given chunk as a function at the top of the stack.
|
/// Pushes the given chunk as a function at the top of the stack.
|
||||||
///
|
///
|
||||||
/// Equivalent to [`lua_loadx`].
|
/// Equivalent to [`lua_loadx`].
|
||||||
@ -1007,7 +990,7 @@ impl Stack {
|
|||||||
///
|
///
|
||||||
/// Equivalent to `string.dump(f, mode)`.
|
/// Equivalent to `string.dump(f, mode)`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if the value at index `idx` is not a function.
|
/// Panics if the value at index `idx` is not a function.
|
||||||
pub fn dump(&self, idx: c_int, mode: DumpMode) -> Result<BString> {
|
pub fn dump(&self, idx: c_int, mode: DumpMode) -> Result<BString> {
|
||||||
@ -1037,7 +1020,7 @@ impl Stack {
|
|||||||
/// Equivalent to calling [`load`](Self::load) on the chunk and then [`call`](Self::call) on the
|
/// Equivalent to calling [`load`](Self::load) on the chunk and then [`call`](Self::call) on the
|
||||||
/// loaded function.
|
/// loaded function.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if there are not enough values on the stack or thread status is invalid.
|
/// Panics if there are not enough values on the stack or thread status is invalid.
|
||||||
pub fn eval(&mut self, chunk: &Chunk, narg: c_int, nret: c_int) -> Result<c_int> {
|
pub fn eval(&mut self, chunk: &Chunk, narg: c_int, nret: c_int) -> Result<c_int> {
|
||||||
@ -1056,14 +1039,14 @@ impl Stack {
|
|||||||
/// the index `top - narg` (i.e. the function is pushed first and then `narg` values as
|
/// the index `top - narg` (i.e. the function is pushed first and then `narg` values as
|
||||||
/// arguments). All arguments and the function are popped from the stack and then any return
|
/// arguments). All arguments and the function are popped from the stack and then any return
|
||||||
/// values are pushed. If `nret` is not [`LUA_MULTRET`], then the number of return values pushed
|
/// values are pushed. If `nret` is not [`LUA_MULTRET`], then the number of return values pushed
|
||||||
/// will be exactly `nret`, filling with nils if necessary. Finally, the number of values pushed
|
/// will be exactly `nret`, filling with nils if necessary. Finally, the number of returned
|
||||||
/// to the stack is returned.
|
/// values pushed to the stack is returned.
|
||||||
///
|
///
|
||||||
/// The current thread status must not be suspended or dead.
|
/// The current thread status must not be suspended or dead.
|
||||||
///
|
///
|
||||||
/// Equivalent to [`lua_pcall`].
|
/// Equivalent to [`lua_pcall`].
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if there are not enough values on the stack, the function to call is not on the
|
/// Panics if there are not enough values on the stack, the function to call is not on the
|
||||||
/// stack, or thread status is invalid.
|
/// stack, or thread status is invalid.
|
||||||
@ -1113,8 +1096,8 @@ impl Stack {
|
|||||||
/// the index `top - narg` (i.e. the function is pushed first and then `narg` values as
|
/// the index `top - narg` (i.e. the function is pushed first and then `narg` values as
|
||||||
/// arguments). All arguments and the function are popped from the stack and then any return
|
/// arguments). All arguments and the function are popped from the stack and then any return
|
||||||
/// values are pushed. If `nret` is not [`LUA_MULTRET`], then the number of return values pushed
|
/// values are pushed. If `nret` is not [`LUA_MULTRET`], then the number of return values pushed
|
||||||
/// will be exactly `nret`, filling with nils if necessary. Finally, the number values pushed to
|
/// will be exactly `nret`, filling with nils if necessary. Finally, the number of returned
|
||||||
/// the stack is returned.
|
/// values pushed to the stack is returned.
|
||||||
///
|
///
|
||||||
/// If the thread yields a Rust [`Future`] value, then it will be polled to completion before
|
/// If the thread yields a Rust [`Future`] value, then it will be polled to completion before
|
||||||
/// the thread is resumed with the output of the [`Future`] as the argument. If the thread
|
/// the thread is resumed with the output of the [`Future`] as the argument. If the thread
|
||||||
@ -1125,7 +1108,7 @@ impl Stack {
|
|||||||
/// Equivalent to multiple calls to [`lua_resume`] until the thread completes with a normal
|
/// Equivalent to multiple calls to [`lua_resume`] until the thread completes with a normal
|
||||||
/// result.
|
/// result.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if there are not enough values on the stack, the function to call is not on the
|
/// Panics if there are not enough values on the stack, the function to call is not on the
|
||||||
/// stack, or thread status is invalid.
|
/// stack, or thread status is invalid.
|
||||||
@ -1189,7 +1172,7 @@ impl Stack {
|
|||||||
///
|
///
|
||||||
/// Equivalent to [`lua_resume`].
|
/// Equivalent to [`lua_resume`].
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panic
|
||||||
///
|
///
|
||||||
/// Panics if there are not enough values on the stack, the function to call is not on the
|
/// Panics if there are not enough values on the stack, the function to call is not on the
|
||||||
/// stack, or thread status is invalid.
|
/// stack, or thread status is invalid.
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
pub use lb::fs;
|
|
||||||
pub use lb::net;
|
|
171
src/main.rs
171
src/main.rs
@ -1,8 +1,9 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use lb_core::{GlobalState, PrettyError};
|
||||||
use mimalloc::MiMalloc;
|
use mimalloc::MiMalloc;
|
||||||
use owo_colors::OwoColorize;
|
use owo_colors::OwoColorize;
|
||||||
use std::{backtrace::Backtrace, fmt::Display, net::SocketAddr, num::NonZero, panic, thread};
|
use std::{backtrace::Backtrace, net::SocketAddr, num::NonZero, panic, thread};
|
||||||
use sysexits::ExitCode;
|
use tokio::{runtime, task::LocalSet};
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static GLOBAL: MiMalloc = MiMalloc;
|
static GLOBAL: MiMalloc = MiMalloc;
|
||||||
@ -20,18 +21,13 @@ fn panic_cb(panic: &panic::PanicHookInfo) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
eprint!(
|
eprint!(
|
||||||
"{}:\n{trace}",
|
|
||||||
format_args!(
|
|
||||||
"thread '{}' panicked at {location}: {msg}",
|
|
||||||
thread::current().name().unwrap_or("<unnamed>")
|
|
||||||
)
|
|
||||||
.red()
|
|
||||||
);
|
|
||||||
|
|
||||||
eprintln!(
|
|
||||||
"{}",
|
"{}",
|
||||||
"This is a bug in luby. Please kindly report this at https://git.lua.re/luaneko/luby."
|
PrettyError::new(msg)
|
||||||
.yellow()
|
.with_trace(trace)
|
||||||
|
.prepend(format_args!(
|
||||||
|
"thread '{}' panicked at {location}",
|
||||||
|
thread::current().name().unwrap_or("<unnamed>")
|
||||||
|
))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,29 +35,25 @@ fn panic_cb(panic: &panic::PanicHookInfo) {
|
|||||||
struct Args {
|
struct Args {
|
||||||
/// Paths to scripts to execute.
|
/// Paths to scripts to execute.
|
||||||
#[clap(value_name = "SCRIPTS")]
|
#[clap(value_name = "SCRIPTS")]
|
||||||
path: Vec<String>,
|
paths: Vec<String>,
|
||||||
|
|
||||||
/// Strings to execute.
|
/// Strings to execute.
|
||||||
#[clap(long, short = 'e', value_name = "CHUNK")]
|
#[clap(long, short = 'e', value_name = "CHUNK")]
|
||||||
eval: Vec<String>,
|
evals: Vec<String>,
|
||||||
|
|
||||||
/// Libraries to require on startup.
|
/// Libraries to require on startup.
|
||||||
#[clap(long, short = 'l', value_name = "NAME")]
|
#[clap(long, short = 'l', value_name = "NAME")]
|
||||||
lib: Vec<String>,
|
libs: Vec<String>,
|
||||||
|
|
||||||
/// Console log level.
|
/// Console log level.
|
||||||
#[clap(long, value_name = "LEVEL", default_value = "debug")]
|
#[clap(long, value_name = "LEVEL", default_value = "debug")]
|
||||||
log: tracing::Level,
|
log_level: tracing::Level,
|
||||||
|
|
||||||
/// LuaJIT control commands.
|
/// Number of runtime worker threads.
|
||||||
#[clap(long, short = 'j', value_name = "CMD=FLAGS")]
|
|
||||||
jit: Vec<String>,
|
|
||||||
|
|
||||||
/// Number of tokio worker threads.
|
|
||||||
#[clap(long, value_name = "THREADS", default_value_t = Self::threads())]
|
#[clap(long, value_name = "THREADS", default_value_t = Self::threads())]
|
||||||
threads: NonZero<usize>,
|
threads: NonZero<usize>,
|
||||||
|
|
||||||
/// Number of tokio blocking threads.
|
/// Number of runtime blocking threads.
|
||||||
#[clap(long, value_name = "THREADS", default_value_t = Self::blocking_threads())]
|
#[clap(long, value_name = "THREADS", default_value_t = Self::blocking_threads())]
|
||||||
blocking_threads: NonZero<usize>,
|
blocking_threads: NonZero<usize>,
|
||||||
|
|
||||||
@ -70,12 +62,7 @@ struct Args {
|
|||||||
enable_console: bool,
|
enable_console: bool,
|
||||||
|
|
||||||
/// tokio-console publish address.
|
/// tokio-console publish address.
|
||||||
#[clap(
|
#[clap(long, value_name = "ADDRESS", default_value = "127.0.0.1:6669")]
|
||||||
long,
|
|
||||||
value_name = "ADDRESS",
|
|
||||||
default_value = "127.0.0.1:6669",
|
|
||||||
requires = "enable_console"
|
|
||||||
)]
|
|
||||||
console_addr: SocketAddr,
|
console_addr: SocketAddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,38 +76,28 @@ impl Args {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exit_err<T, E: Display>(code: ExitCode) -> impl FnOnce(E) -> T {
|
fn main() {
|
||||||
move |err| {
|
|
||||||
eprintln!("{}", err.red());
|
|
||||||
code.exit()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), ExitCode> {
|
|
||||||
panic::set_hook(Box::new(panic_cb));
|
panic::set_hook(Box::new(panic_cb));
|
||||||
|
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
init_logger(&args);
|
init_logger(&args);
|
||||||
|
let runtime = init_runtime(&args);
|
||||||
|
GlobalState::set(init_vm(&args));
|
||||||
|
|
||||||
let tokio = init_tokio(&args);
|
let main = LocalSet::new();
|
||||||
let lua = init_lua(&args);
|
main.spawn_local(run(args));
|
||||||
let main = lua.spawn(async |s| main_async(args, s).await);
|
runtime.block_on(main);
|
||||||
|
|
||||||
tokio.block_on(async {
|
|
||||||
lua.await;
|
|
||||||
main.await.unwrap()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_logger(args: &Args) {
|
fn init_logger(args: &Args) {
|
||||||
use tracing::level_filters::LevelFilter;
|
use tracing::level_filters::LevelFilter;
|
||||||
use tracing_subscriber::{Layer, util::*};
|
use tracing_subscriber::{Layer, util::*};
|
||||||
|
|
||||||
let log = tracing_subscriber::fmt()
|
let console = tracing_subscriber::fmt()
|
||||||
.compact()
|
.compact()
|
||||||
.with_env_filter(
|
.with_env_filter(
|
||||||
tracing_subscriber::EnvFilter::builder()
|
tracing_subscriber::EnvFilter::builder()
|
||||||
.with_default_directive(LevelFilter::from(args.log).into())
|
.with_default_directive(LevelFilter::from(args.log_level).into())
|
||||||
.from_env_lossy(),
|
.from_env_lossy(),
|
||||||
)
|
)
|
||||||
.with_file(false)
|
.with_file(false)
|
||||||
@ -133,93 +110,59 @@ fn init_logger(args: &Args) {
|
|||||||
.with_default_env()
|
.with_default_env()
|
||||||
.server_addr(args.console_addr)
|
.server_addr(args.console_addr)
|
||||||
.spawn()
|
.spawn()
|
||||||
.with_subscriber(log)
|
.with_subscriber(console)
|
||||||
.init()
|
.init()
|
||||||
} else {
|
} else {
|
||||||
log.init()
|
console.init()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_tokio(args: &Args) -> tokio::runtime::Runtime {
|
fn init_runtime(args: &Args) -> runtime::Runtime {
|
||||||
let mut rt = match args.threads.get() {
|
if args.threads.get() == 1 {
|
||||||
1 => tokio::runtime::Builder::new_current_thread(),
|
runtime::Builder::new_current_thread()
|
||||||
n => {
|
} else {
|
||||||
let mut rt = tokio::runtime::Builder::new_multi_thread();
|
runtime::Builder::new_multi_thread()
|
||||||
rt.worker_threads(n - 1);
|
|
||||||
rt
|
|
||||||
}
|
}
|
||||||
};
|
.enable_all()
|
||||||
|
.thread_name("lb")
|
||||||
rt.enable_all()
|
.worker_threads(args.threads.get() - 1)
|
||||||
.thread_name("luby")
|
|
||||||
.max_blocking_threads(args.blocking_threads.get())
|
.max_blocking_threads(args.blocking_threads.get())
|
||||||
.build()
|
.build()
|
||||||
.unwrap_or_else(exit_err(ExitCode::OsErr))
|
.unwrap_or_else(|err| panic!("failed to initialise runtime: {err}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_lua(args: &Args) -> lb::runtime::Runtime {
|
fn init_vm(_args: &Args) -> luajit::State {
|
||||||
let rt = lb::runtime::Builder::new();
|
let mut state =
|
||||||
let mut rt = rt.build().unwrap_or_else(exit_err(ExitCode::Software));
|
luajit::State::new().unwrap_or_else(|err| panic!("failed to initialise runtime: {err}"));
|
||||||
|
|
||||||
for arg in args.jit.iter() {
|
let mut registry = luaffi::Registry::new();
|
||||||
let mut s = rt.guard();
|
registry.preload::<lb_core::lb_core>("lb:core");
|
||||||
if let Some((cmd, flags)) = parse_jitlib_cmd(arg)
|
|
||||||
&& let Ok(_) = s.require(format!("jit.{cmd}"), 1)
|
|
||||||
{
|
|
||||||
(s.push("start"), s.get(-2), s.push(flags));
|
|
||||||
s.call(1, 0)
|
|
||||||
} else {
|
|
||||||
s.require("jit", 1).unwrap();
|
|
||||||
match arg.as_str() {
|
|
||||||
cmd @ ("on" | "off" | "flush") => {
|
|
||||||
(s.push(cmd), s.get(-2));
|
|
||||||
s.call(0, 0)
|
|
||||||
}
|
|
||||||
arg => {
|
|
||||||
(s.push("opt"), s.get(-2));
|
|
||||||
(s.push("start"), s.get(-2), s.push(arg));
|
|
||||||
s.call(1, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.unwrap_or_else(exit_err(ExitCode::Usage));
|
|
||||||
}
|
|
||||||
|
|
||||||
rt
|
println!("{registry}");
|
||||||
|
|
||||||
|
state
|
||||||
|
.load(&luajit::Chunk::new(registry.done()).name("@[luby]"))
|
||||||
|
.and_then(|()| state.call(0, 0))
|
||||||
|
.unwrap_or_else(|err| panic!("failed to load modules: {err}"));
|
||||||
|
|
||||||
|
state
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_jitlib_cmd(s: &str) -> Option<(&str, &str)> {
|
async fn run(args: Args) {
|
||||||
match s {
|
let mut state = GlobalState::new_thread();
|
||||||
"p" => Some(("p", "Flspv10")),
|
for ref path in args.paths {
|
||||||
"v" => Some(("v", "-")),
|
|
||||||
"dump" => Some(("dump", "tirs")),
|
|
||||||
_ => s.split_once('='),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn main_async(args: Args, state: &mut luajit::State) -> Result<(), ExitCode> {
|
|
||||||
for ref path in args.path {
|
|
||||||
let mut s = state.guard();
|
|
||||||
let chunk = match std::fs::read(path) {
|
let chunk = match std::fs::read(path) {
|
||||||
Ok(chunk) => chunk,
|
Ok(chunk) => chunk,
|
||||||
Err(err) => {
|
Err(err) => return eprintln!("{}", format!("{path}: {err}").red()),
|
||||||
eprintln!("{}", format_args!("{path}: {err}").red());
|
|
||||||
ExitCode::NoInput.exit();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
s.load(&luajit::Chunk::new(chunk).path(path))
|
if let Err(err) = state.load(&luajit::Chunk::new(chunk).path(path)) {
|
||||||
.unwrap_or_else(exit_err(ExitCode::NoInput));
|
return eprintln!("{}", err.red());
|
||||||
|
|
||||||
if let Err(err) = s.call_async(0, 0).await {
|
|
||||||
match err.trace() {
|
|
||||||
Some(trace) => eprintln!("{}\n{trace}", err.red()), // runtime error
|
|
||||||
None => eprintln!("{}", err.red()),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ExitCode::DataErr.exit();
|
match state.call_async(0, 0).await {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) => GlobalState::uncaught_error(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user