Compare commits
22 Commits
06d3eebaa1
...
2352ba66d4
Author | SHA1 | Date | |
---|---|---|---|
2352ba66d4 | |||
c39106b790 | |||
173149f2f7 | |||
e801ee468b | |||
7c5ea599c8 | |||
ba6cb10a1a | |||
300b4539bd | |||
9f76dff977 | |||
fae1c6e162 | |||
68f6e48043 | |||
066b4e7564 | |||
c9026123e6 | |||
c3fb3407c4 | |||
884acd71e1 | |||
e08ff38803 | |||
40478fb7de | |||
0fd59f6874 | |||
0667f79ff5 | |||
5be3f2970c | |||
728ee58e0d | |||
1b8c461d7e | |||
ab14e5f28d |
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
|
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
|
||||||
"runtime.version": "LuaJIT",
|
"runtime.version": "LuaJIT",
|
||||||
"diagnostics.disable": ["redefined-local"]
|
"diagnostics.disable": ["redefined-local", "lowercase-global"]
|
||||||
}
|
}
|
||||||
|
50
Cargo.lock
generated
50
Cargo.lock
generated
@ -210,7 +210,7 @@ dependencies = [
|
|||||||
"bitflags",
|
"bitflags",
|
||||||
"cexpr",
|
"cexpr",
|
||||||
"clang-sys",
|
"clang-sys",
|
||||||
"itertools",
|
"itertools 0.13.0",
|
||||||
"log",
|
"log",
|
||||||
"prettyplease",
|
"prettyplease",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -449,12 +449,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.12"
|
version = "0.3.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
|
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -733,6 +733,15 @@ dependencies = [
|
|||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
@ -746,12 +755,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lb_core"
|
name = "lb"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"luaffi",
|
"luaffi",
|
||||||
|
"luaify",
|
||||||
"luajit",
|
"luajit",
|
||||||
"owo-colors",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -812,7 +821,6 @@ dependencies = [
|
|||||||
"luaify",
|
"luaify",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"simdutf8",
|
"simdutf8",
|
||||||
"static_assertions",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -860,8 +868,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"console-subscriber",
|
"console-subscriber",
|
||||||
"lb_core",
|
"lb",
|
||||||
"luaffi",
|
|
||||||
"luajit",
|
"luajit",
|
||||||
"mimalloc",
|
"mimalloc",
|
||||||
"owo-colors",
|
"owo-colors",
|
||||||
@ -1066,9 +1073,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prettyplease"
|
name = "prettyplease"
|
||||||
version = "0.2.34"
|
version = "0.2.35"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55"
|
checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"syn",
|
"syn",
|
||||||
@ -1100,7 +1107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
|
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"itertools",
|
"itertools 0.14.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
@ -1334,12 +1341,6 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "static_assertions"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
@ -1348,9 +1349,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.103"
|
version = "2.0.104"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
|
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1673,6 +1674,15 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.60.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.53.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.6"
|
version = "0.52.6"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"crates/lb_core",
|
"crates/lb",
|
||||||
"crates/luaffi",
|
"crates/luaffi",
|
||||||
"crates/luaffi_impl",
|
"crates/luaffi_impl",
|
||||||
"crates/luaify",
|
"crates/luaify",
|
||||||
@ -20,8 +20,7 @@ edition = "2024"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.40", features = ["derive"] }
|
clap = { version = "4.5.40", features = ["derive"] }
|
||||||
console-subscriber = "0.4.1"
|
console-subscriber = "0.4.1"
|
||||||
lb_core = { version = "0.1.0", path = "crates/lb_core" }
|
lb = { version = "0.1.0", path = "crates/lb" }
|
||||||
luaffi = { version = "0.1.0", path = "crates/luaffi" }
|
|
||||||
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"
|
||||||
|
10
bacon.toml
Normal file
10
bacon.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[jobs.test]
|
||||||
|
command = ["cargo", "test", "--all"]
|
||||||
|
need_stdout = true
|
||||||
|
|
||||||
|
[jobs.doc]
|
||||||
|
command = ["cargo", "doc", "--all", "--no-deps"]
|
||||||
|
|
||||||
|
[jobs.doc-open]
|
||||||
|
command = ["cargo", "doc", "--all", "--no-deps", "--open"]
|
||||||
|
on_success = "back"
|
@ -1,10 +1,13 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "lb_core"
|
name = "lb"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
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" }
|
||||||
owo-colors = "4.2.1"
|
tokio = { version = "1.45.1", features = ["rt", "time", "fs"] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
luaify = { path = "../luaify" }
|
||||||
tokio = { version = "1.45.1", features = ["full"] }
|
tokio = { version = "1.45.1", features = ["full"] }
|
2
crates/lb/src/lib.rs
Normal file
2
crates/lb/src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod rt;
|
||||||
|
pub mod task;
|
@ -1,91 +0,0 @@
|
|||||||
use luaffi::{cdef, metatype};
|
|
||||||
use owo_colors::OwoColorize;
|
|
||||||
use std::{cell::RefCell, fmt, process};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct GlobalState(luajit::State);
|
|
||||||
|
|
||||||
impl GlobalState {
|
|
||||||
thread_local! {
|
|
||||||
static STATE: RefCell<Option<GlobalState>> = RefCell::new(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set(state: luajit::State) -> Option<luajit::State> {
|
|
||||||
Self::STATE.with_borrow_mut(|s| s.replace(Self(state)).map(|s| s.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_current<T>(f: impl FnOnce(&luajit::State) -> T) -> T {
|
|
||||||
Self::STATE.with_borrow(|s| f(&s.as_ref().expect("lua state not initialised").0))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_current_mut<T>(f: impl FnOnce(&mut luajit::State) -> T) -> T {
|
|
||||||
Self::STATE.with_borrow_mut(|s| f(&mut s.as_mut().expect("lua state not initialised").0))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_thread() -> luajit::State {
|
|
||||||
Self::with_current(|s| s.new_thread())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn uncaught_error(err: luajit::Error) {
|
|
||||||
let mut err = PrettyError::from(err);
|
|
||||||
if let Some(task) = tokio::task::try_id() {
|
|
||||||
err.prepend(format_args!("uncaught error in task {task}"));
|
|
||||||
}
|
|
||||||
|
|
||||||
eprintln!("{err}");
|
|
||||||
process::abort()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct PrettyError {
|
|
||||||
msg: String,
|
|
||||||
trace: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrettyError {
|
|
||||||
pub fn new(msg: impl fmt::Display) -> Self {
|
|
||||||
Self {
|
|
||||||
msg: format!("{msg}"),
|
|
||||||
trace: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_trace(mut self, trace: impl fmt::Display) -> Self {
|
|
||||||
self.trace = Some(format!("{trace}"));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prepend(&mut self, msg: impl fmt::Display) -> &mut Self {
|
|
||||||
if self.msg.is_empty() {
|
|
||||||
self.msg = format!("{msg}");
|
|
||||||
} else {
|
|
||||||
self.msg = format!("{msg}:\n{}", self.msg);
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<luajit::Error> for PrettyError {
|
|
||||||
fn from(value: luajit::Error) -> Self {
|
|
||||||
match value {
|
|
||||||
luajit::Error::Resume { msg, trace } => Self::new(msg).with_trace(trace),
|
|
||||||
err => Self::new(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for PrettyError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
match self.trace {
|
|
||||||
Some(ref trace) => write!(f, "{}\n{trace}", self.msg.red()),
|
|
||||||
None => write!(f, "{}", self.msg.red()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cdef]
|
|
||||||
pub struct lb_core;
|
|
||||||
|
|
||||||
#[metatype]
|
|
||||||
impl lb_core {}
|
|
@ -9,4 +9,3 @@ luaffi_impl = { version = "0.1.0", path = "../luaffi_impl" }
|
|||||||
luaify = { version = "0.1.0", path = "../luaify" }
|
luaify = { version = "0.1.0", path = "../luaify" }
|
||||||
rustc-hash = "2.1.1"
|
rustc-hash = "2.1.1"
|
||||||
simdutf8 = "0.1.5"
|
simdutf8 = "0.1.5"
|
||||||
static_assertions = "1.1.0"
|
|
||||||
|
@ -1,30 +1,47 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
__internal::{display, type_id},
|
__internal::{display, type_id},
|
||||||
CDef, CDefBuilder, Metatype, MetatypeBuilder, ToFfi, Type, TypeBuilder,
|
Cdef, CdefBuilder, FfiReturnConvention, Metatype, MetatypeBuilder, ToFfi, Type, TypeBuilder,
|
||||||
|
UnsafeExternCFn,
|
||||||
};
|
};
|
||||||
use luaify::luaify;
|
use luaify::luaify;
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
|
marker::PhantomPinned,
|
||||||
mem,
|
mem,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
ptr,
|
ptr,
|
||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// guardrail bytes prepended to all lua_future values in the header part to hopefully try to prevent
|
||||||
|
// misinterpreting other kinds of cdata as a lua_future if the lua user code yields a cdata that is
|
||||||
|
// not a lua_future for some odd reason.
|
||||||
|
type Signature = u64;
|
||||||
|
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: ToFfi>> {
|
pub struct lua_future<F: Future<Output: ToFfi>> {
|
||||||
//
|
//
|
||||||
// SAFETY: .poll MUST be the first field. It is only to be called by the Rust async runtime to
|
// SAFETY: LuaJIT guarantees that cdata payloads, which are GC-managed, are never relocated
|
||||||
// advance the future without knowing its type (see `lua_pollable` below).
|
// (i.e. pinned). We can safely assume that we are pinned and poll the future inside this
|
||||||
|
// wrapper. We use this to our advantage by storing the future value directly inside the cdata
|
||||||
|
// payload instead of boxing the future and introducing indirection.
|
||||||
|
//
|
||||||
|
// https://github.com/LuaJIT/LuaJIT/issues/1167#issuecomment-1968047229
|
||||||
|
//
|
||||||
|
// .sig and .poll fields MUST come first. .poll is only to be called by the Rust async runtime
|
||||||
|
// to advance the future without knowing its type (see `lua_pollable` below).
|
||||||
//
|
//
|
||||||
// This assumes that the crate containing the async runtime and the crate containing the future
|
// This assumes that the crate containing the async runtime and the crate containing the future
|
||||||
// type are ABI-compatible (compiled by the same compiler with the same target into the same binary).
|
// type are ABI-compatible (compiled by the same compiler with the same target into the same
|
||||||
// This is always the case for luby because all modules are statically linked into one binary.
|
// binary). This is always the case for luby because all modules are statically linked into one
|
||||||
|
// binary.
|
||||||
//
|
//
|
||||||
// .poll and .state are opaque to Lua itself.
|
// only .take and .drop are visible to Lua itself; other fields are opaque.
|
||||||
//
|
//
|
||||||
poll: fn(&mut Self, cx: &mut Context) -> Poll<()>,
|
sig: Signature,
|
||||||
|
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 ToFfi>::To,
|
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),
|
||||||
@ -34,15 +51,17 @@ pub struct lua_future<F: Future<Output: ToFfi>> {
|
|||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
pub struct lua_pollable {
|
pub struct lua_pollable {
|
||||||
//
|
//
|
||||||
// SAFETY: The only way to obtain a reference to a `lua_pollable` is by returning a `lua_future<T>`
|
// SAFETY: The only way to obtain a reference to a `lua_pollable` is by returning a
|
||||||
// from Rust to Lua, which LuaJIT boxes into cdata and `coroutine.yield`'s back to Rust, then
|
// `lua_future<T>` from Rust to Lua, which LuaJIT boxes into cdata and `coroutine.yield`'s back
|
||||||
// casting the yielded pointer value to `*mut lua_pollable`.
|
// to Rust, then casting the yielded pointer value to `*mut lua_pollable`.
|
||||||
//
|
//
|
||||||
// This is the type-erased "header" part of a `lua_future<T>` which allows the async runtime to
|
// This is the type-erased "header" part of a `lua_future<T>` which allows the async runtime to
|
||||||
// poll the future without knowing its concrete type (essentially dynamic dispatch). It has the
|
// poll the future without knowing its concrete type (essentially dynamic dispatch). It has the
|
||||||
// same layout as `lua_future<T>` without the state part.
|
// same layout as `lua_future<T>` without the state part.
|
||||||
//
|
//
|
||||||
poll: fn(&mut Self, cx: &mut Context) -> Poll<()>,
|
sig: Signature,
|
||||||
|
poll: fn(Pin<&mut Self>, cx: &mut Context) -> Poll<()>,
|
||||||
|
_phantom: PhantomPinned,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum State<F: Future> {
|
enum State<F: Future> {
|
||||||
@ -54,6 +73,7 @@ enum State<F: Future> {
|
|||||||
impl<F: Future<Output: ToFfi>> 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,
|
||||||
poll: Self::poll,
|
poll: Self::poll,
|
||||||
state: State::Pending(fut),
|
state: State::Pending(fut),
|
||||||
take: Self::take,
|
take: Self::take,
|
||||||
@ -61,39 +81,29 @@ impl<F: Future<Output: ToFfi>> lua_future<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll(&mut self, cx: &mut Context) -> Poll<()> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
|
||||||
//
|
// SAFETY: we do not ever move the future here, so this is safe
|
||||||
// SAFETY: LuaJIT guarantees that cdata payloads, which are GC-managed, are never
|
let this = unsafe { Pin::into_inner_unchecked(self) };
|
||||||
// relocated (i.e. pinned). We can safely assume that we are pinned and poll the future.
|
match this.state {
|
||||||
//
|
|
||||||
// We use this to our advantage by storing the future value directly inside the cdata
|
|
||||||
// payload instead of boxing the future and introducing indirection.
|
|
||||||
//
|
|
||||||
// https://github.com/LuaJIT/LuaJIT/issues/1167#issuecomment-1968047229
|
|
||||||
//
|
|
||||||
match self.state {
|
|
||||||
State::Pending(ref mut fut) => match unsafe { Pin::new_unchecked(fut) }.poll(cx) {
|
State::Pending(ref mut fut) => match unsafe { Pin::new_unchecked(fut) }.poll(cx) {
|
||||||
Poll::Pending => Poll::Pending,
|
Poll::Pending => Poll::Pending,
|
||||||
Poll::Ready(value) => Poll::Ready(self.state = State::Fulfilled(value)),
|
Poll::Ready(value) => Poll::Ready(this.state = State::Fulfilled(value)), // drop the future in-place
|
||||||
},
|
},
|
||||||
State::Fulfilled(_) => Poll::Ready(()),
|
State::Fulfilled(_) => Poll::Ready(()),
|
||||||
State::Complete => unreachable!("lua_future::poll() called on completed future"),
|
State::Complete => unreachable!("lua_future::poll() called on a completed future"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn take(&mut self) -> <F::Output as ToFfi>::To {
|
unsafe extern "C" fn take(&mut self) -> <F::Output as ToFfi>::To {
|
||||||
// `fut:__take()` returns the fulfilled value by-value because it is the lowest common
|
// `fut:__take()` returns the fulfilled value by-value (not by out-param) because if we
|
||||||
// denominator for supported return conventions (all `ToFfi` impls support return by-value;
|
// preallocate a cdata for the out-param and the thread for some reason gets dropped and
|
||||||
// primitives e.g. don't support return by out-param because they get boxed in cdata).
|
// never resumed, the GC could call the destructor on an uninitialised cdata.
|
||||||
//
|
|
||||||
// Plus, if we preallocate a cdata for out-param and the thread for some reason gets dropped
|
|
||||||
// and never resumed, GC could call the destructor on an uninitialised cdata.
|
|
||||||
match self.state {
|
match self.state {
|
||||||
State::Fulfilled(_) => match mem::replace(&mut self.state, State::Complete) {
|
State::Fulfilled(_) => match mem::replace(&mut self.state, State::Complete) {
|
||||||
State::Fulfilled(value) => value.convert(),
|
State::Fulfilled(value) => value.convert(),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
State::Pending(_) => panic!("lua_future::take() called on pending future"),
|
State::Pending(_) => panic!("lua_future::take() called on a pending future"),
|
||||||
State::Complete => panic!("lua_future::take() called twice"),
|
State::Complete => panic!("lua_future::take() called twice"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -103,12 +113,21 @@ impl<F: Future<Output: ToFfi>> lua_future<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl lua_pollable {
|
||||||
|
pub fn is_valid(&self) -> bool {
|
||||||
|
// TODO: signature check can currently read out-of-bounds if lua code for some reason yields
|
||||||
|
// a cdata of size less than 8 bytes that is not a lua_future. there is no easy way to fix
|
||||||
|
// afaik this because there is no way to find the size of a cdata payload using the C API.
|
||||||
|
self.sig == SIGNATURE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Future for lua_pollable {
|
impl Future for lua_pollable {
|
||||||
type Output = ();
|
type Output = ();
|
||||||
|
|
||||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||||
// SAFETY: see comment above in `lua_future::poll()`
|
assert!(self.is_valid(), "invalid lua_pollable value");
|
||||||
(self.poll)(Pin::into_inner(self), cx)
|
(self.poll)(self, cx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,11 +145,11 @@ unsafe impl<F: Future<Output: ToFfi> + 'static> Type for lua_future<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<F: Future<Output: ToFfi> + '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))
|
s.field_opaque(mem::offset_of!(Self, take)) // opaque .sig, .poll and .state
|
||||||
.field::<unsafe extern "C" fn(*mut Self) -> <F::Output as ToFfi>::To>("__take")
|
.field::<UnsafeExternCFn<(&mut Self,), <F::Output as ToFfi>::To>>("__take")
|
||||||
.field::<unsafe extern "C" fn(*mut Self)>("__drop");
|
.field::<UnsafeExternCFn<(&mut Self,), ()>>("__drop");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +168,7 @@ unsafe impl<F: Future<Output: ToFfi> + 'static> ToFfi for lua_future<F> {
|
|||||||
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.
|
||||||
@ -160,7 +179,7 @@ unsafe impl<F: Future<Output: ToFfi> + 'static> ToFfi for lua_future<F> {
|
|||||||
// `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 ToFfi>::postlude(ret)
|
<F::Output as ToFfi>::postlude(ret, FfiReturnConvention::ByValue)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
pub use luaify::*;
|
pub use luaify::*;
|
||||||
use rustc_hash::FxHasher;
|
use rustc_hash::FxHasher;
|
||||||
pub use static_assertions::*;
|
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
fmt::{self, Display, Formatter},
|
fmt::{self, Display, Formatter},
|
||||||
@ -13,11 +12,22 @@ pub fn type_id<T: 'static>() -> u64 {
|
|||||||
hash.finish()
|
hash.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! export {
|
||||||
|
($($fn:expr),+ $(,)?) => {
|
||||||
|
// this ensures ffi function symbol exports are actually present in the resulting binary,
|
||||||
|
// otherwise they may get dead code-eliminated before it reaches the linker
|
||||||
|
#[used]
|
||||||
|
static __FFI_EXPORTS: &[fn()] = unsafe {
|
||||||
|
&[$(::std::mem::transmute($fn as *const ())),*]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! display {
|
macro_rules! display {
|
||||||
($($fmt:expr),+) => {{ crate::__internal::disp(move |f| write!(f, $($fmt),+)) }};
|
($($fmt:expr),+) => {{ crate::__internal::disp(move |f| write!(f, $($fmt),+)) }};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use display;
|
pub(crate) use {display, export};
|
||||||
|
|
||||||
pub fn disp(f: impl Fn(&mut Formatter) -> fmt::Result) -> impl Display {
|
pub fn disp(f: impl Fn(&mut Formatter) -> fmt::Result) -> impl Display {
|
||||||
struct Disp<F: Fn(&mut Formatter) -> fmt::Result>(F);
|
struct Disp<F: Fn(&mut Formatter) -> fmt::Result>(F);
|
||||||
|
24
crates/luaffi/src/lib.lua
Normal file
24
crates/luaffi/src/lib.lua
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
local LUA_REFNIL = -1 -- lib_aux.c
|
||||||
|
local FREELIST_REF = 0
|
||||||
|
|
||||||
|
local function __ref(value, t)
|
||||||
|
if value == nil then return LUA_REFNIL end
|
||||||
|
if t == nil then t = __registry end
|
||||||
|
local ref = t[FREELIST_REF]
|
||||||
|
if ref ~= nil and ref ~= 0 then
|
||||||
|
t[FREELIST_REF] = t[ref]
|
||||||
|
else
|
||||||
|
ref = #t + 1
|
||||||
|
end
|
||||||
|
t[ref] = value
|
||||||
|
return ref
|
||||||
|
end
|
||||||
|
|
||||||
|
local function __unref(ref, t)
|
||||||
|
if ref < 0 then return nil end
|
||||||
|
if t == nil then t = __registry end
|
||||||
|
local value = t[ref]
|
||||||
|
t[ref] = t[FREELIST_REF]
|
||||||
|
t[FREELIST_REF] = ref
|
||||||
|
return value
|
||||||
|
end
|
@ -1,14 +1,15 @@
|
|||||||
use crate::__internal::{disp, display, write_sep};
|
use crate::__internal::{disp, display, export, write_sep};
|
||||||
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,
|
||||||
mem, slice,
|
mem, slice,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod future;
|
pub mod future;
|
||||||
pub mod option;
|
// pub mod option;
|
||||||
pub mod string;
|
pub mod string;
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
@ -19,8 +20,8 @@ const KEEP_FN: &str = "luaffi_keep";
|
|||||||
const IS_UTF8_FN: &str = "luaffi_is_utf8";
|
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.
|
// garbage-collected until the end of the function. This shall exist until LuaJIT one day implements
|
||||||
// This shall exist until LuaJIT one day implements something like `ffi.keep(obj)`.
|
// something like `ffi.keep(obj)`.
|
||||||
//
|
//
|
||||||
// https://github.com/LuaJIT/LuaJIT/issues/1167
|
// https://github.com/LuaJIT/LuaJIT/issues/1167
|
||||||
#[unsafe(export_name = "luaffi_keep")]
|
#[unsafe(export_name = "luaffi_keep")]
|
||||||
@ -31,8 +32,9 @@ 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()
|
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)] = &[
|
||||||
// libs in global
|
|
||||||
("table", "table"),
|
("table", "table"),
|
||||||
("string", "string"),
|
("string", "string"),
|
||||||
("math", "math"),
|
("math", "math"),
|
||||||
@ -43,12 +45,12 @@ const CACHE_LIBS: &[(&str, &str)] = &[
|
|||||||
// require
|
// require
|
||||||
("bit", r#"require("bit")"#),
|
("bit", r#"require("bit")"#),
|
||||||
("ffi", r#"require("ffi")"#),
|
("ffi", r#"require("ffi")"#),
|
||||||
("new", r#"require("table.new")"#),
|
("__tnew", r#"require("table.new")"#),
|
||||||
("clear", r#"require("table.clear")"#),
|
("__tclear", r#"require("table.clear")"#),
|
||||||
];
|
];
|
||||||
|
|
||||||
// https://www.lua.org/manual/5.1/manual.html#5.1
|
// https://www.lua.org/manual/5.1/manual.html#5.1
|
||||||
const CACHE_GLOBALS: &[(&str, &str)] = &[
|
const CACHE_LOCALS: &[(&str, &str)] = &[
|
||||||
// base
|
// base
|
||||||
("assert", "assert"),
|
("assert", "assert"),
|
||||||
("error", "error"),
|
("error", "error"),
|
||||||
@ -71,55 +73,64 @@ const CACHE_GLOBALS: &[(&str, &str)] = &[
|
|||||||
("tostring", "tostring"),
|
("tostring", "tostring"),
|
||||||
("require", "require"),
|
("require", "require"),
|
||||||
// table
|
// table
|
||||||
("concat", "table.concat"),
|
("__tconcat", "table.concat"),
|
||||||
("insert", "table.insert"),
|
("__tinsert", "table.insert"),
|
||||||
("maxn", "table.maxn"),
|
("__tmaxn", "table.maxn"),
|
||||||
("remove", "table.remove"),
|
("__tremove", "table.remove"),
|
||||||
("sort", "table.sort"),
|
("__tsort", "table.sort"),
|
||||||
|
("__tpack", "table.pack"),
|
||||||
|
("__tunpack", "table.unpack"),
|
||||||
// string
|
// string
|
||||||
("strlen", "string.len"),
|
("__slen", "string.len"),
|
||||||
("format", "string.format"),
|
("__sformat", "string.format"),
|
||||||
("strsub", "string.sub"),
|
("__ssub", "string.sub"),
|
||||||
("gsub", "string.gsub"),
|
("__sgsub", "string.gsub"),
|
||||||
("gmatch", "string.gmatch"),
|
("__sgmatch", "string.gmatch"),
|
||||||
("dump", "string.dump"),
|
("__sdump", "string.dump"),
|
||||||
// math
|
// math
|
||||||
("random", "math.random"),
|
("__fmod", "math.fmod"),
|
||||||
// coroutine
|
// coroutine
|
||||||
("yield", "coroutine.yield"),
|
("__yield", "coroutine.yield"),
|
||||||
|
// package
|
||||||
|
("__preload", "package.preload"),
|
||||||
// debug
|
// debug
|
||||||
("traceback", "debug.traceback"),
|
("__traceback", "debug.traceback"),
|
||||||
|
("__registry", "debug.getregistry()"),
|
||||||
// ffi
|
// ffi
|
||||||
("C", "ffi.C"),
|
("__C", "ffi.C"),
|
||||||
("cdef", "ffi.cdef"),
|
("__ct", "{}"),
|
||||||
("typeof", "ffi.typeof"),
|
("__cdef", "ffi.cdef"),
|
||||||
("metatype", "ffi.metatype"),
|
("__new", "ffi.new"),
|
||||||
("cast", "ffi.cast"),
|
("__typeof", "ffi.typeof"),
|
||||||
("gc", "ffi.gc"),
|
("__istype", "ffi.istype"),
|
||||||
|
("__metatype", "ffi.metatype"),
|
||||||
|
("__cast", "ffi.cast"),
|
||||||
|
("__gc", "ffi.gc"),
|
||||||
|
("__sizeof", "ffi.sizeof"),
|
||||||
|
("__alignof", "ffi.alignof"),
|
||||||
|
("__intern", "ffi.string"),
|
||||||
// bit
|
// bit
|
||||||
("tobit", "bit.tobit"),
|
("__bnot", "bit.bnot"),
|
||||||
("tohex", "bit.tohex"),
|
("__band", "bit.band"),
|
||||||
("bnot", "bit.bnot"),
|
("__bor", "bit.bor"),
|
||||||
("band", "bit.band"),
|
("__bxor", "bit.bxor"),
|
||||||
("bor", "bit.bor"),
|
("__blshift", "bit.lshift"),
|
||||||
("bxor", "bit.bxor"),
|
("__brshift", "bit.rshift"),
|
||||||
("lshift", "bit.lshift"),
|
("__barshift", "bit.arshift"),
|
||||||
("rshift", "bit.rshift"),
|
("__brol", "bit.rol"),
|
||||||
("arshift", "bit.arshift"),
|
("__bror", "bit.ror"),
|
||||||
("rol", "bit.rol"),
|
("__bswap", "bit.bswap"),
|
||||||
("ror", "bit.ror"),
|
|
||||||
("bswap", "bit.bswap"),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
fn cache_local(f: &mut Formatter, list: &[(&str, &str)]) -> fmt::Result {
|
fn cache_local(f: &mut Formatter, list: &[(&str, &str)]) -> fmt::Result {
|
||||||
write!(f, "local ")?;
|
write!(f, "local ")?;
|
||||||
write_sep(f, ", ", list.iter().map(|(s, _)| s))?;
|
write_sep(f, ", ", list.iter().map(|(s, _)| s))?;
|
||||||
write!(f, "\n = ")?;
|
write!(f, " = ")?;
|
||||||
write_sep(f, ", ", list.iter().map(|(_, s)| s))?;
|
write_sep(f, ", ", list.iter().map(|(_, s)| s))?;
|
||||||
writeln!(f, ";")
|
writeln!(f, ";")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub struct Registry {
|
pub struct Registry {
|
||||||
types: HashSet<String>,
|
types: HashSet<String>,
|
||||||
funcs: HashSet<String>,
|
funcs: HashSet<String>,
|
||||||
@ -130,20 +141,15 @@ pub struct Registry {
|
|||||||
impl Registry {
|
impl Registry {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let mut s = Self::default();
|
let mut s = Self::default();
|
||||||
s.declare::<extern "C" fn(ptr: *const c_void)>(KEEP_FN);
|
s.declare::<UnsafeExternCFn<(*const c_void,), ()>>(KEEP_FN);
|
||||||
s.declare::<unsafe extern "C" fn(ptr: *const u8, len: usize) -> bool>(IS_UTF8_FN);
|
s.declare::<UnsafeExternCFn<(*const u8, usize), bool>>(IS_UTF8_FN);
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn preload<T: Type>(&mut self, _name: impl Display) -> &mut Self {
|
|
||||||
self.include::<T>();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn include<T: Type>(&mut self) -> &mut Self {
|
pub fn include<T: Type>(&mut self) -> &mut Self {
|
||||||
self.types
|
self.types
|
||||||
.insert(T::name().to_string())
|
.insert(T::name().to_string())
|
||||||
.then(|| T::build(&mut TypeBuilder::new(self)));
|
.then(|| T::build(&mut TypeBuilder::new::<T>(self)));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +157,18 @@ impl Registry {
|
|||||||
self.include::<T>()
|
self.include::<T>()
|
||||||
.funcs
|
.funcs
|
||||||
.insert(name.to_string())
|
.insert(name.to_string())
|
||||||
.then(|| writeln!(self.cdef, "{};", T::cdecl(name)).unwrap());
|
.then(|| writeln!(self.cdef, "{};", T::extern_cdecl(name)).unwrap());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn preload<T: Type>(&mut self, name: impl Display) -> &mut Self {
|
||||||
|
self.include::<T>();
|
||||||
|
writeln!(
|
||||||
|
self.lua,
|
||||||
|
r#"__preload["{name}"] = function(...) return __ct.{}(...); end;"#,
|
||||||
|
T::name()
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,10 +181,11 @@ impl Display for Registry {
|
|||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
let name = env!("CARGO_PKG_NAME");
|
let name = env!("CARGO_PKG_NAME");
|
||||||
let version = env!("CARGO_PKG_VERSION");
|
let version = env!("CARGO_PKG_VERSION");
|
||||||
writeln!(f, "-- automatically generated by {name} {version}")?;
|
writeln!(f, "--- automatically generated by {name} {version}")?;
|
||||||
cache_local(f, CACHE_LIBS)?;
|
cache_local(f, CACHE_LIBS)?;
|
||||||
cache_local(f, CACHE_GLOBALS)?;
|
cache_local(f, CACHE_LOCALS)?;
|
||||||
writeln!(f, "cdef [[{}]];", self.cdef)?;
|
writeln!(f, "{}", include_str!("./lib.lua"))?;
|
||||||
|
writeln!(f, "__cdef [[\n{}\n]];", self.cdef.trim())?;
|
||||||
write!(f, "{}", self.lua)
|
write!(f, "{}", self.lua)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,6 +193,10 @@ impl Display for Registry {
|
|||||||
pub unsafe trait Type {
|
pub unsafe trait Type {
|
||||||
fn name() -> impl Display;
|
fn name() -> impl Display;
|
||||||
fn cdecl(name: impl Display) -> impl Display;
|
fn cdecl(name: impl Display) -> impl Display;
|
||||||
|
fn extern_cdecl(name: impl Display) -> impl Display {
|
||||||
|
Self::cdecl(name)
|
||||||
|
}
|
||||||
|
|
||||||
fn build(b: &mut TypeBuilder);
|
fn build(b: &mut TypeBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +206,10 @@ pub struct TypeBuilder<'r> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> TypeBuilder<'r> {
|
impl<'r> TypeBuilder<'r> {
|
||||||
fn new(registry: &'r mut Registry) -> Self {
|
fn new<T: Type>(registry: &'r mut Registry) -> Self {
|
||||||
|
let ct = T::name();
|
||||||
|
let cdecl = T::cdecl("");
|
||||||
|
writeln!(registry.lua, r#"__ct.{ct} = __typeof("{cdecl}");"#).unwrap();
|
||||||
Self { registry }
|
Self { registry }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,9 +218,9 @@ impl<'r> TypeBuilder<'r> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cdef<T: CDef>(&mut self) -> &mut Self {
|
pub fn cdef<T: Cdef>(&mut self) -> &mut Self {
|
||||||
let mut b = CDefBuilder::new::<T>(self.registry);
|
let mut b = CdefBuilder::new::<T>(self.registry);
|
||||||
<T as CDef>::build(&mut b);
|
<T as Cdef>::build(&mut b);
|
||||||
drop(b);
|
drop(b);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -208,28 +233,20 @@ impl<'r> TypeBuilder<'r> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe trait CDef: Type {
|
pub unsafe trait Cdef: Type {
|
||||||
fn build(b: &mut CDefBuilder);
|
fn build(b: &mut CdefBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CDefBuilder<'r> {
|
pub struct CdefBuilder<'r> {
|
||||||
registry: &'r mut Registry,
|
registry: &'r mut Registry,
|
||||||
cdef: String,
|
cdef: String,
|
||||||
align: usize,
|
align: usize,
|
||||||
opaque: usize,
|
opaque: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> CDefBuilder<'r> {
|
impl<'r> CdefBuilder<'r> {
|
||||||
fn new<T: CDef>(registry: &'r mut Registry) -> Self {
|
fn new<T: Cdef>(registry: &'r mut Registry) -> Self {
|
||||||
writeln!(
|
|
||||||
registry.lua,
|
|
||||||
r#"local {} = typeof("{}");"#,
|
|
||||||
T::name(),
|
|
||||||
T::cdecl("")
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
registry,
|
registry,
|
||||||
cdef: format!("{} {{ ", T::cdecl("")),
|
cdef: format!("{} {{ ", T::cdecl("")),
|
||||||
@ -255,14 +272,14 @@ impl<'r> CDefBuilder<'r> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inner_struct(&mut self, f: impl FnOnce(&mut CDefBuilder)) -> &mut Self {
|
pub fn inner_struct(&mut self, f: impl FnOnce(&mut CdefBuilder)) -> &mut Self {
|
||||||
self.cdef.push_str("struct { ");
|
self.cdef.push_str("struct { ");
|
||||||
f(self);
|
f(self);
|
||||||
self.cdef.push_str("}; ");
|
self.cdef.push_str("}; ");
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inner_union(&mut self, f: impl FnOnce(&mut CDefBuilder)) -> &mut Self {
|
pub fn inner_union(&mut self, f: impl FnOnce(&mut CdefBuilder)) -> &mut Self {
|
||||||
self.cdef.push_str("union { ");
|
self.cdef.push_str("union { ");
|
||||||
f(self);
|
f(self);
|
||||||
self.cdef.push_str("}; ");
|
self.cdef.push_str("}; ");
|
||||||
@ -270,7 +287,7 @@ impl<'r> CDefBuilder<'r> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'r> Drop for CDefBuilder<'r> {
|
impl<'r> Drop for CdefBuilder<'r> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let Self {
|
let Self {
|
||||||
registry,
|
registry,
|
||||||
@ -285,7 +302,7 @@ impl<'r> Drop for CDefBuilder<'r> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe trait Metatype {
|
pub unsafe trait Metatype {
|
||||||
type Target: CDef;
|
type Target: Cdef;
|
||||||
fn build(b: &mut MetatypeBuilder);
|
fn build(b: &mut MetatypeBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,7 +320,7 @@ impl<'r> MetatypeBuilder<'r> {
|
|||||||
registry,
|
registry,
|
||||||
name: T::Target::name().to_string(),
|
name: T::Target::name().to_string(),
|
||||||
cdef: String::new(),
|
cdef: String::new(),
|
||||||
lua: format!(r#"do local __mt, __idx = {{}}, {{}}; __mt.__index = __idx; "#),
|
lua: r#"do local __mt, __idx = {}, {}; __mt.__index = __idx; "#.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,7 +350,7 @@ impl<'r> MetatypeBuilder<'r> {
|
|||||||
name: impl Display,
|
name: impl Display,
|
||||||
f: impl FnOnce(&mut MetatypeMethodBuilder),
|
f: impl FnOnce(&mut MetatypeMethodBuilder),
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
write!(self.lua, "__idx.{name} = ").unwrap();
|
write!(self.lua, "__mt.__{name} = ").unwrap();
|
||||||
f(&mut MetatypeMethodBuilder::new(self));
|
f(&mut MetatypeMethodBuilder::new(self));
|
||||||
write!(self.lua, "; ").unwrap();
|
write!(self.lua, "; ").unwrap();
|
||||||
self
|
self
|
||||||
@ -357,37 +374,40 @@ 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, "metatype({name}, __mt); end;").unwrap();
|
writeln!(registry.lua, r#"__metatype(__ct.{name}, __mt); end;"#).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe trait FromFfi: Sized {
|
pub unsafe trait FromFfi: Sized {
|
||||||
type From: Type + Sized;
|
type From: Type + Sized;
|
||||||
type FromValue: Type + Sized;
|
type FromArg: Type + Sized;
|
||||||
|
|
||||||
const ARG_KEEPALIVE: bool = false;
|
fn require_keepalive() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn prelude(_arg: &str) -> impl Display {
|
fn prelude(_arg: &str) -> impl Display {
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self;
|
fn convert(from: Self::From) -> Self;
|
||||||
fn convert_value(from: Self::FromValue) -> Self;
|
fn convert_arg(from: Self::FromArg) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe trait ToFfi: Sized {
|
pub unsafe trait ToFfi: Sized {
|
||||||
type To: Type + Sized;
|
type To: Type + Sized;
|
||||||
|
|
||||||
fn postlude(_ret: &str) -> impl Display {
|
fn postlude(_ret: &str, _conv: FfiReturnConvention) -> impl Display {
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert(self) -> Self::To;
|
fn convert(self) -> Self::To;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum FfiReturnConvention {
|
pub enum FfiReturnConvention {
|
||||||
Void,
|
Void,
|
||||||
|
#[default]
|
||||||
ByValue,
|
ByValue,
|
||||||
ByOutParam,
|
ByOutParam,
|
||||||
}
|
}
|
||||||
@ -413,16 +433,14 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn param<T: FromFfi>(&mut self, name: impl Display) -> &mut Self {
|
pub fn param<T: FromFfi>(&mut self, name: impl Display) -> &mut Self {
|
||||||
self.metatype.registry.include::<T::From>();
|
|
||||||
|
|
||||||
(!self.params.is_empty()).then(|| self.params.push_str(", "));
|
(!self.params.is_empty()).then(|| self.params.push_str(", "));
|
||||||
(!self.args.is_empty()).then(|| self.args.push_str(", "));
|
(!self.args.is_empty()).then(|| self.args.push_str(", "));
|
||||||
write!(self.params, "{name}").unwrap();
|
write!(self.params, "{name}").unwrap();
|
||||||
write!(self.args, "{name}").unwrap();
|
write!(self.args, "{name}").unwrap();
|
||||||
|
|
||||||
if T::ARG_KEEPALIVE {
|
if T::require_keepalive() {
|
||||||
write!(self.prelude, "local __keep_{name} = {name}; ").unwrap();
|
write!(self.prelude, "local __keep_{name} = {name}; ").unwrap();
|
||||||
write!(self.postlude, "C.{KEEP_FN}(__keep_{name}); ").unwrap();
|
write!(self.postlude, "__C.{KEEP_FN}(__keep_{name}); ").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let name = name.to_string();
|
let name = name.to_string();
|
||||||
@ -432,6 +450,10 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
|
|||||||
|
|
||||||
pub fn param_str(&mut self, name: impl Display) -> &mut Self {
|
pub fn param_str(&mut self, name: impl Display) -> &mut Self {
|
||||||
// fast-path for &str and &[u8]-like parameters
|
// fast-path for &str and &[u8]-like parameters
|
||||||
|
//
|
||||||
|
// this passes one lua `string` argument as two C `const uint8_t *ptr` and `uintptr_t len`
|
||||||
|
// arguments, bypassing the slower generic `&[u8]: FromFfi` path which constructs a
|
||||||
|
// temporary cdata to pass the string and its length in one argument
|
||||||
(!self.params.is_empty()).then(|| self.params.push_str(", "));
|
(!self.params.is_empty()).then(|| self.params.push_str(", "));
|
||||||
(!self.args.is_empty()).then(|| self.args.push_str(", "));
|
(!self.args.is_empty()).then(|| self.args.push_str(", "));
|
||||||
write!(self.params, "{name}").unwrap();
|
write!(self.params, "{name}").unwrap();
|
||||||
@ -445,7 +467,13 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call<T: ToFfi>(&mut self, func: impl Display, conv: FfiReturnConvention) {
|
pub fn param_ignored(&mut self) -> &mut Self {
|
||||||
|
(!self.params.is_empty()).then(|| self.params.push_str(", "));
|
||||||
|
write!(self.params, "_").unwrap();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call<T: ToFfi>(&mut self, func: impl Display, ret: FfiReturnConvention) {
|
||||||
let Self {
|
let Self {
|
||||||
metatype,
|
metatype,
|
||||||
params,
|
params,
|
||||||
@ -454,31 +482,29 @@ impl<'r, 'm> MetatypeMethodBuilder<'r, 'm> {
|
|||||||
postlude,
|
postlude,
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
metatype.registry.include::<T::To>();
|
|
||||||
|
|
||||||
let lua = &mut metatype.lua;
|
let lua = &mut metatype.lua;
|
||||||
write!(lua, "function({params}) {prelude}").unwrap();
|
write!(lua, "function({params}) {prelude}").unwrap();
|
||||||
|
|
||||||
match conv {
|
match ret {
|
||||||
FfiReturnConvention::Void => {
|
FfiReturnConvention::Void => {
|
||||||
write!(lua, "C.{func}({args}); {postlude}return nil; end").unwrap();
|
write!(lua, "__C.{func}({args}); {postlude}end").unwrap();
|
||||||
}
|
}
|
||||||
FfiReturnConvention::ByValue => {
|
FfiReturnConvention::ByValue => {
|
||||||
let check = T::postlude("res");
|
let check = T::postlude("__res", ret);
|
||||||
write!(
|
write!(
|
||||||
lua,
|
lua,
|
||||||
"local res = C.{func}({args}); {check}{postlude}return res; end"
|
"local __res = __C.{func}({args}); {check}{postlude}return __res; end"
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
FfiReturnConvention::ByOutParam => {
|
FfiReturnConvention::ByOutParam => {
|
||||||
let ct = T::To::name();
|
let ct = T::To::name();
|
||||||
let check = T::postlude("res");
|
let check = T::postlude("__res", ret);
|
||||||
write!(lua, "local res = {ct}(); C.{func}(res").unwrap();
|
write!(lua, "local __res = __new(__ct.{ct}); __C.{func}(__res").unwrap();
|
||||||
if !args.is_empty() {
|
if !args.is_empty() {
|
||||||
write!(lua, ", {args}").unwrap();
|
write!(lua, ", {args}").unwrap();
|
||||||
}
|
}
|
||||||
write!(lua, "); {check}{postlude}return res; end").unwrap()
|
write!(lua, "); {check}{postlude}return __res; end").unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -505,24 +531,32 @@ impl_primitive!(c_void, "void");
|
|||||||
|
|
||||||
unsafe impl ToFfi for () {
|
unsafe impl ToFfi for () {
|
||||||
//
|
//
|
||||||
// SAFETY: Unit type return maps to a C void return, which is a nil return in lua.
|
// SAFETY: Unit type return maps to a C void return, which is a nil return in lua. There is no
|
||||||
// There is no equivalent to passing a unit type as an argument in C.
|
// equivalent to passing a unit type as an argument in C. `c_void` cannot be returned from rust
|
||||||
// `c_void` cannot be returned from rust so it should return the unit type instead.
|
// so it should return the unit type instead.
|
||||||
//
|
//
|
||||||
type To = ();
|
type To = ();
|
||||||
fn convert(self) -> Self::To {}
|
fn convert(self) -> Self::To {}
|
||||||
|
|
||||||
|
fn postlude(_ret: &str, conv: FfiReturnConvention) -> impl Display {
|
||||||
|
assert!(
|
||||||
|
conv == FfiReturnConvention::Void,
|
||||||
|
"void type cannot be instantiated"
|
||||||
|
);
|
||||||
|
""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_copy_primitive {
|
macro_rules! impl_primitive_abi {
|
||||||
($rtype:ty, $ctype:expr, $ltype:expr) => {
|
($rtype:ty, $ctype:expr, $ltype:expr $(, $unwrap:expr)?) => {
|
||||||
impl_primitive!($rtype, $ctype);
|
impl_primitive!($rtype, $ctype);
|
||||||
|
|
||||||
//
|
//
|
||||||
// SAFETY: Primitive types are always copyable so we can pass and return them by value.
|
// SAFETY: Primitive types are always copyable so we can pass and return them by value.
|
||||||
//
|
//
|
||||||
unsafe impl FromFfi for $rtype {
|
unsafe impl FromFfi for $rtype {
|
||||||
type From = $rtype;
|
type From = Self;
|
||||||
type FromValue = $rtype;
|
type FromArg = Self;
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl Display {
|
fn prelude(arg: &str) -> impl Display {
|
||||||
display!(r#"assert(type({arg}) == "{0}", "{0} expected in argument '{arg}', got " .. type({arg})); "#, $ltype)
|
display!(r#"assert(type({arg}) == "{0}", "{0} expected in argument '{arg}', got " .. type({arg})); "#, $ltype)
|
||||||
@ -532,227 +566,223 @@ macro_rules! impl_copy_primitive {
|
|||||||
from
|
from
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_value(from: Self::FromValue) -> Self {
|
fn convert_arg(from: Self::FromArg) -> Self {
|
||||||
from
|
from
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl ToFfi for $rtype {
|
unsafe impl ToFfi for $rtype {
|
||||||
type To = $rtype;
|
type To = Self;
|
||||||
|
|
||||||
fn convert(self) -> Self::To {
|
fn convert(self) -> Self::To {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn postlude(ret: &str, conv: FfiReturnConvention) -> impl Display {
|
||||||
|
disp(move |f| Ok({
|
||||||
|
match conv {
|
||||||
|
FfiReturnConvention::Void => unreachable!(),
|
||||||
|
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_copy_primitive!(bool, "bool", "boolean");
|
impl_primitive_abi!(bool, "bool", "boolean", |n| display!("{n} ~= 0"));
|
||||||
impl_copy_primitive!(u8, "uint8_t", "number");
|
impl_primitive_abi!(u8, "uint8_t", "number", |n| display!("tonumber({n})"));
|
||||||
impl_copy_primitive!(u16, "uint16_t", "number");
|
impl_primitive_abi!(u16, "uint16_t", "number", |n| display!("tonumber({n})"));
|
||||||
impl_copy_primitive!(u32, "uint32_t", "number");
|
impl_primitive_abi!(u32, "uint32_t", "number", |n| display!("tonumber({n})"));
|
||||||
impl_copy_primitive!(u64, "uint64_t", "number");
|
impl_primitive_abi!(u64, "uint64_t", "number");
|
||||||
impl_copy_primitive!(usize, "uintptr_t", "number");
|
impl_primitive_abi!(usize, "uintptr_t", "number");
|
||||||
impl_copy_primitive!(i8, "int8_t", "number");
|
impl_primitive_abi!(i8, "int8_t", "number", |n| display!("tonumber({n})"));
|
||||||
impl_copy_primitive!(i16, "int16_t", "number");
|
impl_primitive_abi!(i16, "int16_t", "number", |n| display!("tonumber({n})"));
|
||||||
impl_copy_primitive!(i32, "int32_t", "number");
|
impl_primitive_abi!(i32, "int32_t", "number", |n| display!("tonumber({n})"));
|
||||||
impl_copy_primitive!(i64, "int64_t", "number");
|
impl_primitive_abi!(i64, "int64_t", "number");
|
||||||
impl_copy_primitive!(isize, "intptr_t", "number");
|
impl_primitive_abi!(isize, "intptr_t", "number");
|
||||||
impl_copy_primitive!(c_float, "float", "number");
|
impl_primitive_abi!(c_float, "float", "number", |n| display!("tonumber({n})"));
|
||||||
impl_copy_primitive!(c_double, "double", "number");
|
impl_primitive_abi!(c_double, "double", "number", |n| display!("tonumber({n})"));
|
||||||
|
|
||||||
unsafe impl<T: Type> Type for *const T {
|
macro_rules! impl_const_ptr {
|
||||||
fn name() -> impl Display {
|
($ty:ty) => {
|
||||||
display!("ptr_const_{}", T::name())
|
unsafe impl<T: Type> Type for $ty {
|
||||||
}
|
fn name() -> impl Display {
|
||||||
|
display!("const_{}_ptr", T::name())
|
||||||
|
}
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display {
|
fn cdecl(name: impl Display) -> impl Display {
|
||||||
T::cdecl(display!("const *{name}"))
|
T::cdecl(display!("const *{name}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(b: &mut TypeBuilder) {
|
fn build(b: &mut TypeBuilder) {
|
||||||
b.include::<T>();
|
b.include::<T>();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: Type> Type for *mut T {
|
impl_const_ptr!(*const T);
|
||||||
fn name() -> impl Display {
|
impl_const_ptr!(&T);
|
||||||
display!("ptr_mut_{}", T::name())
|
impl_const_ptr!(Option<&T>);
|
||||||
}
|
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display {
|
macro_rules! impl_mut_ptr {
|
||||||
T::cdecl(display!("*{name}"))
|
($ty:ty) => {
|
||||||
}
|
unsafe impl<T: Type> Type for $ty {
|
||||||
|
fn name() -> impl Display {
|
||||||
|
display!("{}_ptr", T::name())
|
||||||
|
}
|
||||||
|
|
||||||
fn build(b: &mut TypeBuilder) {
|
fn cdecl(name: impl Display) -> impl Display {
|
||||||
b.include::<T>();
|
T::cdecl(display!("*{name}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build(b: &mut TypeBuilder) {
|
||||||
|
b.include::<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_mut_ptr!(*mut T);
|
||||||
|
impl_mut_ptr!(&mut T);
|
||||||
|
impl_mut_ptr!(Option<&mut T>);
|
||||||
|
|
||||||
//
|
//
|
||||||
// SAFETY: Pass by value for pointers, which maps to a `cdata` argument in lua containing either:
|
// SAFETY: Pass by value for pointers, which maps to a `cdata` argument in lua containing either:
|
||||||
// * the pointer value itself (`T *`), or
|
|
||||||
// * the pointer to the base of the cdata payload (`T`).
|
|
||||||
//
|
//
|
||||||
unsafe impl<T: Type> FromFfi for *const T {
|
// * a reference which gets converted to a pointer (`T &` cdata), or
|
||||||
type From = *const T;
|
// * the pointer value itself (`T *` cdata), or
|
||||||
type FromValue = *const T;
|
// * the pointer to the base of the cdata payload (`T` cdata).
|
||||||
|
//
|
||||||
|
// LuaJIT will check for pointer compatibility automatically.
|
||||||
|
//
|
||||||
|
macro_rules! impl_ptr_fromabi {
|
||||||
|
($ty:ty) => {
|
||||||
|
unsafe impl<T: Type> FromFfi for $ty {
|
||||||
|
type From = Self;
|
||||||
|
type FromArg = Self;
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
from
|
from
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_value(from: Self::FromValue) -> Self {
|
fn convert_arg(from: Self::FromArg) -> Self {
|
||||||
from
|
from
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: Type> FromFfi for *mut T {
|
impl_ptr_fromabi!(*const T);
|
||||||
type From = *mut T;
|
impl_ptr_fromabi!(*mut T);
|
||||||
type FromValue = *mut T;
|
impl_ptr_fromabi!(Option<&T>);
|
||||||
|
impl_ptr_fromabi!(Option<&mut T>);
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
unsafe impl<'s, T: Type> FromFfi for &'s T {
|
||||||
from
|
type From = Option<&'s T>;
|
||||||
}
|
type FromArg = Option<&'s T>;
|
||||||
|
|
||||||
fn convert_value(from: Self::FromValue) -> Self {
|
|
||||||
from
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// SAFETY: Return by value for pointers, which maps to a `cdata` return in lua containing the pointer (`T *`).
|
|
||||||
// We also map null pointers to `nil` for convenience (otherwise it's still a cdata value containing
|
|
||||||
// a null pointer)
|
|
||||||
//
|
|
||||||
unsafe impl<T: Type> ToFfi for *const T {
|
|
||||||
type To = *const T;
|
|
||||||
|
|
||||||
fn convert(self) -> Self::To {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn postlude(ret: &str) -> impl Display {
|
|
||||||
display!("if {ret} == nil then {ret} = nil; end; ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: Type> ToFfi for *mut T {
|
|
||||||
type To = *mut T;
|
|
||||||
|
|
||||||
fn convert(self) -> Self::To {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn postlude(ret: &str) -> impl Display {
|
|
||||||
display!("if {ret} == nil then {ret} = nil; end; ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// SAFETY: No `ToFfi` for references because we can't guarantee that the returned reference converted
|
|
||||||
// to a pointer will not outlive the pointee.
|
|
||||||
//
|
|
||||||
unsafe impl<T: Type> Type for &T {
|
|
||||||
fn name() -> impl Display {
|
|
||||||
display!("ref_const_{}", T::name())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display {
|
|
||||||
T::cdecl(display!("const *{name}"))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build(b: &mut TypeBuilder) {
|
|
||||||
b.include::<T>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: Type> Type for &mut T {
|
|
||||||
fn name() -> impl Display {
|
|
||||||
display!("ref_mut_{}", T::name())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display {
|
|
||||||
T::cdecl(display!("*{name}"))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build(b: &mut TypeBuilder) {
|
|
||||||
b.include::<T>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// SAFETY: Pass by value for references, which have the same semantics as pointers (see above).
|
|
||||||
// Must ensure that the pointer is not nil before being converted to a reference.
|
|
||||||
//
|
|
||||||
unsafe impl<T: Type> FromFfi for &T {
|
|
||||||
type From = *const T;
|
|
||||||
type FromValue = *const T;
|
|
||||||
|
|
||||||
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 {
|
||||||
Self::convert_value(from)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_value(from: Self::FromValue) -> Self {
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
!from.is_null(),
|
from.is_some(),
|
||||||
"<&T>::convert() called on a null pointer when it was checked to be non-null"
|
"<&T>::convert() called on a null reference when it was checked to be non-null"
|
||||||
);
|
);
|
||||||
|
|
||||||
unsafe { &*from }
|
unsafe { from.unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_arg(from: Self::FromArg) -> Self {
|
||||||
|
FromFfi::convert(from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: Type> FromFfi for &mut T {
|
unsafe impl<'s, T: Type> FromFfi for &'s mut T {
|
||||||
//
|
//
|
||||||
// SAFETY: `FromFfi` for *mutable* references is safe because it is guaranteed that no two Rust
|
// 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).
|
// 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:
|
// i.e. The call stack will always look something like this:
|
||||||
//
|
//
|
||||||
// * Runtime (LuaJIT/Rust) -> Lua (via C) -> Rust (via FFI):
|
// * Runtime (LuaJIT/Rust) -> Lua (via C) -> Rust (via FFI): This is SAFE and the only use case
|
||||||
// This is SAFE and the only use case we support. All references (mutable or not) to Rust
|
// we support. All references (mutable or not) to Rust user objects will be dropped before
|
||||||
// user objects will be dropped before returning to Lua.
|
// returning to Lua.
|
||||||
//
|
//
|
||||||
// * Runtime (LuaJIT/Rust) -> Lua (via C) -> Rust (via FFI) -> Lua (via callback):
|
// * Runtime (LuaJIT/Rust) -> Lua (via C) -> Rust (via FFI) -> Lua (via callback): This is
|
||||||
// This is UNSAFE because we cannot prevent the Lua callback from calling back into Rust code
|
// UNSAFE because we cannot prevent the Lua callback from calling back into Rust code via
|
||||||
// via FFI which could violate exclusive borrow semantics. This is prevented by not
|
// FFI which could violate exclusive borrow semantics. This is prevented by not implementing
|
||||||
// implementing `FromFfi` for function pointers (see below).
|
// `FromFfi` for function pointers (see below).
|
||||||
//
|
//
|
||||||
type From = *mut T;
|
// The runtime does not keep any references to Rust user objects boxed in cdata (futures are
|
||||||
type FromValue = *mut T;
|
// 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 {
|
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 {
|
||||||
Self::convert_value(from)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_value(from: Self::FromValue) -> Self {
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
!from.is_null(),
|
from.is_some(),
|
||||||
"<&mut T>::convert() called on a null pointer when it was checked to be non-null"
|
"<&mut T>::convert() called on a null reference when it was checked to be non-null"
|
||||||
);
|
);
|
||||||
|
|
||||||
unsafe { &mut *from }
|
unsafe { from.unwrap_unchecked() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_arg(from: Self::FromArg) -> Self {
|
||||||
|
FromFfi::convert(from)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// SAFETY: No `FromFfi` and `ToFfi` for arrays because passing or returning them by value is not
|
// SAFETY: Return by value for pointers, which maps to a `cdata` return in lua containing the
|
||||||
// a thing in C (they are just pointers).
|
// pointer (`T *`). We also map null pointers to `nil` for convenience (otherwise it's still a cdata
|
||||||
|
// value containing a null pointer)
|
||||||
|
//
|
||||||
|
macro_rules! impl_ptr_toabi {
|
||||||
|
($ty:ty) => {
|
||||||
|
unsafe impl<T: Type> ToFfi for $ty {
|
||||||
|
type To = Self;
|
||||||
|
|
||||||
|
fn convert(self) -> Self::To {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display {
|
||||||
|
display!("if {ret} == nil then {ret} = nil; end; ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_ptr_toabi!(*const 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 `ToFfi` for arrays because passing or returning them by value is not a
|
||||||
|
// thing in C (they are just pointers).
|
||||||
//
|
//
|
||||||
// TODO: we could automatically convert them to tables and vice-versa
|
// TODO: we could automatically convert them to tables and vice-versa
|
||||||
//
|
//
|
||||||
unsafe impl<T: Type> Type for [T] {
|
unsafe impl<T: Type> Type for [T] {
|
||||||
fn name() -> impl Display {
|
fn name() -> impl Display {
|
||||||
display!("arr_{}", T::name())
|
display!("{}_arr", T::name())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display {
|
fn cdecl(name: impl Display) -> impl Display {
|
||||||
@ -766,7 +796,7 @@ unsafe impl<T: Type> Type for [T] {
|
|||||||
|
|
||||||
unsafe impl<T: Type, const N: usize> Type for [T; N] {
|
unsafe impl<T: Type, const N: usize> Type for [T; N] {
|
||||||
fn name() -> impl Display {
|
fn name() -> impl Display {
|
||||||
display!("arr{N}_{}", T::name())
|
display!("{}_arr{N}", T::name())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display {
|
fn cdecl(name: impl Display) -> impl Display {
|
||||||
@ -778,35 +808,46 @@ unsafe impl<T: Type, const N: usize> Type for [T; N] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct UnsafeExternCFn<In, Out>(PhantomData<unsafe extern "C" fn(In) -> Out>);
|
||||||
|
|
||||||
macro_rules! impl_function {
|
macro_rules! impl_function {
|
||||||
(fn($($arg:tt),*) -> $ret:tt) => {
|
(fn($($arg:tt),*) -> $ret:tt) => {
|
||||||
impl_function!((extern "C" fn($($arg),*) -> $ret), fn($($arg),*) -> $ret);
|
impl_function!(UnsafeExternCFn, fn($($arg),*) -> $ret);
|
||||||
impl_function!((unsafe extern "C" fn($($arg),*) -> $ret), fn($($arg),*) -> $ret);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
(($($type:tt)+), fn($($arg:tt),*) -> $ret:tt) => {
|
($ty:tt, fn($($arg:tt),*) -> $ret:tt) => {
|
||||||
//
|
//
|
||||||
// SAFETY: No `FromFfi` for function pointers because of borrow safety invariants (see above in `&mut T`).
|
// SAFETY: No `FromFfi` for function pointers because of borrow safety invariants (see above
|
||||||
|
// in `&mut T`).
|
||||||
//
|
//
|
||||||
// We also can't implement `ToFfi` because we can't call `FromFfi` and `ToFfi` 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 $($type)+ {
|
unsafe impl<$($arg: Type,)* $ret: Type> Type for $ty<($($arg,)*), $ret> {
|
||||||
fn name() -> impl Display {
|
fn name() -> impl Display {
|
||||||
disp(|f| {
|
disp(|f| Ok({
|
||||||
write!(f, "fn")?;
|
write!(f, "fn_{}", $ret::name())?;
|
||||||
$(write!(f, "_{}", $arg::name())?;)*
|
$(write!(f, "_{}", $arg::name())?;)*
|
||||||
write!(f, "_{}", $ret::name())
|
}))
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cdecl(name: impl Display) -> impl Display {
|
fn cdecl(name: impl Display) -> impl Display {
|
||||||
$ret::cdecl(disp(move |f| {
|
$ret::cdecl(disp(move |f| Ok({
|
||||||
let mut _n = 0;
|
let mut _n = 0;
|
||||||
write!(f, "(*{name})(")?;
|
write!(f, "(*{name})(")?;
|
||||||
$(if _n != 0 { write!(f, ", ")?; } write!(f, "{}", $arg::cdecl(""))?; _n += 1;)*
|
$(if _n != 0 { write!(f, ", ")?; } write!(f, "{}", $arg::cdecl(""))?; _n += 1;)*
|
||||||
write!(f, ")")
|
write!(f, ")")?;
|
||||||
}))
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extern_cdecl(name: impl Display) -> impl Display {
|
||||||
|
$ret::cdecl(disp(move |f| Ok({
|
||||||
|
// for top-level function declarations in cdef
|
||||||
|
let mut _n = 0;
|
||||||
|
write!(f, "{name}(")?;
|
||||||
|
$(if _n != 0 { write!(f, ", ")?; } write!(f, "{}", $arg::cdecl(""))?; _n += 1;)*
|
||||||
|
write!(f, ")")?;
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(b: &mut TypeBuilder) {
|
fn build(b: &mut TypeBuilder) {
|
||||||
@ -814,7 +855,7 @@ macro_rules! impl_function {
|
|||||||
b.include::<$ret>();
|
b.include::<$ret>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_function!(fn() -> Z);
|
impl_function!(fn() -> Z);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{CDef, CDefBuilder, FromFfi, ToFfi, Type, TypeBuilder, display};
|
use crate::{Cdef, CdefBuilder, FfiReturnConvention, FromFfi, ToFfi, Type, TypeBuilder, display};
|
||||||
use std::{ffi::c_int, fmt::Display, ptr};
|
use std::{ffi::c_int, fmt::Display, ptr};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -22,41 +22,39 @@ unsafe impl<T: Type> Type for lua_option<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: Type> CDef for lua_option<T> {
|
unsafe impl<T: Type> Cdef for lua_option<T> {
|
||||||
fn build(b: &mut CDefBuilder) {
|
fn build(b: &mut CdefBuilder) {
|
||||||
b.field::<c_int>("__tag").field::<T>("__value");
|
b.field::<c_int>("__tag").field::<T>("__value");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<T: FromFfi> FromFfi for Option<T> {
|
unsafe impl<T: FromFfi> FromFfi for Option<T> {
|
||||||
type From = *mut Self::FromValue; // pass by-ref
|
type From = lua_option<T::From>;
|
||||||
type FromValue = lua_option<T::FromValue>;
|
type FromArg = *mut Self::From; // pass by-ref
|
||||||
|
|
||||||
const ARG_KEEPALIVE: bool = T::ARG_KEEPALIVE;
|
fn require_keepalive() -> bool {
|
||||||
|
T::require_keepalive()
|
||||||
|
}
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl Display {
|
fn prelude(arg: &str) -> impl Display {
|
||||||
let ct = Self::FromValue::name();
|
let ct = Self::From::name();
|
||||||
display!(
|
display!(
|
||||||
"if {arg} == nil then {arg} = {ct}(); else {}{arg} = {ct}(1, {arg}); end; ",
|
"if {arg} == nil then {arg} = __new(__ct.{ct}); else {}{arg} = __new(__ct.{ct}, 1, {arg}); end; ",
|
||||||
T::prelude(arg)
|
T::prelude(arg)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
debug_assert!(
|
|
||||||
!from.is_null(),
|
|
||||||
"Option<T>::convert() called on a null lua_option<T>"
|
|
||||||
);
|
|
||||||
|
|
||||||
Self::convert_value(unsafe { ptr::replace(from, lua_option::None) })
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_value(from: Self::FromValue) -> Self {
|
|
||||||
match from {
|
match from {
|
||||||
lua_option::Some(value) => Some(T::convert_value(value)),
|
lua_option::Some(value) => Some(T::convert(value)),
|
||||||
lua_option::None => None,
|
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> {
|
unsafe impl<T: ToFfi> ToFfi for Option<T> {
|
||||||
@ -69,12 +67,12 @@ unsafe impl<T: ToFfi> ToFfi for Option<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn postlude(ret: &str) -> impl Display {
|
fn postlude(ret: &str, _conv: FfiReturnConvention) -> impl Display {
|
||||||
// if we don't have a value, return nil. otherwise copy out the inner value immediately,
|
// 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.
|
// forget the option cdata, then call postlude on the inner value.
|
||||||
display!(
|
display!(
|
||||||
"if {ret}.__tag == 0 then {ret} = nil; else {ret} = {ret}.__value; {}end; ",
|
"if {ret}.__tag == 0 then {ret} = nil; else {ret} = {ret}.__value; {}end; ",
|
||||||
T::postlude(ret)
|
T::postlude(ret, FfiReturnConvention::ByValue)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@ use crate::{__internal::disp, FromFfi, IS_UTF8_FN, Type};
|
|||||||
use luaffi_impl::{cdef, metatype};
|
use luaffi_impl::{cdef, metatype};
|
||||||
use std::{fmt, ptr, slice};
|
use std::{fmt, ptr, slice};
|
||||||
|
|
||||||
#[cdef]
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[cdef]
|
||||||
pub struct lua_buf {
|
pub struct lua_buf {
|
||||||
__ptr: *mut u8,
|
__ptr: *const u8,
|
||||||
__len: usize,
|
__len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -13,69 +13,64 @@ pub struct lua_buf {
|
|||||||
impl lua_buf {}
|
impl lua_buf {}
|
||||||
|
|
||||||
unsafe impl FromFfi for *const [u8] {
|
unsafe impl FromFfi for *const [u8] {
|
||||||
type From = *const Self::FromValue; // pass by-ref
|
type From = lua_buf;
|
||||||
type FromValue = lua_buf;
|
type FromArg = *const Self::From;
|
||||||
|
|
||||||
const ARG_KEEPALIVE: bool = true;
|
fn require_keepalive() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl fmt::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 = lua_buf::name();
|
let ct = Self::From::name();
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
r#"if {arg} ~= nil then 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})); "#
|
||||||
)?;
|
)?;
|
||||||
write!(f, "{arg} = {ct}({arg}, #{arg}); end; ")
|
write!(f, "{arg} = __new(__ct.{ct}, {arg}, #{arg}); end; ")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
|
ptr::slice_from_raw_parts(from.__ptr, from.__len)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_arg(from: Self::FromArg) -> Self {
|
||||||
if from.is_null() {
|
if from.is_null() {
|
||||||
ptr::slice_from_raw_parts(ptr::null(), 0)
|
ptr::slice_from_raw_parts(ptr::null(), 0)
|
||||||
} else {
|
} else {
|
||||||
// SAFETY: this is safe because lua_buf is copyable
|
Self::convert(unsafe { *from })
|
||||||
unsafe { Self::convert_value(*from) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_value(from: Self::FromValue) -> Self {
|
|
||||||
ptr::slice_from_raw_parts(from.__ptr, from.__len)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl FromFfi for &str {
|
unsafe impl FromFfi for &str {
|
||||||
type From = *const Self::FromValue; // pass by-ref
|
type From = lua_buf;
|
||||||
type FromValue = lua_buf;
|
type FromArg = *const Self::From;
|
||||||
|
|
||||||
const ARG_KEEPALIVE: bool = true;
|
fn require_keepalive() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn prelude(arg: &str) -> impl fmt::Display {
|
fn prelude(arg: &str) -> impl fmt::Display {
|
||||||
disp(move |f| {
|
disp(move |f| {
|
||||||
let ct = lua_buf::name();
|
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})); "#
|
||||||
)?;
|
)?;
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
r#"assert(C.{IS_UTF8_FN}({arg}, #{arg}), "argument '{arg}' must be a valid utf8 string"); "#
|
r#"assert(__C.{IS_UTF8_FN}({arg}, #{arg}), "argument '{arg}' must be a valid utf-8 string"); "#
|
||||||
)?;
|
)?;
|
||||||
write!(f, "{arg} = {ct}({arg}, #{arg}); ")
|
write!(f, "{arg} = __new(__ct.{ct}, {arg}, #{arg}); ")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert(from: Self::From) -> Self {
|
fn convert(from: Self::From) -> Self {
|
||||||
debug_assert!(
|
// SAFETY: we already checked that the string is nonnull and valid utf8 from the lua side
|
||||||
!from.is_null(),
|
debug_assert!(!from.__ptr.is_null());
|
||||||
"<&str>::convert() called on a null lua_buf"
|
|
||||||
);
|
|
||||||
|
|
||||||
// SAFETY: this is safe because lua_buf is copyable
|
|
||||||
unsafe { Self::convert_value(*from) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_value(from: Self::FromValue) -> Self {
|
|
||||||
let s = unsafe { slice::from_raw_parts(from.__ptr, from.__len) };
|
let s = unsafe { slice::from_raw_parts(from.__ptr, from.__len) };
|
||||||
|
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
@ -83,7 +78,11 @@ unsafe impl FromFfi for &str {
|
|||||||
"<&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"
|
||||||
);
|
);
|
||||||
|
|
||||||
// SAFETY: we already checked that the string is valid utf8 from the lua side
|
|
||||||
unsafe { std::str::from_utf8_unchecked(s) }
|
unsafe { std::str::from_utf8_unchecked(s) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn convert_arg(from: Self::FromArg) -> Self {
|
||||||
|
debug_assert!(!from.is_null());
|
||||||
|
unsafe { Self::convert(*from) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,4 +10,4 @@ proc-macro = true
|
|||||||
darling = "0.20.11"
|
darling = "0.20.11"
|
||||||
proc-macro2 = "1.0.95"
|
proc-macro2 = "1.0.95"
|
||||||
quote = "1.0.40"
|
quote = "1.0.40"
|
||||||
syn = { version = "2.0.103", features = ["full", "visit-mut"] }
|
syn = { version = "2.0.103", features = ["full"] }
|
||||||
|
@ -2,7 +2,7 @@ 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};
|
use quote::{format_ident, quote};
|
||||||
use syn::*;
|
use syn::{ext::IdentExt, *};
|
||||||
|
|
||||||
#[derive(Debug, FromMeta)]
|
#[derive(Debug, FromMeta)]
|
||||||
pub struct Args {}
|
pub struct Args {}
|
||||||
@ -22,7 +22,7 @@ pub fn transform(_args: Args, mut item: Item) -> Result<TokenStream> {
|
|||||||
_ => syn_error!(item, "expected struct or enum"),
|
_ => syn_error!(item, "expected struct or enum"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mod_name = format_ident!("__cdef__{name}");
|
let mod_name = format_ident!("__{name}_cdef");
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -42,23 +42,28 @@ pub fn transform(_args: Args, mut item: Item) -> Result<TokenStream> {
|
|||||||
fn generate_type(ty: &Ident) -> Result<TokenStream> {
|
fn generate_type(ty: &Ident) -> Result<TokenStream> {
|
||||||
let ffi = ffi_crate();
|
let ffi = ffi_crate();
|
||||||
let fmt = quote!(::std::format!);
|
let fmt = quote!(::std::format!);
|
||||||
let name_fmt = LitStr::new(&format!("{ty}"), ty.span());
|
let name = LitStr::new(&format!("{}", ty.unraw()), ty.span());
|
||||||
let cdecl_fmt = LitStr::new(&format!("struct {ty} {{name}}"), ty.span());
|
let cdecl_fmt = LitStr::new(&format!("struct {} {{}}", ty.unraw()), ty.span());
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
unsafe impl #ffi::Type for #ty {
|
unsafe impl #ffi::Type for #ty {
|
||||||
fn name() -> ::std::string::String {
|
fn name() -> impl ::std::fmt::Display {
|
||||||
#fmt(#name_fmt)
|
#name
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cdecl(name: impl ::std::fmt::Display) -> ::std::string::String {
|
fn cdecl(name: impl ::std::fmt::Display) -> impl ::std::fmt::Display {
|
||||||
#fmt(#cdecl_fmt)
|
#fmt(#cdecl_fmt, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(b: &mut #ffi::TypeBuilder) {
|
fn build(b: &mut #ffi::TypeBuilder) {
|
||||||
b.cdef::<Self>().metatype::<Self>();
|
b.cdef::<Self>().metatype::<Self>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe impl #ffi::ToFfi for #ty {
|
||||||
|
type To = Self;
|
||||||
|
fn convert(self) -> Self::To { self }
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,8 +79,8 @@ fn generate_cdef_structure(str: &mut ItemStruct) -> Result<TokenStream> {
|
|||||||
let build = generate_build_cdef(&to_cfields(&mut str.fields)?)?;
|
let build = generate_build_cdef(&to_cfields(&mut str.fields)?)?;
|
||||||
|
|
||||||
Ok(quote! {
|
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 }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -99,8 +104,8 @@ fn generate_cdef_enum(enu: &mut ItemEnum) -> Result<TokenStream> {
|
|||||||
.collect::<Result<Vec<_>>>()?;
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
Ok(quote! {
|
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)* });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,6 +118,11 @@ struct CField {
|
|||||||
attrs: CFieldAttrs,
|
attrs: CFieldAttrs,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct CFieldAttrs {
|
||||||
|
opaque: bool,
|
||||||
|
}
|
||||||
|
|
||||||
fn to_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(),
|
||||||
@ -123,7 +133,7 @@ fn to_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) => format!("{name}"),
|
Some(ref name) => format!("{}", name.unraw()),
|
||||||
None => format!("__{i}"),
|
None => format!("__{i}"),
|
||||||
},
|
},
|
||||||
ty: field.ty.clone(),
|
ty: field.ty.clone(),
|
||||||
@ -133,11 +143,6 @@ fn to_cfields(fields: &mut Fields) -> Result<Vec<CField>> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct CFieldAttrs {
|
|
||||||
opaque: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_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;
|
||||||
|
@ -5,16 +5,8 @@ use syn::parse_macro_input;
|
|||||||
|
|
||||||
mod cdef;
|
mod cdef;
|
||||||
mod metatype;
|
mod metatype;
|
||||||
mod module;
|
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
|
||||||
pub fn module(_args: TokenStream1, input: TokenStream1) -> TokenStream1 {
|
|
||||||
module::transform(parse_macro_input!(input))
|
|
||||||
.unwrap_or_else(|err| err.into_compile_error().into_token_stream())
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn cdef(args: TokenStream1, input: TokenStream1) -> TokenStream1 {
|
pub fn cdef(args: TokenStream1, input: TokenStream1) -> TokenStream1 {
|
||||||
NestedMeta::parse_meta_list(args.into())
|
NestedMeta::parse_meta_list(args.into())
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::utils::{ffi_crate, is_primitive, is_unit, pat_ident, syn_assert, ty_name};
|
use crate::utils::{ffi_crate, is_primitive, is_unit, pat_ident, syn_assert, ty_name};
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use syn::*;
|
use syn::{ext::IdentExt, *};
|
||||||
|
|
||||||
pub fn transform(mut imp: ItemImpl) -> Result<TokenStream> {
|
pub fn transform(mut imp: ItemImpl) -> Result<TokenStream> {
|
||||||
syn_assert!(
|
syn_assert!(
|
||||||
@ -11,7 +11,7 @@ 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
|
||||||
@ -31,25 +31,55 @@ fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
|
|||||||
|
|
||||||
let ffi = ffi_crate();
|
let ffi = ffi_crate();
|
||||||
let ffi_funcs = get_ffi_functions(imp)?;
|
let ffi_funcs = get_ffi_functions(imp)?;
|
||||||
|
|
||||||
|
// wrapper extern "C" functions that call the actual implementation
|
||||||
let ffi_wrappers: Vec<_> = ffi_funcs
|
let ffi_wrappers: Vec<_> = ffi_funcs
|
||||||
.iter()
|
.iter()
|
||||||
.map(generate_ffi_wrapper)
|
.map(generate_ffi_wrapper)
|
||||||
.collect::<Result<_>>()?;
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
|
// ffi function registration code
|
||||||
let ffi_register: Vec<_> = ffi_funcs
|
let ffi_register: Vec<_> = ffi_funcs
|
||||||
.iter()
|
.iter()
|
||||||
.map(generate_ffi_register)
|
.map(generate_ffi_register)
|
||||||
.collect::<Result<_>>()?;
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
let ffi_drop_fn = format_ident!("__ffi_drop");
|
let ffi_register_new = match ffi_funcs
|
||||||
let ffi_drop_name = format!("{ty_name}_drop");
|
.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) }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ffi function symbol export code
|
||||||
let ffi_exports = {
|
let ffi_exports = {
|
||||||
let mut names = vec![&ffi_drop_fn];
|
let mut names = vec![&ffi_drop_rname];
|
||||||
names.extend(ffi_funcs.iter().map(|f| &f.rust_name));
|
names.extend(ffi_funcs.iter().map(|f| &f.rust_name));
|
||||||
generate_ffi_exports(&ty, names.into_iter())?
|
generate_ffi_exports(&ty, names.into_iter())?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// lua function registration code
|
||||||
let lua_funcs = get_lua_functions(imp)?;
|
let lua_funcs = get_lua_functions(imp)?;
|
||||||
let lua_register: Vec<_> = lua_funcs
|
let lua_register: Vec<_> = lua_funcs
|
||||||
.iter()
|
.iter()
|
||||||
@ -57,6 +87,11 @@ fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
|
|||||||
.collect::<Result<_>>()?;
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
|
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;
|
||||||
|
|
||||||
@ -64,17 +99,8 @@ fn generate_impls(imp: &mut ItemImpl) -> Result<TokenStream> {
|
|||||||
#(#ffi_register)*
|
#(#ffi_register)*
|
||||||
#(#lua_register)*
|
#(#lua_register)*
|
||||||
|
|
||||||
b.declare::<unsafe extern "C" fn(*mut Self)>(#ffi_drop_name);
|
#ffi_register_new
|
||||||
b.metatable_raw("gc", ::std::format_args!("C.{}", #ffi_drop_name));
|
#ffi_register_drop
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl #ty {
|
|
||||||
#(#ffi_wrappers)*
|
|
||||||
|
|
||||||
#[unsafe(export_name = #ffi_drop_name)]
|
|
||||||
unsafe extern "C" fn #ffi_drop_fn(&mut self) {
|
|
||||||
unsafe { ::std::ptr::drop_in_place(self) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +115,13 @@ struct FfiFunction {
|
|||||||
c_name: String,
|
c_name: String,
|
||||||
params: Vec<PatType>,
|
params: Vec<PatType>,
|
||||||
ret: Type,
|
ret: Type,
|
||||||
ret_out: bool,
|
ret_by_out: bool,
|
||||||
|
attrs: FfiFunctionAttrs,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct FfiFunctionAttrs {
|
||||||
|
metatable: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
||||||
@ -103,6 +135,7 @@ 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
|
||||||
@ -118,22 +151,24 @@ fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
|||||||
})
|
})
|
||||||
.collect::<Result<_>>()?;
|
.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(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// whether to use out-param for return values
|
// whether to use out-param for return values
|
||||||
let ret_out = !is_primitive(&ret);
|
let ret_by_out = !is_primitive(&ret);
|
||||||
|
|
||||||
funcs.push(FfiFunction {
|
funcs.push(FfiFunction {
|
||||||
name: func.sig.ident.clone(),
|
name: func.sig.ident.clone(),
|
||||||
rust_name: format_ident!("__ffi_{}", func.sig.ident),
|
rust_name: format_ident!("__ffi_{}", func.sig.ident.unraw()),
|
||||||
lua_name: format!("{}", func.sig.ident),
|
lua_name: format!("{}", func.sig.ident.unraw()),
|
||||||
c_name: format!("{}_{}", ty_name(&imp.self_ty)?, func.sig.ident),
|
c_name: format!("{}_{}", ty_name(&imp.self_ty)?, func.sig.ident.unraw()),
|
||||||
params,
|
params,
|
||||||
ret,
|
ret,
|
||||||
ret_out,
|
ret_by_out,
|
||||||
|
attrs: parse_ffi_function_attrs(&mut func.attrs)?,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,6 +176,27 @@ fn get_ffi_functions(imp: &mut ItemImpl) -> Result<Vec<FfiFunction>> {
|
|||||||
Ok(funcs)
|
Ok(funcs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_ffi_function_attrs(attrs: &mut Vec<Attribute>) -> Result<FfiFunctionAttrs> {
|
||||||
|
let mut parsed = FfiFunctionAttrs::default();
|
||||||
|
let mut i = 0;
|
||||||
|
while let Some(attr) = attrs.get(i) {
|
||||||
|
if let Some(name) = attr.path().get_ident() {
|
||||||
|
if name == "metatable" {
|
||||||
|
parsed.metatable = Some(attr.parse_args::<LitStr>()?.value());
|
||||||
|
attrs.remove(i);
|
||||||
|
continue;
|
||||||
|
} else if name == "new" {
|
||||||
|
parsed.metatable = Some("new".into());
|
||||||
|
attrs.remove(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(parsed)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum FfiArgType {
|
enum FfiArgType {
|
||||||
Default,
|
Default,
|
||||||
@ -150,14 +206,6 @@ fn get_ffi_arg_type(_ty: &Type) -> FfiArgType {
|
|||||||
FfiArgType::Default
|
FfiArgType::Default
|
||||||
}
|
}
|
||||||
|
|
||||||
fn escape_self(name: &Ident) -> Ident {
|
|
||||||
if name == "self" {
|
|
||||||
format_ident!("__self")
|
|
||||||
} else {
|
|
||||||
name.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_ffi_wrapper(func: &FfiFunction) -> Result<TokenStream> {
|
fn generate_ffi_wrapper(func: &FfiFunction) -> Result<TokenStream> {
|
||||||
let ffi = ffi_crate();
|
let ffi = ffi_crate();
|
||||||
let name = &func.name;
|
let name = &func.name;
|
||||||
@ -166,37 +214,34 @@ fn generate_ffi_wrapper(func: &FfiFunction) -> Result<TokenStream> {
|
|||||||
let mut params = vec![];
|
let mut params = vec![];
|
||||||
let mut args = vec![];
|
let mut args = vec![];
|
||||||
|
|
||||||
for param in func.params.iter() {
|
for (i, param) in func.params.iter().enumerate() {
|
||||||
let name = escape_self(pat_ident(¶m.pat)?);
|
let name = format_ident!("arg{i}");
|
||||||
let ty = ¶m.ty;
|
let ty = ¶m.ty;
|
||||||
|
|
||||||
match get_ffi_arg_type(ty) {
|
match get_ffi_arg_type(ty) {
|
||||||
FfiArgType::Default => {
|
FfiArgType::Default => {
|
||||||
params.push(quote! { #name: <#ty as #ffi::FromFfi>::From });
|
params.push(quote! { #name: <#ty as #ffi::FromFfi>::FromArg });
|
||||||
args.push(quote! { <#ty as #ffi::FromFfi>::convert(#name) });
|
args.push(quote! { <#ty as #ffi::FromFfi>::convert_arg(#name) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make return by out-param the first parameter
|
let (ret, call) = if func.ret_by_out {
|
||||||
let (ret, do_ret) = if func.ret_out {
|
// make return by out-param the first parameter
|
||||||
let ret = &func.ret;
|
let ret = &func.ret;
|
||||||
params.insert(0, quote! { __ret_out: *mut #ret });
|
params.insert(0, quote! { out: *mut #ret });
|
||||||
(
|
(
|
||||||
quote! { () },
|
quote!(()),
|
||||||
quote! { unsafe { ::std::ptr::write(__ret_out, __ret) }; },
|
quote! { ::std::ptr::write(out, Self::#name(#(#args),*)) },
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let ret = &func.ret;
|
let ret = &func.ret;
|
||||||
(quote! { #ret }, quote! { return __ret; })
|
(quote! { #ret }, quote! { Self::#name(#(#args),*) })
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#[unsafe(export_name = #c_name)]
|
#[unsafe(export_name = #c_name)]
|
||||||
unsafe extern "C" fn #rust_name(#(#params),*) -> #ret {
|
unsafe extern "C" fn #rust_name(#(#params),*) -> #ret { unsafe { #call } }
|
||||||
let __ret = Self::#name(#(#args),*);
|
|
||||||
#do_ret
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,36 +249,58 @@ fn generate_ffi_register(func: &FfiFunction) -> Result<TokenStream> {
|
|||||||
let ffi = ffi_crate();
|
let ffi = ffi_crate();
|
||||||
let lua_name = &func.lua_name;
|
let lua_name = &func.lua_name;
|
||||||
let c_name = &func.c_name;
|
let c_name = &func.c_name;
|
||||||
|
|
||||||
let mut params = vec![];
|
let mut params = vec![];
|
||||||
let mut asserts = 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() {
|
for param in func.params.iter() {
|
||||||
let name = format!("{}", pat_ident(¶m.pat)?);
|
let name = format!("{}", pat_ident(¶m.pat)?);
|
||||||
let ty = ¶m.ty;
|
let ty = ¶m.ty;
|
||||||
|
|
||||||
params.push(match get_ffi_arg_type(ty) {
|
match get_ffi_arg_type(ty) {
|
||||||
FfiArgType::Default => quote! { b.param::<#ty>(#name); },
|
FfiArgType::Default => {
|
||||||
});
|
params.push(quote! { <#ty as #ffi::FromFfi>::FromArg });
|
||||||
|
register.push(quote! { b.param::<#ty>(#name); })
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret = &func.ret;
|
let ret = &func.ret;
|
||||||
let ret_conv = if is_unit(ret) {
|
let ret_conv = if is_unit(ret) {
|
||||||
quote! { #ffi::FfiReturnConvention::Void }
|
quote! { #ffi::FfiReturnConvention::Void }
|
||||||
} else if func.ret_out {
|
} else if func.ret_by_out {
|
||||||
asserts.push(quote! { #ffi::__internal::assert_type_ne_all!(#ret, ()); });
|
quote! { #ffi::FfiReturnConvention::ByOutParam }
|
||||||
quote! { #ffi::FfiReturnConvention::OutParam }
|
|
||||||
} else {
|
} else {
|
||||||
asserts.push(quote! { #ffi::__internal::assert_type_ne_all!(#ret, ()); });
|
|
||||||
quote! { #ffi::FfiReturnConvention::ByValue }
|
quote! { #ffi::FfiReturnConvention::ByValue }
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(quote! {
|
let declare = if func.ret_by_out {
|
||||||
b.index(#lua_name, |b| {
|
quote! { b.declare::<#ffi::UnsafeExternCFn<(*mut #ret, #(#params,)*), ()>>(#c_name); }
|
||||||
#(#asserts)*
|
} else {
|
||||||
#(#params)*
|
quote! { b.declare::<#ffi::UnsafeExternCFn<(#(#params,)*), #ret>>(#c_name); }
|
||||||
b.call::<#ret>(#c_name, #ret_conv);
|
};
|
||||||
});
|
|
||||||
})
|
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>(
|
fn generate_ffi_exports<'a>(
|
||||||
@ -241,7 +308,8 @@ fn generate_ffi_exports<'a>(
|
|||||||
names: impl Iterator<Item = &'a Ident>,
|
names: impl Iterator<Item = &'a Ident>,
|
||||||
) -> Result<TokenStream> {
|
) -> Result<TokenStream> {
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
// hack to prevent ffi functions from being dead code-eliminated
|
// this ensures ffi function symbol exports are actually present in the resulting binary,
|
||||||
|
// otherwise they may get dead code-eliminated before it reaches the linker
|
||||||
#[used]
|
#[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 ())),*]
|
||||||
@ -251,7 +319,7 @@ fn generate_ffi_exports<'a>(
|
|||||||
|
|
||||||
struct LuaFunction {
|
struct LuaFunction {
|
||||||
name: String,
|
name: String,
|
||||||
params: Vec<PatType>,
|
params: Vec<Pat>,
|
||||||
body: Block,
|
body: Block,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +333,8 @@ fn get_lua_functions(imp: &mut ItemImpl) -> Result<Vec<LuaFunction>> {
|
|||||||
&& let Some(ref abi) = abi.name
|
&& let Some(ref abi) = abi.name
|
||||||
&& abi.value() == "Lua"
|
&& abi.value() == "Lua"
|
||||||
{
|
{
|
||||||
let params = func
|
// normalise inputs to Pat
|
||||||
|
let mut params: Vec<_> = func
|
||||||
.sig
|
.sig
|
||||||
.inputs
|
.inputs
|
||||||
.iter()
|
.iter()
|
||||||
@ -274,15 +343,26 @@ fn get_lua_functions(imp: &mut ItemImpl) -> Result<Vec<LuaFunction>> {
|
|||||||
FnArg::Receiver(recv) => {
|
FnArg::Receiver(recv) => {
|
||||||
syn_assert!(ty_name(&recv.ty)? == "Self", recv, "must be `self`");
|
syn_assert!(ty_name(&recv.ty)? == "Self", recv, "must be `self`");
|
||||||
syn_assert!(recv.mutability.is_none(), recv, "cannot be mut");
|
syn_assert!(recv.mutability.is_none(), recv, "cannot be mut");
|
||||||
parse_quote! { self: cdata }
|
Pat::Type(parse_quote! { self: cdata })
|
||||||
}
|
}
|
||||||
FnArg::Typed(ty) => ty.clone(),
|
FnArg::Typed(ty) => Pat::Type(ty.clone()),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Result<_>>()?;
|
.collect::<Result<_>>()?;
|
||||||
|
|
||||||
|
if let Some(_) = func.sig.variadic {
|
||||||
|
params.push(parse_quote!(variadic!()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// shouldn't specify an output type
|
||||||
|
syn_assert!(
|
||||||
|
matches!(func.sig.output, ReturnType::Default),
|
||||||
|
func.sig.output,
|
||||||
|
"cannot have return type"
|
||||||
|
);
|
||||||
|
|
||||||
funcs.push(LuaFunction {
|
funcs.push(LuaFunction {
|
||||||
name: format!("{}", func.sig.ident),
|
name: format!("{}", func.sig.ident.unraw()),
|
||||||
body: func.block.clone(),
|
body: func.block.clone(),
|
||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
use crate::utils::syn_assert;
|
|
||||||
use proc_macro2::TokenStream;
|
|
||||||
use quote::quote;
|
|
||||||
use syn::*;
|
|
||||||
|
|
||||||
pub fn transform(func: ItemFn) -> Result<TokenStream> {
|
|
||||||
syn_assert!(
|
|
||||||
func.sig.generics.params.is_empty(),
|
|
||||||
func.sig.generics,
|
|
||||||
"cannot be generic (not yet implemented)"
|
|
||||||
);
|
|
||||||
|
|
||||||
// let impls = generate_impls(&mut imp)?;
|
|
||||||
// let mod_name = format_ident!("__metatype__{}", ty_name(&imp.self_ty)?);
|
|
||||||
|
|
||||||
Ok(quote! {
|
|
||||||
#func
|
|
||||||
|
|
||||||
// #[doc(hidden)]
|
|
||||||
// #[allow(unused, non_snake_case)]
|
|
||||||
// mod #mod_name {
|
|
||||||
// use super::*;
|
|
||||||
// #impls
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
use std::env;
|
use std::env;
|
||||||
use syn::*;
|
use syn::{spanned::Spanned, *};
|
||||||
|
|
||||||
macro_rules! syn_error {
|
macro_rules! syn_error {
|
||||||
($src:expr, $($fmt:expr),+) => {{
|
($src:expr, $($fmt:expr),+) => {{
|
||||||
@ -35,11 +35,15 @@ pub fn ty_name(ty: &Type) -> Result<&Ident> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pat_ident(pat: &Pat) -> Result<&Ident> {
|
pub fn pat_ident(pat: &Pat) -> Result<Ident> {
|
||||||
match pat {
|
Ok(match pat {
|
||||||
Pat::Ident(ident) => Ok(&ident.ident),
|
Pat::Ident(ident) => match ident.subpat {
|
||||||
|
Some((_, ref subpat)) => syn_error!(subpat, "unexpected subpattern"),
|
||||||
|
None => ident.ident.clone(),
|
||||||
|
},
|
||||||
|
Pat::Wild(wild) => Ident::new("_", wild.span()),
|
||||||
_ => syn_error!(pat, "expected ident"),
|
_ => syn_error!(pat, "expected ident"),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_unit(ty: &Type) -> bool {
|
pub fn is_unit(ty: &Type) -> bool {
|
||||||
|
@ -163,9 +163,9 @@ fn generate_expr_binary(f: &mut Formatter, bin: &ExprBinary, cx: Context) -> Res
|
|||||||
| BinOp::Shl(_)
|
| BinOp::Shl(_)
|
||||||
| BinOp::Shr(_)
|
| BinOp::Shr(_)
|
||||||
| BinOp::Eq(_)
|
| BinOp::Eq(_)
|
||||||
|
| BinOp::Ne(_)
|
||||||
| BinOp::Lt(_)
|
| BinOp::Lt(_)
|
||||||
| BinOp::Le(_)
|
| BinOp::Le(_)
|
||||||
| BinOp::Ne(_)
|
|
||||||
| BinOp::Ge(_)
|
| BinOp::Ge(_)
|
||||||
| BinOp::Gt(_)
|
| BinOp::Gt(_)
|
||||||
| BinOp::And(_)
|
| BinOp::And(_)
|
||||||
@ -233,22 +233,22 @@ fn generate_expr_binary(f: &mut Formatter, bin: &ExprBinary, cx: Context) -> Res
|
|||||||
BinOp::MulAssign(_) => assign_bin_op!("*"),
|
BinOp::MulAssign(_) => assign_bin_op!("*"),
|
||||||
BinOp::Div(_) => bin_op!("/"),
|
BinOp::Div(_) => bin_op!("/"),
|
||||||
BinOp::DivAssign(_) => assign_bin_op!("/"),
|
BinOp::DivAssign(_) => assign_bin_op!("/"),
|
||||||
BinOp::Rem(_) => call_op!("math.fmod"),
|
BinOp::Rem(_) => call_op!("__fmod"),
|
||||||
BinOp::RemAssign(_) => assign_call_op!("math.fmod"),
|
BinOp::RemAssign(_) => assign_call_op!("__fmod"),
|
||||||
BinOp::BitAnd(_) => call_op!("band"),
|
BinOp::BitAnd(_) => call_op!("__band"),
|
||||||
BinOp::BitAndAssign(_) => assign_call_op!("band"),
|
BinOp::BitAndAssign(_) => assign_call_op!("__band"),
|
||||||
BinOp::BitOr(_) => call_op!("bor"),
|
BinOp::BitOr(_) => call_op!("__bor"),
|
||||||
BinOp::BitOrAssign(_) => assign_call_op!("bor"),
|
BinOp::BitOrAssign(_) => assign_call_op!("__bor"),
|
||||||
BinOp::BitXor(_) => call_op!("bxor"),
|
BinOp::BitXor(_) => call_op!("__bxor"),
|
||||||
BinOp::BitXorAssign(_) => assign_call_op!("bxor"),
|
BinOp::BitXorAssign(_) => assign_call_op!("__bxor"),
|
||||||
BinOp::Shl(_) => call_op!("lshift"),
|
BinOp::Shl(_) => call_op!("__blshift"),
|
||||||
BinOp::ShlAssign(_) => assign_call_op!("lshift"),
|
BinOp::ShlAssign(_) => assign_call_op!("__blshift"),
|
||||||
BinOp::Shr(_) => call_op!("arshift"),
|
BinOp::Shr(_) => call_op!("__barshift"),
|
||||||
BinOp::ShrAssign(_) => assign_call_op!("arshift"),
|
BinOp::ShrAssign(_) => assign_call_op!("__barshift"),
|
||||||
BinOp::Eq(_) => bin_op!("=="),
|
BinOp::Eq(_) => bin_op!("=="),
|
||||||
|
BinOp::Ne(_) => bin_op!("~="),
|
||||||
BinOp::Lt(_) => bin_op!("<"),
|
BinOp::Lt(_) => bin_op!("<"),
|
||||||
BinOp::Le(_) => bin_op!("<="),
|
BinOp::Le(_) => bin_op!("<="),
|
||||||
BinOp::Ne(_) => bin_op!("~="),
|
|
||||||
BinOp::Ge(_) => bin_op!(">="),
|
BinOp::Ge(_) => bin_op!(">="),
|
||||||
BinOp::Gt(_) => bin_op!(">"),
|
BinOp::Gt(_) => bin_op!(">"),
|
||||||
BinOp::And(_) => bin_op!("and"),
|
BinOp::And(_) => bin_op!("and"),
|
||||||
@ -569,9 +569,10 @@ fn generate_expr_while(f: &mut Formatter, whil: &ExprWhile, cx: Context) -> Resu
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn generate_punctuated_expr(f: &mut Formatter, exprs: &Punctuated<Expr, Token![,]>) -> Result<()> {
|
fn generate_punctuated_expr(f: &mut Formatter, exprs: &Punctuated<Expr, Token![,]>) -> Result<()> {
|
||||||
|
let len = exprs.len();
|
||||||
for (i, expr) in exprs.iter().enumerate() {
|
for (i, expr) in exprs.iter().enumerate() {
|
||||||
(i != 0).then(|| f.write(","));
|
(i != 0).then(|| f.write(","));
|
||||||
generate_expr(f, expr, Context::expr(false))?;
|
generate_expr(f, expr, Context::expr(i == len - 1))?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -795,6 +796,8 @@ fn generate_receiver(f: &mut Formatter, recv: &Receiver) -> Result<()> {
|
|||||||
fn generate_macro(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> {
|
fn generate_macro(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> {
|
||||||
match format!("{}", mac.path.require_ident()?).as_str() {
|
match format!("{}", mac.path.require_ident()?).as_str() {
|
||||||
"concat" => generate_macro_concat(f, mac, cx),
|
"concat" => generate_macro_concat(f, mac, cx),
|
||||||
|
"len" => generate_macro_len(f, mac, cx),
|
||||||
|
"variadic" => generate_macro_variadic(f, mac, cx),
|
||||||
"embed" => generate_macro_embed(f, mac, cx),
|
"embed" => generate_macro_embed(f, mac, cx),
|
||||||
"raw" => generate_macro_raw(f, mac, cx),
|
"raw" => generate_macro_raw(f, mac, cx),
|
||||||
name => syn_error!(mac.path, "unknown macro '{name}'"),
|
name => syn_error!(mac.path, "unknown macro '{name}'"),
|
||||||
@ -813,6 +816,24 @@ fn generate_macro_concat(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_macro_len(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> {
|
||||||
|
syn_assert!(cx.is_value(), mac, "len! must be in expression position");
|
||||||
|
cx.is_ret().then(|| f.write("return"));
|
||||||
|
f.write("#");
|
||||||
|
generate_expr(f, &mac.parse_body()?, Context::expr(false))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_macro_variadic(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> {
|
||||||
|
syn_assert!(
|
||||||
|
cx.is_multi_expr(),
|
||||||
|
mac,
|
||||||
|
"variadic! must be in multi-value expression position"
|
||||||
|
);
|
||||||
|
cx.is_ret().then(|| f.write("return"));
|
||||||
|
f.write("...");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_macro_embed(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> {
|
fn generate_macro_embed(f: &mut Formatter, mac: &Macro, cx: Context) -> Result<()> {
|
||||||
syn_assert!(cx.is_value(), mac, "embed! must be in expression position");
|
syn_assert!(cx.is_value(), mac, "embed! must be in expression position");
|
||||||
cx.is_ret().then(|| f.write("return"));
|
cx.is_ret().then(|| f.write("return"));
|
||||||
@ -871,9 +892,9 @@ fn generate_pat_ident(f: &mut Formatter, ident: &PatIdent, _cx: PatContext) -> R
|
|||||||
generate_ident(f, &ident.ident)
|
generate_ident(f, &ident.ident)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_pat_macro(f: &mut Formatter, mac: &PatMacro, _cx: PatContext) -> Result<()> {
|
fn generate_pat_macro(f: &mut Formatter, mac: &PatMacro, cx: PatContext) -> Result<()> {
|
||||||
assert_no_attrs!(mac);
|
assert_no_attrs!(mac);
|
||||||
generate_macro(f, &mac.mac, Context::expr(false))
|
generate_macro(f, &mac.mac, Context::expr(cx.is_multi()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_pat_tuple(f: &mut Formatter, tuple: &PatTuple, cx: PatContext) -> Result<()> {
|
fn generate_pat_tuple(f: &mut Formatter, tuple: &PatTuple, cx: PatContext) -> Result<()> {
|
||||||
@ -900,9 +921,15 @@ fn generate_pat_wild(f: &mut Formatter, wild: &PatWild, _cx: PatContext) -> Resu
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn generate_punctuated_pat(f: &mut Formatter, pats: &Punctuated<Pat, Token![,]>) -> Result<()> {
|
fn generate_punctuated_pat(f: &mut Formatter, pats: &Punctuated<Pat, Token![,]>) -> Result<()> {
|
||||||
|
let len = pats.len();
|
||||||
for (i, pat) in pats.iter().enumerate() {
|
for (i, pat) in pats.iter().enumerate() {
|
||||||
(i != 0).then(|| f.write(","));
|
(i != 0).then(|| f.write(","));
|
||||||
generate_pat(f, pat, PatContext::Single)?;
|
let cx = if i == len - 1 {
|
||||||
|
PatContext::Multi
|
||||||
|
} else {
|
||||||
|
PatContext::Single
|
||||||
|
};
|
||||||
|
generate_pat(f, pat, cx)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::utils::{LuaType, syn_error, unwrap_expr_ident, unwrap_pat_ident, wrap_expr_block};
|
use crate::utils::{LuaType, expr_ident, pat_ident, syn_error, wrap_expr_block};
|
||||||
use quote::format_ident;
|
use quote::format_ident;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use syn::{spanned::*, visit_mut::*, *};
|
use syn::{spanned::*, visit_mut::*, *};
|
||||||
@ -73,7 +73,7 @@ impl Visitor {
|
|||||||
match input {
|
match input {
|
||||||
Pat::Ident(_) => {}
|
Pat::Ident(_) => {}
|
||||||
Pat::Type(typed) => {
|
Pat::Type(typed) => {
|
||||||
let ident = unwrap_pat_ident(&typed.pat)?;
|
let ident = pat_ident(&typed.pat)?;
|
||||||
let ty = mem::replace(&mut typed.ty, parse_quote!(_));
|
let ty = mem::replace(&mut typed.ty, parse_quote!(_));
|
||||||
match (&*ty).try_into()? {
|
match (&*ty).try_into()? {
|
||||||
LuaType::Any => {}
|
LuaType::Any => {}
|
||||||
@ -112,7 +112,7 @@ impl Visitor {
|
|||||||
Some((Ident::new("self", recv.self_token.span()), ty))
|
Some((Ident::new("self", recv.self_token.span()), ty))
|
||||||
}
|
}
|
||||||
FnArg::Typed(typed) => {
|
FnArg::Typed(typed) => {
|
||||||
let ident = unwrap_pat_ident(&typed.pat)?;
|
let ident = pat_ident(&typed.pat)?;
|
||||||
let ty = mem::replace(&mut typed.ty, parse_quote!(_));
|
let ty = mem::replace(&mut typed.ty, parse_quote!(_));
|
||||||
Some((ident, ty))
|
Some((ident, ty))
|
||||||
}
|
}
|
||||||
@ -149,9 +149,9 @@ impl Visitor {
|
|||||||
let mut prelude: Option<Stmt> = None;
|
let mut prelude: Option<Stmt> = None;
|
||||||
let ty: LuaType = (&*cast.ty).try_into()?;
|
let ty: LuaType = (&*cast.ty).try_into()?;
|
||||||
let ty_str = format!("{ty}");
|
let ty_str = format!("{ty}");
|
||||||
let (ident, msg) = match unwrap_expr_ident(&arg).ok() {
|
let (ident, msg) = match expr_ident(&arg) {
|
||||||
Some(ident) => (ident.clone(), format!("{ty} expected in '{ident}', got ")),
|
Ok(ident) => (ident.clone(), format!("{ty} expected in '{ident}', got ")),
|
||||||
None => {
|
Err(_) => {
|
||||||
let ident = Ident::new("_", arg.span());
|
let ident = Ident::new("_", arg.span());
|
||||||
prelude = Some(parse_quote! { let #ident = #arg; });
|
prelude = Some(parse_quote! { let #ident = #arg; });
|
||||||
(ident, format!("{ty} expected, got "))
|
(ident, format!("{ty} expected, got "))
|
||||||
|
@ -25,14 +25,14 @@ pub fn wrap_expr_block(expr: &Expr) -> Block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unwrap_expr_ident(expr: &Expr) -> Result<&Ident> {
|
pub fn expr_ident(expr: &Expr) -> Result<&Ident> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Path(path) => path.path.require_ident(),
|
Expr::Path(path) => path.path.require_ident(),
|
||||||
_ => syn_error!(expr, "expected ident"),
|
_ => syn_error!(expr, "expected ident"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unwrap_pat_ident(pat: &Pat) -> Result<Ident> {
|
pub fn pat_ident(pat: &Pat) -> Result<Ident> {
|
||||||
Ok(match pat {
|
Ok(match pat {
|
||||||
Pat::Ident(ident) => match ident.subpat {
|
Pat::Ident(ident) => match ident.subpat {
|
||||||
Some((_, ref subpat)) => syn_error!(subpat, "unexpected subpattern"),
|
Some((_, ref subpat)) => syn_error!(subpat, "unexpected subpattern"),
|
||||||
|
@ -279,16 +279,16 @@ fn ops() {
|
|||||||
assert_eq!(luaify!(|| a *= b), r#"function()a=a*b;end"#);
|
assert_eq!(luaify!(|| a *= b), r#"function()a=a*b;end"#);
|
||||||
assert_eq!(luaify!(|| a / b), r#"function()return a/b;end"#);
|
assert_eq!(luaify!(|| a / b), r#"function()return a/b;end"#);
|
||||||
assert_eq!(luaify!(|| a /= b), r#"function()a=a/b;end"#);
|
assert_eq!(luaify!(|| a /= b), r#"function()a=a/b;end"#);
|
||||||
assert_eq!(luaify!(|| a = b % c), r#"function()a=math.fmod(b,c);end"#);
|
assert_eq!(luaify!(|| a = b % c), r#"function()a=__fmod(b,c);end"#);
|
||||||
assert_eq!(luaify!(|| a = b << c), r#"function()a=lshift(b,c);end"#);
|
assert_eq!(luaify!(|| a = b << c), r#"function()a=__blshift(b,c);end"#);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
luaify!(|| a <<= b << c),
|
luaify!(|| a <<= b << c),
|
||||||
r#"function()a=lshift(a,lshift(b,c));end"#
|
r#"function()a=__blshift(a,__blshift(b,c));end"#
|
||||||
);
|
);
|
||||||
assert_eq!(luaify!(|| a = b >> c), r#"function()a=arshift(b,c);end"#);
|
assert_eq!(luaify!(|| a = b >> c), r#"function()a=__barshift(b,c);end"#);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
luaify!(|| a >>= b >> c),
|
luaify!(|| a >>= b >> c),
|
||||||
r#"function()a=arshift(a,arshift(b,c));end"#
|
r#"function()a=__barshift(a,__barshift(b,c));end"#
|
||||||
);
|
);
|
||||||
assert_eq!(luaify!(|| a && b), r#"function()return a and b;end"#);
|
assert_eq!(luaify!(|| a && b), r#"function()return a and b;end"#);
|
||||||
assert_eq!(luaify!(|| a || b), r#"function()return a or b;end"#);
|
assert_eq!(luaify!(|| a || b), r#"function()return a or b;end"#);
|
||||||
@ -310,15 +310,15 @@ fn ops() {
|
|||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
luaify!(|| -a || !--b && c >> d),
|
luaify!(|| -a || !--b && c >> d),
|
||||||
r#"function()return-a or not-(-b)and arshift(c,d);end"#
|
r#"function()return-a or not-(-b)and __barshift(c,d);end"#
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
luaify!(|| -a || !(--b && c) >> d),
|
luaify!(|| -a || !(--b && c) >> d),
|
||||||
r#"function()return-a or arshift(not(-(-b)and c),d);end"#
|
r#"function()return-a or __barshift(not(-(-b)and c),d);end"#
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
luaify!(|| a >> b << c >> d),
|
luaify!(|| a >> b << c >> d),
|
||||||
r#"function()return arshift(lshift(arshift(a,b),c),d);end"#
|
r#"function()return __barshift(__blshift(__barshift(a,b),c),d);end"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -373,3 +373,32 @@ fn ifs() {
|
|||||||
r#"function()if a==b then c();elseif b==c then return a();else d();end;end"#
|
r#"function()if a==b then c();elseif b==c then return a();else d();end;end"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn variadic() {
|
||||||
|
assert_eq!(luaify!(|a, b, variadic!()| {}), r#"function(a,b,...)end"#);
|
||||||
|
assert_eq!(
|
||||||
|
luaify!(|variadic!()| {
|
||||||
|
let (a, b) = variadic!();
|
||||||
|
}),
|
||||||
|
r#"function(...)local a,b=...;end"#
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
luaify!(|a, variadic!()| {
|
||||||
|
let (a, b) = (a, b, c, variadic!());
|
||||||
|
func(a, b, (c, (d, variadic!())))
|
||||||
|
}),
|
||||||
|
r#"function(a,...)local a,b=a,b,c,...;return func(a,b,c,d,...);end"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn length() {
|
||||||
|
assert_eq!(luaify!(len!(a)), r#"#a"#);
|
||||||
|
assert_eq!(
|
||||||
|
luaify!({
|
||||||
|
let (a, b, c) = (len!(a), len!(b), len!(c));
|
||||||
|
}),
|
||||||
|
r#"local a,b,c=#a,#b,#c;"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -6,8 +6,6 @@ edition = "2024"
|
|||||||
[lib]
|
[lib]
|
||||||
path = "lib.rs"
|
path = "lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["jit", "ffi", "lua52"]
|
default = ["jit", "ffi", "lua52"]
|
||||||
runtime = []
|
runtime = []
|
||||||
|
@ -118,7 +118,7 @@ fn build_runtime(src_path: &Path) {
|
|||||||
|
|
||||||
let mut make = find_make();
|
let mut make = find_make();
|
||||||
|
|
||||||
make.current_dir(&src_path)
|
make.current_dir(src_path)
|
||||||
.env_clear()
|
.env_clear()
|
||||||
.arg("-e")
|
.arg("-e")
|
||||||
.env("PATH", env!("PATH"))
|
.env("PATH", env!("PATH"))
|
||||||
@ -160,7 +160,7 @@ fn build_runtime(src_path: &Path) {
|
|||||||
// host toolchain config
|
// host toolchain config
|
||||||
match (host_ptr_width, target_ptr_width) {
|
match (host_ptr_width, target_ptr_width) {
|
||||||
(64, 64) | (32, 32) => make.env("HOST_CC", &host_cc),
|
(64, 64) | (32, 32) => make.env("HOST_CC", &host_cc),
|
||||||
(64, 32) => make.env("HOST_CC", format!("{} -m32", host_cc)),
|
(64, 32) => make.env("HOST_CC", format!("{host_cc} -m32")),
|
||||||
(n, m) if n != m => panic!("cannot cross-compile on {n}-bit host for {m}-bit target"),
|
(n, m) if n != m => panic!("cannot cross-compile on {n}-bit host for {m}-bit target"),
|
||||||
(n, _) => panic!("unsupported {n}-bit architecture"),
|
(n, _) => panic!("unsupported {n}-bit architecture"),
|
||||||
};
|
};
|
||||||
@ -196,7 +196,7 @@ fn build_runtime(src_path: &Path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// propagate linker config
|
// propagate linker config
|
||||||
if let Some(path) = env::var("RUSTC_LINKER").ok() {
|
if let Ok(path) = env::var("RUSTC_LINKER") {
|
||||||
make.env("TARGET_LD", panic_err!(which(path), "failed to find ld"));
|
make.env("TARGET_LD", panic_err!(which(path), "failed to find ld"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
1
rustfmt.toml
Normal file
1
rustfmt.toml
Normal file
@ -0,0 +1 @@
|
|||||||
|
max_width = 100
|
@ -1,2 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
cargo build "$@"
|
|
@ -1,2 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
cargo doc --all --no-deps "$@"
|
|
14
src/main.rs
14
src/main.rs
@ -136,12 +136,12 @@ fn init_vm(_args: &Args) -> luajit::State {
|
|||||||
luajit::State::new().unwrap_or_else(|err| panic!("failed to initialise runtime: {err}"));
|
luajit::State::new().unwrap_or_else(|err| panic!("failed to initialise runtime: {err}"));
|
||||||
|
|
||||||
let mut registry = luaffi::Registry::new();
|
let mut registry = luaffi::Registry::new();
|
||||||
registry.include::<lb_core::lb_core>();
|
registry.preload::<lb_core::lb_core>("lb:core");
|
||||||
|
|
||||||
println!("{registry}");
|
println!("{registry}");
|
||||||
|
|
||||||
state
|
state
|
||||||
.load(Some("@[luby]"), registry.done(), luajit::LoadMode::TEXT)
|
.load(&luajit::Chunk::new(registry.done()).name("@[luby]"))
|
||||||
.and_then(|()| state.call(0, 0))
|
.and_then(|()| state.call(0, 0))
|
||||||
.unwrap_or_else(|err| panic!("failed to load modules: {err}"));
|
.unwrap_or_else(|err| panic!("failed to load modules: {err}"));
|
||||||
|
|
||||||
@ -156,13 +156,13 @@ async fn run(args: Args) {
|
|||||||
Err(err) => return eprintln!("{}", format!("{path}: {err}").red()),
|
Err(err) => return eprintln!("{}", format!("{path}: {err}").red()),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) = state.load(Some(format!("@{path}")), chunk, Default::default()) {
|
if let Err(err) = state.load(&luajit::Chunk::new(chunk).path(path)) {
|
||||||
return eprintln!("{}", err.red());
|
return eprintln!("{}", err.red());
|
||||||
}
|
}
|
||||||
|
|
||||||
state
|
match state.call_async(0, 0).await {
|
||||||
.call_async(0)
|
Ok(_) => {}
|
||||||
.await
|
Err(err) => GlobalState::uncaught_error(err),
|
||||||
.unwrap_or_else(GlobalState::uncaught_error);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
7
stylua.toml
Normal file
7
stylua.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
syntax = "LuaJIT"
|
||||||
|
indent_type = "Spaces"
|
||||||
|
indent_width = 2
|
||||||
|
column_width = 100
|
||||||
|
quote_style = "ForceDouble"
|
||||||
|
call_parentheses = "NoSingleTable"
|
||||||
|
collapse_simple_statement = "ConditionalOnly"
|
Loading…
x
Reference in New Issue
Block a user