From 06d3eebaa1dbde4f242e15dd18e9e07ec2da05ac Mon Sep 17 00:00:00 2001 From: luaneko Date: Fri, 20 Jun 2025 21:48:23 +1000 Subject: [PATCH] Working commit --- .gitmodules | 2 +- Cargo.lock | 11 +- Cargo.toml | 5 + crates/lb_core/src/lib.rs | 11 +- crates/luaffi/src/lib.rs | 10 +- crates/luaffi_impl/src/cdef.rs | 2 +- crates/luaffi_impl/src/lib.rs | 10 +- crates/luaffi_impl/src/metatype.rs | 6 +- crates/luaffi_impl/src/module.rs | 26 +++++ crates/luaffi_impl/src/utils.rs | 5 +- crates/luajit-sys/Cargo.toml | 23 ++++ crates/{luajit => luajit-sys}/build.rs | 91 ++++++++++----- crates/luajit-sys/lib.rs | 99 ++++++++++++++++ crates/{luajit => luajit-sys}/src | 0 crates/luajit/Cargo.toml | 15 +-- crates/luajit/{ => src}/lib.rs | 155 ++++++++++--------------- scripts/build.sh | 2 + scripts/doc.sh | 2 + 18 files changed, 316 insertions(+), 159 deletions(-) create mode 100644 crates/luaffi_impl/src/module.rs create mode 100644 crates/luajit-sys/Cargo.toml rename crates/{luajit => luajit-sys}/build.rs (71%) create mode 100644 crates/luajit-sys/lib.rs rename crates/{luajit => luajit-sys}/src (100%) rename crates/luajit/{ => src}/lib.rs (90%) create mode 100755 scripts/build.sh create mode 100755 scripts/doc.sh diff --git a/.gitmodules b/.gitmodules index 6e62b72..2032e1e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "luajit"] - path = crates/luajit/src + path = crates/luajit-sys/src url = https://github.com/LuaJIT/LuaJIT.git diff --git a/Cargo.lock b/Cargo.lock index a053a02..6520d62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -838,12 +838,19 @@ dependencies = [ name = "luajit" version = "0.1.0" dependencies = [ - "bindgen", "bitflags", "bstr", - "cc", "luaffi", + "luajit-sys", "thiserror", +] + +[[package]] +name = "luajit-sys" +version = "0.1.0" +dependencies = [ + "bindgen", + "cc", "which", ] diff --git a/Cargo.toml b/Cargo.toml index 16a51d8..58d9d99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,8 +5,13 @@ members = [ "crates/luaffi_impl", "crates/luaify", "crates/luajit", + "crates/luajit-sys", ] +[profile] +dev.panic = "abort" +release.panic = "abort" + [package] name = "luby" version = "0.1.0" diff --git a/crates/lb_core/src/lib.rs b/crates/lb_core/src/lib.rs index b03a240..d65c9fc 100644 --- a/crates/lb_core/src/lib.rs +++ b/crates/lb_core/src/lib.rs @@ -1,29 +1,28 @@ use luaffi::{cdef, metatype}; -use luajit::State; use owo_colors::OwoColorize; use std::{cell::RefCell, fmt, process}; #[derive(Debug)] -pub struct GlobalState(State); +pub struct GlobalState(luajit::State); impl GlobalState { thread_local! { static STATE: RefCell> = RefCell::new(None); } - pub fn set(state: State) -> Option { + pub fn set(state: luajit::State) -> Option { Self::STATE.with_borrow_mut(|s| s.replace(Self(state)).map(|s| s.0)) } - pub fn with_current(f: impl FnOnce(&State) -> T) -> T { + pub fn with_current(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(f: impl FnOnce(&mut State) -> T) -> T { + pub fn with_current_mut(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() -> State { + pub fn new_thread() -> luajit::State { Self::with_current(|s| s.new_thread()) } diff --git a/crates/luaffi/src/lib.rs b/crates/luaffi/src/lib.rs index 6b2d416..0521407 100644 --- a/crates/luaffi/src/lib.rs +++ b/crates/luaffi/src/lib.rs @@ -135,6 +135,11 @@ impl Registry { s } + pub fn preload(&mut self, _name: impl Display) -> &mut Self { + self.include::(); + self + } + pub fn include(&mut self) -> &mut Self { self.types .insert(T::name().to_string()) @@ -150,11 +155,6 @@ impl Registry { self } - pub fn preload(&mut self, name: impl Display) -> &mut Self { - self.include::(); - self - } - pub fn done(&self) -> String { self.to_string() } diff --git a/crates/luaffi_impl/src/cdef.rs b/crates/luaffi_impl/src/cdef.rs index ef15807..87ab870 100644 --- a/crates/luaffi_impl/src/cdef.rs +++ b/crates/luaffi_impl/src/cdef.rs @@ -2,7 +2,7 @@ use crate::utils::{ffi_crate, syn_assert, syn_error}; use darling::FromMeta; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use syn::{spanned::*, *}; +use syn::*; #[derive(Debug, FromMeta)] pub struct Args {} diff --git a/crates/luaffi_impl/src/lib.rs b/crates/luaffi_impl/src/lib.rs index 4ed3b1f..b836404 100644 --- a/crates/luaffi_impl/src/lib.rs +++ b/crates/luaffi_impl/src/lib.rs @@ -5,8 +5,16 @@ use syn::parse_macro_input; mod cdef; mod metatype; +mod module; 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] pub fn cdef(args: TokenStream1, input: TokenStream1) -> TokenStream1 { NestedMeta::parse_meta_list(args.into()) @@ -17,7 +25,7 @@ pub fn cdef(args: TokenStream1, input: TokenStream1) -> TokenStream1 { } #[proc_macro_attribute] -pub fn metatype(args: TokenStream1, input: TokenStream1) -> TokenStream1 { +pub fn metatype(_args: TokenStream1, input: TokenStream1) -> TokenStream1 { metatype::transform(parse_macro_input!(input)) .unwrap_or_else(|err| err.into_compile_error().into_token_stream()) .into() diff --git a/crates/luaffi_impl/src/metatype.rs b/crates/luaffi_impl/src/metatype.rs index 59fc81b..cbc29dc 100644 --- a/crates/luaffi_impl/src/metatype.rs +++ b/crates/luaffi_impl/src/metatype.rs @@ -1,7 +1,7 @@ -use crate::utils::{ffi_crate, is_primitive, is_unit, pat_ident, syn_assert, syn_error, ty_name}; +use crate::utils::{ffi_crate, is_primitive, is_unit, pat_ident, syn_assert, ty_name}; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use syn::{spanned::*, *}; +use syn::*; pub fn transform(mut imp: ItemImpl) -> Result { syn_assert!( @@ -146,7 +146,7 @@ enum FfiArgType { Default, } -fn get_ffi_arg_type(ty: &Type) -> FfiArgType { +fn get_ffi_arg_type(_ty: &Type) -> FfiArgType { FfiArgType::Default } diff --git a/crates/luaffi_impl/src/module.rs b/crates/luaffi_impl/src/module.rs new file mode 100644 index 0000000..5e55d46 --- /dev/null +++ b/crates/luaffi_impl/src/module.rs @@ -0,0 +1,26 @@ +use crate::utils::syn_assert; +use proc_macro2::TokenStream; +use quote::quote; +use syn::*; + +pub fn transform(func: ItemFn) -> Result { + 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 + // } + }) +} diff --git a/crates/luaffi_impl/src/utils.rs b/crates/luaffi_impl/src/utils.rs index f0b3302..13254ac 100644 --- a/crates/luaffi_impl/src/utils.rs +++ b/crates/luaffi_impl/src/utils.rs @@ -1,8 +1,9 @@ use std::env; -use syn::{spanned::*, *}; +use syn::*; macro_rules! syn_error { ($src:expr, $($fmt:expr),+) => {{ + use syn::spanned::*; return Err(syn::Error::new($src.span(), format!($($fmt),*))); }}; } @@ -10,7 +11,7 @@ macro_rules! syn_error { macro_rules! syn_assert { ($cond:expr, $src:expr, $($fmt:expr),+) => {{ if !$cond { - syn_error!($src, $($fmt),+); + crate::utils::syn_error!($src, $($fmt),+); } }}; } diff --git a/crates/luajit-sys/Cargo.toml b/crates/luajit-sys/Cargo.toml new file mode 100644 index 0000000..12d9bd3 --- /dev/null +++ b/crates/luajit-sys/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "luajit-sys" +version = "0.1.0" +edition = "2024" + +[lib] +path = "lib.rs" + +[dependencies] + +[features] +default = ["jit", "ffi", "lua52"] +runtime = [] +jit = [] +ffi = [] +unwind = [] +bundled-alloc = [] +lua52 = [] + +[build-dependencies] +bindgen = "0.71.1" +cc = "1.2.26" +which = "8.0.0" diff --git a/crates/luajit/build.rs b/crates/luajit-sys/build.rs similarity index 71% rename from crates/luajit/build.rs rename to crates/luajit-sys/build.rs index 8049b5b..4aedb51 100644 --- a/crates/luajit/build.rs +++ b/crates/luajit-sys/build.rs @@ -7,23 +7,28 @@ use std::{ }; use which::which; +/// Unwraps a Result and panics in a way that prints a nicer error message to the user. macro_rules! panic_err { - ($value:expr) => { $value.unwrap_or_else(|err| panic!("{err}")) }; - ($value:expr, $($arg:expr)+) => { $value.unwrap_or_else(|err| panic!("{}: {err}", format_args!($($arg),+))) }; + ($value:expr) => {{ $value.unwrap_or_else(|err| panic!("{err}")) }}; + ($value:expr, $($arg:expr)+) => {{ $value.unwrap_or_else(|err| panic!("{}: {err}", format_args!($($arg),+))) }}; } +/// Formats arguments in a way that is intended to address environment variables. macro_rules! env_name { - ($($arg:expr),+) => { format!($($arg),+).replace("-", "_").to_uppercase() }; + ($($arg:expr),+) => {{ format!($($arg),+).replace("-", "_").to_uppercase() }}; } +/// Expands to the value of an environment variable. macro_rules! env { ($($arg:expr),+) => { panic_err!(env::var(env_name!($($arg),+))) }; } +/// Expands to the value of a cargo configuration environment variable. macro_rules! cfg { ($($arg:expr),+) => { env!("CARGO_CFG_{}", format_args!($($arg),+)) }; } +/// Whether a cargo feature is enabled. macro_rules! feature { ($($arg:expr),+) => { env::var_os(env_name!("CARGO_FEATURE_{}", format_args!($($arg),+))).is_some() }; } @@ -38,8 +43,9 @@ fn main() { ); panic_err!(write(out_path.join(".relver"), get_relver())); - println!("cargo::rerun-if-changed={}", src_path.display()); + println!("cargo::rerun-if-changed={}", src_path.display()); // rerun if luajit source changed + // NOTE: we cannot build bindings without building luajit first (luajit generates some headers) build_runtime(&out_path.join("src")); build_bindings(&out_path.join("src")); } @@ -49,7 +55,7 @@ fn get_relver() -> String { Command::new("git") .args(["show", "-s", "--format=%ct"]) .output(), - "failed to obtain release version (is the Git command available?)" + "failed to obtain luajit release version (is the Git command available?)" ); String::from_utf8_lossy(&out.stdout).into() @@ -76,6 +82,7 @@ fn copy_recursive(src: impl AsRef, dst: impl AsRef) -> io::Result<() } fn find_make() -> Command { + // always use gnu make on bsds let name = match parse_target(&env!("HOST")) { (_, _, "freebsd" | "openbsd" | "netbsd" | "dragonfly", _) => "gmake", _ => "make", @@ -100,24 +107,16 @@ fn parse_target(target: &str) -> (&str, &str, &str, Option<&str>) { } fn build_runtime(src_path: &Path) { - let mut make = find_make(); - let opt_level = env!("OPT_LEVEL"); - let debug = opt_level == "0"; - - let host = env!("HOST"); + let host = env!("HOST"); // host triplet let host_cc = find_cc(&host).to_str().unwrap().to_owned(); let host_ptr_width = 8 * mem::size_of::(); - let target = env!("TARGET"); + let target = env!("TARGET"); // target triplet let target_cc = find_cc(&target).to_str().unwrap().to_owned(); // let target_features: HashSet<&str> = HashSet::from_iter(cfg!("target_feature").split(",")); let target_ptr_width: usize = cfg!("target_pointer_width").parse().unwrap(); - if target == host { - println!("--- begin compile for {target}:"); - } else { - println!("--- begin cross-compile on {host} for {target}:"); - } + let mut make = find_make(); make.current_dir(&src_path) .env_clear() @@ -126,28 +125,39 @@ fn build_runtime(src_path: &Path) { .env("MAKEFLAGS", env!("CARGO_MAKEFLAGS")) .env("BUILDMODE", "static"); - let ccopt = vec![format!("-O{opt_level} -fomit-frame-pointer")]; + let ccopt = vec![format!("-O{} -fomit-frame-pointer", env!("OPT_LEVEL"))]; // propagate opt_level + let mut ccdebug = vec![]; + let mut xcflags = vec![]; - let mut xcflags = vec![ - "-DLUAJIT_ENABLE_LUA52COMPAT", // lua 5.2 compatibility - "-DLUAJIT_USE_SYSMALLOC", // disable bundled allocator - "-DLUAJIT_UNWIND_EXTERNAL -funwind-tables", // use external frame unwinding - ]; - - if debug { - make.env("CCDEBUG", "-g"); // generate debug information + if env!("OPT_LEVEL") == "0" { + ccdebug.push("-g"); // generate debug information xcflags.push("-DLUAJIT_USE_GDBJIT"); // gdb support xcflags.push("-DLUA_USE_APICHECK -DLUA_USE_ASSERT"); // enable assertions } - if !feature!("enable-jit") { + xcflags.push(if feature!("unwind") { + "-DLUAJIT_UNWIND_EXTERNAL -funwind-tables" // external frame unwinding (C++ exceptions) + } else { + "-DLUAJIT_UNWIND_INTERNAL" // internal frame unwinding (setjmp/longjmp) + }); + + if !feature!("jit") { xcflags.push("-DLUAJIT_DISABLE_JIT"); } - if !feature!("enable-ffi") { + if !feature!("ffi") { xcflags.push("-DLUAJIT_DISABLE_FFI"); } + if !feature!("bundled-alloc") { + xcflags.push("-DLUAJIT_USE_SYSMALLOC"); // using system malloc disables the bundled allocator + } + + if feature!("lua52") { + xcflags.push("-DLUAJIT_ENABLE_LUA52COMPAT"); + } + + // host toolchain config match (host_ptr_width, target_ptr_width) { (64, 64) | (32, 32) => make.env("HOST_CC", &host_cc), (64, 32) => make.env("HOST_CC", format!("{} -m32", host_cc)), @@ -155,6 +165,7 @@ fn build_runtime(src_path: &Path) { (n, _) => panic!("unsupported {n}-bit architecture"), }; + // target system config match cfg!("target_os").as_str() { "linux" | "android" => make.env("TARGET_SYS", "Linux"), "windows" => make.env("TARGET_SYS", "Windows"), @@ -173,6 +184,7 @@ fn build_runtime(src_path: &Path) { _ => make.env("TARGET_SYS", "Other"), }; + // target toolchain config if let Some(cross) = target_cc.strip_suffix("gcc") { make.env("CC", "gcc").env("CROSS", cross); } else if let Some(cross) = target_cc.strip_suffix("clang") { @@ -183,13 +195,22 @@ fn build_runtime(src_path: &Path) { .env("CROSS", format!("{}/", path.parent().unwrap().display())); } + // propagate linker config if let Some(path) = env::var("RUSTC_LINKER").ok() { make.env("TARGET_LD", panic_err!(which(path), "failed to find ld")); } make.env("CCOPT", ccopt.join(" ")) + .env("CCDEBUG", ccdebug.join(" ")) .env("XCFLAGS", xcflags.join(" ")); + if target == host { + println!("--- begin compile for {target}:"); + } else { + println!("--- begin cross-compile on {host} for {target}:"); + } + + println!("{make:?}"); let status = panic_err!(make.status(), "failed to execute make"); (!status.success()).then(|| panic!("failed to compile luajit: {status}: {make:?}")); @@ -200,12 +221,22 @@ fn build_runtime(src_path: &Path) { } fn build_bindings(src_path: &Path) { + let mut clang_args = vec![format!("--target={}", env!("TARGET"))]; + let abi = if feature!("unwind") { + clang_args.push("-fexceptions".into()); + bindgen::Abi::CUnwind + } else { + clang_args.push("-fno-exceptions".into()); + bindgen::Abi::C + }; + let mut bindgen = bindgen::builder() - .clang_arg(format!("--target={}", env!("TARGET"))) + .clang_args(clang_args) .allowlist_item(r"^(lua|LUA).*_.+$") .formatter(bindgen::Formatter::None) .default_macro_constant_type(bindgen::MacroTypeVariation::Signed) - .generate_cstr(true); + .generate_cstr(true) + .override_abi(abi, ".*"); for header in ["lua.h", "lualib.h", "lauxlib.h", "luaconf.h", "luajit.h"] { bindgen = bindgen.header(src_path.join(header).to_str().unwrap()); @@ -213,5 +244,5 @@ fn build_bindings(src_path: &Path) { let path = src_path.join("bindgen.rs"); panic_err!(panic_err!(bindgen.generate(), "failed to generate bindings").write_to_file(&path)); - println!("cargo::rustc-env=LJ_BINDINGS={}", path.display()); + println!("cargo::rustc-env=LUAJIT_SYS_BINDGEN={}", path.display()); } diff --git a/crates/luajit-sys/lib.rs b/crates/luajit-sys/lib.rs new file mode 100644 index 0000000..86af2e0 --- /dev/null +++ b/crates/luajit-sys/lib.rs @@ -0,0 +1,99 @@ +#![allow(nonstandard_style)] +use std::{ffi::*, ptr}; + +include!(env!("LUAJIT_SYS_BINDGEN")); + +// #[cfg(all(panic = "abort", feature = "unwind"))] +// compile_error!(r#"feature "unwind" cannot be enabled if panic = "abort""#); + +// #[cfg(all(panic = "unwind", not(feature = "unwind")))] +// compile_error!(r#"feature "unwind" must be enabled if panic = "unwind""#); + +// constants not exposed by lua.h +pub const LUA_TPROTO: c_int = LUA_TTHREAD + 1; +pub const LUA_TCDATA: c_int = LUA_TTHREAD + 2; + +// macros not translated by bindgen +pub unsafe fn lua_upvalueindex(i: c_int) -> c_int { + LUA_GLOBALSINDEX - i +} + +pub unsafe fn lua_pop(L: *mut lua_State, n: c_int) { + unsafe { lua_settop(L, -n - 1) } +} + +pub unsafe fn lua_newtable(L: *mut lua_State) { + unsafe { lua_createtable(L, 0, 0) } +} + +pub unsafe fn lua_register(L: *mut lua_State, n: *const c_char, f: lua_CFunction) { + unsafe { (lua_pushcfunction(L, f), lua_setglobal(L, n)) }; +} + +pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) { + unsafe { lua_pushcclosure(L, f, 0) } +} + +pub unsafe fn lua_strlen(L: *mut lua_State, i: c_int) -> usize { + unsafe { lua_objlen(L, i) } +} + +pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int { + unsafe { (lua_type(L, n) == LUA_TFUNCTION) as c_int } +} + +pub unsafe fn lua_istable(L: *mut lua_State, n: c_int) -> c_int { + unsafe { (lua_type(L, n) == LUA_TTABLE) as c_int } +} + +pub unsafe fn lua_islightuserdata(L: *mut lua_State, n: c_int) -> c_int { + unsafe { (lua_type(L, n) == LUA_TLIGHTUSERDATA) as c_int } +} + +pub unsafe fn lua_isnil(L: *mut lua_State, n: c_int) -> c_int { + unsafe { (lua_type(L, n) == LUA_TNIL) as c_int } +} + +pub unsafe fn lua_isboolean(L: *mut lua_State, n: c_int) -> c_int { + unsafe { (lua_type(L, n) == LUA_TBOOLEAN) as c_int } +} + +pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int { + unsafe { (lua_type(L, n) == LUA_TTHREAD) as c_int } +} + +pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int { + unsafe { (lua_type(L, n) == LUA_TNONE) as c_int } +} + +pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int { + unsafe { (lua_type(L, n) <= LUA_TNIL) as c_int } +} + +pub unsafe fn lua_pushliteral(L: *mut lua_State, s: impl AsRef<[u8]>) { + unsafe { lua_pushlstring(L, s.as_ref().as_ptr().cast(), s.as_ref().len()) } +} + +pub unsafe fn lua_setglobal(L: *mut lua_State, s: *const c_char) { + unsafe { lua_setfield(L, LUA_GLOBALSINDEX, s) } +} + +pub unsafe fn lua_getglobal(L: *mut lua_State, s: *const c_char) { + unsafe { lua_getfield(L, LUA_GLOBALSINDEX, s) } +} + +pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char { + unsafe { lua_tolstring(L, i, ptr::null_mut()) } +} + +pub unsafe fn lua_open() -> *mut lua_State { + unsafe { luaL_newstate() } +} + +pub unsafe fn lua_getregistry(L: *mut lua_State) { + unsafe { lua_pushvalue(L, LUA_REGISTRYINDEX) } +} + +pub unsafe fn lua_getgccount(L: *mut lua_State) -> c_int { + unsafe { lua_gc(L, LUA_GCCOUNT, 0) } +} diff --git a/crates/luajit/src b/crates/luajit-sys/src similarity index 100% rename from crates/luajit/src rename to crates/luajit-sys/src diff --git a/crates/luajit/Cargo.toml b/crates/luajit/Cargo.toml index 3e2c0f0..92894f7 100644 --- a/crates/luajit/Cargo.toml +++ b/crates/luajit/Cargo.toml @@ -3,22 +3,13 @@ name = "luajit" version = "0.1.0" edition = "2024" -[lib] -path = "lib.rs" - [features] -default = ["enable-jit", "enable-ffi"] -runtime = [] -enable-jit = [] -enable-ffi = [] +runtime = ["luajit-sys/runtime"] +unwind = ["luajit-sys/unwind"] [dependencies] bitflags = { version = "2.9.1", features = ["std"] } bstr = "1.12.0" luaffi = { version = "0.1.0", path = "../luaffi" } +luajit-sys = { version = "0.1.0", path = "../luajit-sys" } thiserror = "2.0.12" - -[build-dependencies] -bindgen = "0.71.1" -cc = "1.2.26" -which = "8.0.0" diff --git a/crates/luajit/lib.rs b/crates/luajit/src/lib.rs similarity index 90% rename from crates/luajit/lib.rs rename to crates/luajit/src/lib.rs index e8d1a71..e019ebb 100644 --- a/crates/luajit/lib.rs +++ b/crates/luajit/src/lib.rs @@ -2,6 +2,7 @@ use bitflags::bitflags; use bstr::{BStr, BString, ByteSlice}; use luaffi::future::lua_pollable; +use luajit_sys::*; use std::{ alloc::{Layout, alloc, dealloc, realloc}, ffi::{CString, NulError}, @@ -16,131 +17,79 @@ use std::{ }; use thiserror::Error; -include!(env!("LJ_BINDINGS")); - -// constants not exposed by lua.h -pub const LUA_TPROTO: c_int = LUA_TTHREAD + 1; -pub const LUA_TCDATA: c_int = LUA_TTHREAD + 2; - -// macros not translated by bindgen -pub unsafe fn lua_upvalueindex(i: c_int) -> c_int { - LUA_GLOBALSINDEX - i -} - -pub unsafe fn lua_pop(L: *mut lua_State, n: c_int) { - unsafe { lua_settop(L, -n - 1) } -} - -pub unsafe fn lua_newtable(L: *mut lua_State) { - unsafe { lua_createtable(L, 0, 0) } -} - -pub unsafe fn lua_register(L: *mut lua_State, n: *const c_char, f: lua_CFunction) { - unsafe { (lua_pushcfunction(L, f), lua_setglobal(L, n)) }; -} - -pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) { - unsafe { lua_pushcclosure(L, f, 0) } -} - -pub unsafe fn lua_strlen(L: *mut lua_State, i: c_int) -> usize { - unsafe { lua_objlen(L, i) } -} - -pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int { - unsafe { (lua_type(L, n) == LUA_TFUNCTION) as c_int } -} - -pub unsafe fn lua_istable(L: *mut lua_State, n: c_int) -> c_int { - unsafe { (lua_type(L, n) == LUA_TTABLE) as c_int } -} - -pub unsafe fn lua_islightuserdata(L: *mut lua_State, n: c_int) -> c_int { - unsafe { (lua_type(L, n) == LUA_TLIGHTUSERDATA) as c_int } -} - -pub unsafe fn lua_isnil(L: *mut lua_State, n: c_int) -> c_int { - unsafe { (lua_type(L, n) == LUA_TNIL) as c_int } -} - -pub unsafe fn lua_isboolean(L: *mut lua_State, n: c_int) -> c_int { - unsafe { (lua_type(L, n) == LUA_TBOOLEAN) as c_int } -} - -pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int { - unsafe { (lua_type(L, n) == LUA_TTHREAD) as c_int } -} - -pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int { - unsafe { (lua_type(L, n) == LUA_TNONE) as c_int } -} - -pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int { - unsafe { (lua_type(L, n) <= LUA_TNIL) as c_int } -} - -pub unsafe fn lua_pushliteral(L: *mut lua_State, s: impl AsRef<[u8]>) { - unsafe { lua_pushlstring(L, s.as_ref().as_ptr().cast(), s.as_ref().len()) } -} - -pub unsafe fn lua_setglobal(L: *mut lua_State, s: *const c_char) { - unsafe { lua_setfield(L, LUA_GLOBALSINDEX, s) } -} - -pub unsafe fn lua_getglobal(L: *mut lua_State, s: *const c_char) { - unsafe { lua_getfield(L, LUA_GLOBALSINDEX, s) } -} - -pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char { - unsafe { lua_tolstring(L, i, ptr::null_mut()) } -} - -pub unsafe fn lua_open() -> *mut lua_State { - unsafe { luaL_newstate() } -} - -pub unsafe fn lua_getregistry(L: *mut lua_State) { - unsafe { lua_pushvalue(L, LUA_REGISTRYINDEX) } -} - -pub unsafe fn lua_getgccount(L: *mut lua_State) -> c_int { - unsafe { lua_gc(L, LUA_GCCOUNT, 0) } -} - +/// LuaJIT error. #[derive(Debug, Error)] +#[non_exhaustive] pub enum Error { + /// Out of memory error. #[error("out of memory")] OutOfMemory, + /// Lua syntax error returned by [`Stack::load`]. #[error("{msg}")] - Syntax { chunk: BString, msg: BString }, + Syntax { + /// Content of the chunk which had errors. + chunk: BString, + /// Lua error message. + msg: BString, + }, + /// Lua chunk name error returned by [`Stack::load`]. #[error("bad chunk name: {0}")] BadChunkName(NulError), + /// Lua error returned by [`Stack::call`]. #[error("{msg}")] - Call { msg: BString }, + Call { + /// Lua error message. + msg: BString, + }, + /// Lua error returned by [`Stack::resume`]. #[error("{msg}")] - Resume { msg: BString, trace: BString }, + Resume { + /// Lua error message. + msg: BString, + /// Lua stack trace. + trace: BString, + }, + /// Type mismatch type error. #[error("{0} expected, got {1}")] - InvalidType(&'static str, &'static str), + InvalidType( + /// The expected type. + &'static str, + /// The actual type. + &'static str, + ), + /// Invalid UTF-8 string error. #[error("{0}")] InvalidUtf8(#[from] Utf8Error), } +/// Lua type. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] pub enum Type { + /// `nil` type. #[default] Nil, + /// `boolean` type. Boolean, + /// `lightuserdata` type. Lightuserdata, + /// `number` type. Number, + /// `string` type. String, + /// `table` type. Table, + /// `function` type. Function, + /// `userdata` type. Userdata, + /// `thread` type. Thread, + /// `cdata` type. Cdata, } impl Type { + /// Converts a raw Lua type code to [`Type`], returning [`None`] if the value is invalid. pub fn from_code(code: c_int) -> Option { Some(match code { LUA_TNIL => Self::Nil, @@ -157,6 +106,7 @@ impl Type { }) } + /// Name of this type, like `"nil"` or `"string"`. pub fn name(&self) -> &'static str { match self { Self::Nil => "nil", @@ -179,15 +129,20 @@ impl fmt::Display for Type { } } +/// Lua thread status. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] pub enum Status { + /// Thread is alive (currently running or can run a function). #[default] Normal, + /// Thread terminated with an error and cannot be resumed. Dead, + /// Thread suspended with `coroutine.yield(...)` and is awaiting resume. Suspended, } impl Status { + /// Converts a raw Lua status code to [`Status`], returning [`None`] if the value is invalid. pub fn from_code(code: c_int) -> Option { Some(match code { LUA_OK => Self::Normal, @@ -197,6 +152,7 @@ impl Status { }) } + /// Name of this status, like `"normal"` or `"dead"`. pub fn name(&self) -> &'static str { match self { Status::Normal => "normal", @@ -212,14 +168,18 @@ impl fmt::Display for Status { } } +/// Result of [`Stack::resume`]. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum ResumeStatus { + /// Thread returned successfully and can run another function. #[default] Ok, + /// Thread suspended with `coroutine.yield(...)` and is awaiting resume. Suspended, } impl ResumeStatus { + /// Name of this status, like `"ok"` or `"suspended"`. pub fn name(&self) -> &'static str { match self { ResumeStatus::Ok => "ok", @@ -722,7 +682,10 @@ impl Stack { let value = self.index(1); let ptr = value.cdata::().cast_mut(); if ptr.is_null() { - return Err(Error::InvalidType("cdata", value.type_of().name())); + return Err(Error::InvalidType( + "cdata", + value.type_of().name(), + )); } else { unsafe { (&mut *ptr).await } narg = 1; diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 0000000..0ddfeb6 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,2 @@ +#!/bin/sh +cargo build "$@" diff --git a/scripts/doc.sh b/scripts/doc.sh new file mode 100755 index 0000000..54d7ed5 --- /dev/null +++ b/scripts/doc.sh @@ -0,0 +1,2 @@ +#!/bin/sh +cargo doc --all --no-deps "$@"