From c322e028e2201fbb12a77caceb1dc97b1822ae54 Mon Sep 17 00:00:00 2001 From: Alex Orlenko Date: Sat, 19 Feb 2022 14:15:15 +0000 Subject: [PATCH] Initial Luau support --- .github/workflows/main.yml | 8 +- Cargo.toml | 4 +- build/find_vendored.rs | 2 +- src/ffi/compat53.rs | 223 +++++++++++++++++++++++++++++++------ src/ffi/lauxlib.rs | 9 ++ src/ffi/lua.rs | 33 +++++- src/ffi/lualib.rs | 3 + src/ffi/luau/lauxlib.rs | 188 +++++++++++++++++++++++++++++++ src/ffi/luau/lua.rs | 59 ++++++---- src/ffi/luau/luacode.rs | 22 ++++ src/ffi/luau/lualib.rs | 84 +------------- src/ffi/luau/mod.rs | 4 + src/ffi/mod.rs | 26 +++-- src/function.rs | 7 +- src/lib.rs | 2 + src/lua.rs | 125 +++++++++++++++++---- src/scope.rs | 42 ++++--- src/stdlib.rs | 13 ++- src/types.rs | 6 +- src/userdata.rs | 12 +- src/util.rs | 64 ++++++++++- tests/async.rs | 4 +- tests/function.rs | 1 + tests/hooks.rs | 2 + tests/memory.rs | 7 +- tests/tests.rs | 24 ++-- tests/thread.rs | 3 +- tests/userdata.rs | 13 ++- 28 files changed, 762 insertions(+), 228 deletions(-) create mode 100644 src/ffi/luau/lauxlib.rs create mode 100644 src/ffi/luau/luacode.rs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 22ba395..e287d35 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,7 +9,7 @@ jobs: matrix: os: [ubuntu-20.04, macos-latest, windows-latest] rust: [stable] - lua: [lua54, lua53, lua52, lua51, luajit] + lua: [lua54, lua53, lua52, lua51, luajit, luau] include: - os: ubuntu-20.04 target: x86_64-unknown-linux-gnu @@ -108,7 +108,7 @@ jobs: matrix: os: [ubuntu-20.04, macos-latest, windows-latest] rust: [stable, nightly] - lua: [lua54, lua53, lua52, lua51, luajit, luajit52] + lua: [lua54, lua53, lua52, lua51, luajit, luajit52, luau] include: - os: ubuntu-20.04 target: x86_64-unknown-linux-gnu @@ -144,7 +144,7 @@ jobs: matrix: os: [ubuntu-20.04] rust: [nightly] - lua: [lua54, lua53, lua52, lua51, luajit] + lua: [lua54, lua53, lua52, lua51, luajit, luau] include: - os: ubuntu-20.04 target: x86_64-unknown-linux-gnu @@ -228,7 +228,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - lua: [lua54, lua53, lua52, lua51, luajit] + lua: [lua54, lua53, lua52, lua51, luajit, luau] steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 diff --git a/Cargo.toml b/Cargo.toml index 076188c..61e6e19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ lua52 = [] lua51 = [] luajit = [] luajit52 = ["luajit"] -luau = ["luau-src"] +luau = ["luau0-src"] vendored = ["lua-src", "luajit-src"] module = ["mlua_derive"] async = ["futures-core", "futures-task", "futures-util"] @@ -59,7 +59,7 @@ cc = { version = "1.0" } pkg-config = { version = "0.3.17" } lua-src = { version = ">= 540.0.0, < 550.0.0", optional = true } luajit-src = { version = ">= 210.3.1, < 220.0.0", optional = true } -luau-src = { version = "1", optional = true } +luau0-src = { version = "1", optional = true, git = "https://github.com/khvzak/luau-src-rs.git" } [dev-dependencies] rustyline = "9.0" diff --git a/build/find_vendored.rs b/build/find_vendored.rs index 4d0d442..d516dfd 100644 --- a/build/find_vendored.rs +++ b/build/find_vendored.rs @@ -18,7 +18,7 @@ pub fn probe_lua() -> Option { builder.build() }; #[cfg(feature = "luau")] - let artifacts = luau_src::Build::new().build(); + let artifacts = luau0_src::Build::new().build(); #[cfg(not(feature = "module"))] artifacts.print_cargo_metadata(); diff --git a/src/ffi/compat53.rs b/src/ffi/compat53.rs index f213e16..8d0fdba 100644 --- a/src/ffi/compat53.rs +++ b/src/ffi/compat53.rs @@ -17,6 +17,9 @@ use super::lua52::*; #[cfg(any(feature = "lua51", feature = "luajit"))] use super::lua51::*; +#[cfg(feature = "luau")] +use super::luau::*; + unsafe fn compat53_reverse(L: *mut lua_State, mut a: c_int, mut b: c_int) { while a < b { lua_pushvalue(L, a); @@ -31,6 +34,7 @@ unsafe fn compat53_reverse(L: *mut lua_State, mut a: c_int, mut b: c_int) { const COMPAT53_LEVELS1: c_int = 12; // size of the first part of the stack const COMPAT53_LEVELS2: c_int = 10; // size of the second part of the stack +#[cfg(not(feature = "luau"))] unsafe fn compat53_countlevels(L: *mut lua_State) -> c_int { let mut ar: lua_Debug = mem::zeroed(); let (mut li, mut le) = (1, 1); @@ -51,6 +55,27 @@ unsafe fn compat53_countlevels(L: *mut lua_State) -> c_int { le - 1 } +#[cfg(feature = "luau")] +unsafe fn compat53_countlevels(L: *mut lua_State) -> c_int { + let mut ar: lua_Debug = mem::zeroed(); + let (mut li, mut le) = (1, 1); + // find an upper bound + while lua_getinfo(L, le, cstr!(""), &mut ar) != 0 { + li = le; + le *= 2; + } + // do a binary search + while li < le { + let m = (li + le) / 2; + if lua_getinfo(L, m, cstr!(""), &mut ar) != 0 { + li = m + 1; + } else { + le = m; + } + } + le - 1 +} + unsafe fn compat53_checkmode( L: *mut lua_State, mode: *const c_char, @@ -81,7 +106,7 @@ unsafe fn compat53_checkmode( LUA_OK } -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) -> c_int { if level == 0 || lua_istable(L, -1) == 0 { return 0; // not found @@ -110,10 +135,18 @@ unsafe fn compat53_findfield(L: *mut lua_State, objidx: c_int, level: c_int) -> return 0; // not found } -#[cfg(any(feature = "lua51", feature = "luajit"))] -unsafe fn compat53_pushglobalfuncname(L: *mut lua_State, ar: *mut lua_Debug) -> c_int { +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] +unsafe fn compat53_pushglobalfuncname( + L: *mut lua_State, + level: c_int, + ar: *mut lua_Debug, +) -> c_int { let top = lua_gettop(L); - lua_getinfo(L, cstr!("f"), ar); // push function + // push function + #[cfg(not(feature = "luau"))] + lua_getinfo(L, cstr!("f"), ar); + #[cfg(feature = "luau")] + lua_getinfo(L, level, cstr!("f"), ar); lua_pushvalue(L, LUA_GLOBALSINDEX); if compat53_findfield(L, top + 1, 2) != 0 { lua_copy(L, -1, top + 1); // move name to proper place @@ -125,6 +158,34 @@ unsafe fn compat53_pushglobalfuncname(L: *mut lua_State, ar: *mut lua_Debug) -> } } +#[cfg(feature = "luau")] +unsafe fn compat53_pushfuncname(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) { + /* + if *(*ar).namewhat != b'\0' as c_char { + // is there a name? + lua_pushfstring(L, cstr!("function '%s'"), (*ar).name); + } else + */ + if *(*ar).what == b'm' as c_char { + // main? + lua_pushliteral(L, "main chunk"); + } else if *(*ar).what == b'C' as c_char { + if compat53_pushglobalfuncname(L, level, ar) != 0 { + lua_pushfstring(L, cstr!("function '%s'"), lua_tostring(L, -1)); + lua_remove(L, -2); // remove name + } else { + lua_pushliteral(L, "?"); + } + } else { + lua_pushfstring( + L, + cstr!("function <%s:%d>"), + (*ar).short_src.as_ptr(), + (*ar).linedefined, + ); + } +} + #[cfg(any(feature = "lua51", feature = "luajit"))] unsafe fn compat53_pushfuncname(L: *mut lua_State, ar: *mut lua_Debug) { if *(*ar).namewhat != b'\0' as c_char { @@ -134,7 +195,7 @@ unsafe fn compat53_pushfuncname(L: *mut lua_State, ar: *mut lua_Debug) { // main? lua_pushliteral(L, "main chunk"); } else if *(*ar).what == b'C' as c_char { - if compat53_pushglobalfuncname(L, ar) != 0 { + if compat53_pushglobalfuncname(L, -1, ar) != 0 { lua_pushfstring(L, cstr!("function '%s'"), lua_tostring(L, -1)); lua_remove(L, -2); // remove name } else { @@ -184,22 +245,22 @@ pub unsafe fn lua_absindex(L: *mut lua_State, mut idx: c_int) -> c_int { } // Comparison and arithmetic functions -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] pub const LUA_OPADD: c_int = 0; -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] pub const LUA_OPSUB: c_int = 1; -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] pub const LUA_OPMUL: c_int = 2; -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] pub const LUA_OPDIV: c_int = 3; -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] pub const LUA_OPMOD: c_int = 4; -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] pub const LUA_OPPOW: c_int = 5; -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] pub const LUA_OPUNM: c_int = 6; -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] static COMPAT53_ARITH_CODE: &str = r#" local op,a,b = ... if op == 0 then return a+b @@ -212,7 +273,7 @@ elseif op == 6 then return -a end "#; -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] pub unsafe fn lua_arith(L: *mut lua_State, op: c_int) { #[allow(clippy::manual_range_contains)] if op < LUA_OPADD || op > LUA_OPUNM { @@ -242,7 +303,7 @@ pub unsafe fn lua_rotate(L: *mut lua_State, mut idx: c_int, mut n: c_int) { } } -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] #[inline(always)] pub unsafe fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int) { let abs_to = lua_absindex(L, toidx); @@ -263,6 +324,12 @@ pub unsafe fn lua_isinteger(L: *mut lua_State, idx: c_int) -> c_int { return 0; } +#[cfg(any( + feature = "lua52", + feature = "lua51", + feature = "luajit", + feature = "luau" +))] #[inline(always)] pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer { lua_tointegerx(L, i, ptr::null_mut()) @@ -284,6 +351,12 @@ pub unsafe fn lua_tonumberx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> l // Implemented for Lua 5.2 as well // See https://github.com/keplerproject/lua-compat-5.3/issues/40 +#[cfg(any( + feature = "lua52", + feature = "lua51", + feature = "luajit", + feature = "luau" +))] #[inline(always)] pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> lua_Integer { let mut ok = 0; @@ -301,20 +374,20 @@ pub unsafe fn lua_tointegerx(L: *mut lua_State, i: c_int, isnum: *mut c_int) -> return 0; } -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] #[inline(always)] pub unsafe fn lua_rawlen(L: *mut lua_State, idx: c_int) -> usize { lua_objlen(L, idx) } -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] pub const LUA_OPEQ: c_int = 0; -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] pub const LUA_OPLT: c_int = 1; -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] pub const LUA_OPLE: c_int = 2; -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] #[inline(always)] pub unsafe fn lua_compare(L: *mut lua_State, mut idx1: c_int, mut idx2: c_int, op: c_int) -> c_int { match op { @@ -335,7 +408,7 @@ pub unsafe fn lua_compare(L: *mut lua_State, mut idx1: c_int, mut idx2: c_int, o } } -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] #[inline(always)] pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> *const c_char { if l == 0 { @@ -356,7 +429,7 @@ pub unsafe fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize) -> } } -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] #[inline(always)] pub unsafe fn lua_pushstring(L: *mut lua_State, s: *const c_char) -> *const c_char { lua_pushstring_(L, s); @@ -401,7 +474,7 @@ pub unsafe fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: lua_Integer) -> c_in lua_type(L, -1) } -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] #[inline(always)] pub unsafe fn lua_rawgetp(L: *mut lua_State, idx: c_int, p: *const c_void) -> c_int { let abs_i = lua_absindex(L, idx); @@ -424,6 +497,22 @@ pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int { lua_type(L, -1) } +#[cfg(feature = "luau")] +#[inline(always)] +pub unsafe fn lua_getuservalue(L: *mut lua_State, mut idx: c_int) -> c_int { + luaL_checkstack(L, 2, cstr!("not enough stack slots available")); + idx = lua_absindex(L, idx); + lua_pushliteral(L, "__mlua_uservalues"); + lua_rawget(L, LUA_REGISTRYINDEX); + if lua_istable(L, -1) == 0 { + return LUA_TNIL; + } + lua_pushvalue(L, idx); + lua_rawget(L, -2); + lua_remove(L, -2); + lua_type(L, -1) +} + #[cfg(feature = "lua52")] #[inline(always)] pub unsafe fn lua_getuservalue(L: *mut lua_State, idx: c_int) -> c_int { @@ -449,7 +538,7 @@ pub unsafe fn lua_rawseti(L: *mut lua_State, idx: c_int, n: lua_Integer) { ) } -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] #[inline(always)] pub unsafe fn lua_rawsetp(L: *mut lua_State, idx: c_int, p: *const c_void) { let abs_i = lua_absindex(L, idx); @@ -466,6 +555,34 @@ pub unsafe fn lua_setuservalue(L: *mut lua_State, idx: c_int) { lua_setfenv(L, idx); } +#[cfg(feature = "luau")] +#[inline(always)] +pub unsafe fn lua_setuservalue(L: *mut lua_State, mut idx: c_int) { + luaL_checkstack(L, 4, cstr!("not enough stack slots available")); + idx = lua_absindex(L, idx); + lua_pushliteral(L, "__mlua_uservalues"); + lua_pushvalue(L, -1); + lua_rawget(L, LUA_REGISTRYINDEX); + if lua_istable(L, -1) == 0 { + lua_pop(L, 1); + lua_createtable(L, 0, 2); // main table + lua_createtable(L, 0, 1); // metatable + lua_pushliteral(L, "k"); + lua_setfield(L, -2, cstr!("__mode")); + lua_setmetatable(L, -2); + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); + } + lua_replace(L, -2); + lua_pushvalue(L, idx); + lua_pushvalue(L, -3); + lua_remove(L, -4); + lua_rawset(L, -3); + lua_pop(L, 1); +} + +#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] #[inline(always)] pub unsafe fn lua_dump( L: *mut lua_State, @@ -476,7 +593,7 @@ pub unsafe fn lua_dump( lua_dump_(L, writer, data) } -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] #[inline(always)] pub unsafe fn lua_len(L: *mut lua_State, idx: c_int) { match lua_type(L, idx) { @@ -565,7 +682,7 @@ pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void { return _ptr; } -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] #[inline(always)] pub unsafe fn lua_pushglobaltable(L: *mut lua_State) { lua_pushvalue(L, LUA_GLOBALSINDEX); @@ -575,7 +692,7 @@ pub unsafe fn lua_pushglobaltable(L: *mut lua_State) { // lauxlib ported functions // -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] #[inline(always)] pub unsafe fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char) { if lua_checkstack(L, sz + LUA_MINSTACK) == 0 { @@ -628,7 +745,7 @@ pub unsafe fn luaL_loadbufferx( luaL_loadbuffer(L, buff, sz, name) } -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] #[inline(always)] pub unsafe fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer { let mut isnum = 0; @@ -682,6 +799,46 @@ pub unsafe fn luaL_traceback( lua_concat(L, lua_gettop(L) - top); } +#[cfg(feature = "luau")] +pub unsafe fn luaL_traceback( + L: *mut lua_State, + L1: *mut lua_State, + msg: *const c_char, + mut level: c_int, +) { + let mut ar: lua_Debug = mem::zeroed(); + let top = lua_gettop(L); + let numlevels = compat53_countlevels(L1); + let mark = if numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2 { + COMPAT53_LEVELS1 + } else { + 0 + }; + + if !msg.is_null() { + lua_pushfstring(L, cstr!("%s\n"), msg); + } + lua_pushliteral(L, "stack traceback:"); + while lua_getinfo(L1, level, cstr!(""), &mut ar) != 0 { + level += 1; + if level == mark { + // too many levels? + lua_pushliteral(L, "\n\t..."); // add a '...' + level = numlevels - COMPAT53_LEVELS2; // and skip to last ones + } else { + lua_getinfo(L1, level - 1, cstr!("sln"), &mut ar); + lua_pushfstring(L, cstr!("\n\t%s:"), ar.short_src.as_ptr()); + if ar.currentline > 0 { + lua_pushfstring(L, cstr!("%d:"), ar.currentline); + } + lua_pushliteral(L, " in "); + compat53_pushfuncname(L, level - 1, &mut ar); + lua_concat(L, lua_gettop(L) - top); + } + } + lua_concat(L, lua_gettop(L) - top); +} + pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char { if luaL_callmeta(L, idx, cstr!("__tostring")) == 0 { let t = lua_type(L, idx); @@ -718,7 +875,7 @@ pub unsafe fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> lua_tolstring(L, -1, len) } -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] #[inline(always)] pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) { luaL_checkstack(L, 1, cstr!("not enough stack slots")); @@ -726,7 +883,7 @@ pub unsafe fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char) { lua_setmetatable(L, -2); } -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] #[inline(always)] pub unsafe fn luaL_testudata(L: *mut lua_State, i: c_int, tname: *const c_char) -> *mut c_void { let mut p = lua_touserdata(L, i); @@ -762,7 +919,7 @@ pub unsafe fn luaL_setfuncs(L: *mut lua_State, mut l: *const luaL_Reg, nup: c_in lua_pop(L, nup); // remove upvalues } -#[cfg(any(feature = "lua51", feature = "luajit"))] +#[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] pub unsafe fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int { let abs_i = lua_absindex(L, idx); luaL_checkstack(L, 3, cstr!("not enough stack slots")); @@ -791,7 +948,7 @@ pub unsafe fn luaL_requiref( lua_pop(L, 1); lua_pushcfunction(L, openf); lua_pushstring(L, modname); - #[cfg(any(feature = "lua52", feature = "lua51"))] + #[cfg(any(feature = "lua52", feature = "lua51", feature = "luau"))] { lua_call(L, 1, 1); lua_pushvalue(L, -1); @@ -803,7 +960,7 @@ pub unsafe fn luaL_requiref( lua_getfield(L, -1, modname); } } - if cfg!(any(feature = "lua52", feature = "lua51")) && glb != 0 { + if cfg!(any(feature = "lua52", feature = "lua51", feature = "luau")) && glb != 0 { lua_pushvalue(L, -1); lua_setglobal(L, modname); } diff --git a/src/ffi/lauxlib.rs b/src/ffi/lauxlib.rs index 2371edb..a543a7d 100644 --- a/src/ffi/lauxlib.rs +++ b/src/ffi/lauxlib.rs @@ -14,6 +14,9 @@ pub use super::lua52::lauxlib::*; #[cfg(any(feature = "lua51", feature = "luajit"))] pub use super::lua51::lauxlib::*; +#[cfg(feature = "luau")] +pub use super::luau::lauxlib::*; + #[cfg(feature = "lua52")] pub use super::compat53::{luaL_getmetafield, luaL_newmetatable, luaL_requiref, luaL_tolstring}; @@ -24,6 +27,12 @@ pub use super::compat53::{ luaL_tolstring, luaL_traceback, }; +#[cfg(feature = "luau")] +pub use super::compat53::{ + luaL_checkstack, luaL_getmetafield, luaL_getsubtable, luaL_len, luaL_newmetatable, + luaL_requiref, luaL_setmetatable, luaL_testudata, luaL_tolstring, luaL_traceback, +}; + // I believe `luaL_traceback` < 5.4 requires this much free stack to not error. // 5.4 uses `luaL_Buffer` pub const LUA_TRACEBACK_STACK: c_int = 11; diff --git a/src/ffi/lua.rs b/src/ffi/lua.rs index 53af3a2..d918443 100644 --- a/src/ffi/lua.rs +++ b/src/ffi/lua.rs @@ -14,6 +14,9 @@ pub use super::lua52::lua::*; #[cfg(any(feature = "lua51", feature = "luajit"))] pub use super::lua51::lua::*; +#[cfg(feature = "luau")] +pub use super::luau::lua::*; + #[cfg(feature = "lua52")] pub use super::compat53::{ lua_dump, lua_getextraspace, lua_getfield, lua_getglobal, lua_geti, lua_gettable, @@ -31,6 +34,14 @@ pub use super::compat53::{ lua_stringtonumber, lua_tointeger, lua_tointegerx, lua_tonumberx, }; +#[cfg(feature = "luau")] +pub use super::compat53::{ + lua_arith, lua_compare, lua_copy, lua_getextraspace, lua_getfield, lua_getglobal, lua_geti, + lua_gettable, lua_getuservalue, lua_isinteger, lua_len, lua_pushglobaltable, lua_pushlstring, + lua_pushstring, lua_rawget, lua_rawgeti, lua_rawgetp, lua_rawlen, lua_rawseti, lua_rawsetp, + lua_rotate, lua_seti, lua_setuservalue, lua_stringtonumber, lua_tointeger, lua_tointegerx, +}; + #[cfg(any(feature = "lua52", feature = "lua53", feature = "lua54",))] pub const LUA_MAX_UPVALUES: c_int = 255; @@ -40,6 +51,9 @@ pub const LUA_MAX_UPVALUES: c_int = 60; #[cfg(all(feature = "luajit", feature = "vendored"))] pub const LUA_MAX_UPVALUES: c_int = 120; +#[cfg(feature = "luau")] +pub const LUA_MAX_UPVALUES: c_int = 200; + // // Lua 5.4 compatibility layer // @@ -48,7 +62,8 @@ pub const LUA_MAX_UPVALUES: c_int = 120; feature = "lua53", feature = "lua52", feature = "lua51", - feature = "luajit" + feature = "luajit", + feature = "luau" ))] #[inline(always)] pub unsafe fn lua_resume( @@ -62,22 +77,32 @@ pub unsafe fn lua_resume( #[cfg(any(feature = "lua51", feature = "luajit"))] let ret = lua_resume_(L, narg); - #[cfg(any(feature = "lua53", feature = "lua52"))] + #[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))] let ret = lua_resume_(L, from, narg); - if ret == LUA_OK || ret == LUA_YIELD { + if (ret == LUA_OK || ret == LUA_YIELD) && !(nres.is_null()) { *nres = lua_gettop(L); } ret } -#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored")))] +#[cfg(any( + feature = "lua54", + feature = "luau", + all(feature = "luajit", feature = "vendored") +))] pub unsafe fn lua_resetthreadx(L: *mut lua_State, th: *mut lua_State) -> c_int { #[cfg(all(feature = "luajit", feature = "vendored"))] { lua_resetthread(L, th); LUA_OK } + #[cfg(feature = "luau")] + { + let _ = L; + lua_resetthread(th); + LUA_OK + } #[cfg(feature = "lua54")] { let _ = L; diff --git a/src/ffi/lualib.rs b/src/ffi/lualib.rs index 9861460..ff50d85 100644 --- a/src/ffi/lualib.rs +++ b/src/ffi/lualib.rs @@ -11,3 +11,6 @@ pub use super::lua52::lualib::*; #[cfg(any(feature = "lua51", feature = "luajit"))] pub use super::lua51::lualib::*; + +#[cfg(feature = "luau")] +pub use super::luau::lualib::*; diff --git a/src/ffi/luau/lauxlib.rs b/src/ffi/luau/lauxlib.rs new file mode 100644 index 0000000..fe7e6a8 --- /dev/null +++ b/src/ffi/luau/lauxlib.rs @@ -0,0 +1,188 @@ +//! Contains definitions from `lualib.h`. + +use std::alloc; +use std::ffi::CStr; +use std::os::raw::{c_char, c_float, c_int, c_void}; +use std::ptr; + +use super::lua::{ + self, lua_CFunction, lua_Integer, lua_Number, lua_State, lua_Unsigned, LUA_ERRSYNTAX, LUA_OK, + LUA_REGISTRYINDEX, +}; +use super::luacode; + +#[repr(C)] +pub struct luaL_Reg { + pub name: *const c_char, + pub func: lua_CFunction, +} + +extern "C" { + pub fn luaL_register(L: *mut lua_State, libname: *const c_char, l: *const luaL_Reg); + #[link_name = "luaL_getmetafield"] + pub fn luaL_getmetafield_(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int; + pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int; + #[link_name = "luaL_typeerrorL"] + pub fn luaL_typeerror(L: *mut lua_State, narg: c_int, tname: *const c_char) -> !; + #[link_name = "luaL_argerrorL"] + pub fn luaL_argerror(L: *mut lua_State, narg: c_int, extramsg: *const c_char) -> !; + pub fn luaL_checklstring(L: *mut lua_State, narg: c_int, l: *mut usize) -> *const c_char; + pub fn luaL_optlstring( + L: *mut lua_State, + narg: c_int, + def: *const c_char, + l: *mut usize, + ) -> *const c_char; + pub fn luaL_checknumber(L: *mut lua_State, narg: c_int) -> lua_Number; + pub fn luaL_optnumber(L: *mut lua_State, narg: c_int, def: lua_Number) -> lua_Number; + + pub fn luaL_checkboolean(L: *mut lua_State, narg: c_int) -> c_int; + pub fn luaL_optboolean(L: *mut lua_State, narg: c_int, def: c_int) -> c_int; + + pub fn luaL_checkinteger(L: *mut lua_State, narg: c_int) -> lua_Integer; + pub fn luaL_optinteger(L: *mut lua_State, narg: c_int, def: lua_Integer) -> lua_Integer; + pub fn luaL_checkunsigned(L: *mut lua_State, narg: c_int) -> lua_Unsigned; + pub fn luaL_optunsigned(L: *mut lua_State, narg: c_int, def: lua_Unsigned) -> lua_Unsigned; + + pub fn luaL_checkvector(L: *mut lua_State, narg: c_int) -> *const c_float; + pub fn luaL_optvector(L: *mut lua_State, narg: c_int, def: *const c_float) -> *const c_float; + + #[link_name = "luaL_checkstack"] + pub fn luaL_checkstack_(L: *mut lua_State, sz: c_int, msg: *const c_char); + pub fn luaL_checktype(L: *mut lua_State, narg: c_int, t: c_int); + pub fn luaL_checkany(L: *mut lua_State, narg: c_int); + + #[link_name = "luaL_newmetatable"] + pub fn luaL_newmetatable_(L: *mut lua_State, tname: *const c_char) -> c_int; + pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void; + + pub fn luaL_where(L: *mut lua_State, lvl: c_int); + + #[link_name = "luaL_errorL"] + pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !; + + pub fn luaL_checkoption( + L: *mut lua_State, + narg: c_int, + def: *const c_char, + lst: *const *const c_char, + ) -> c_int; + + #[link_name = "luaL_tolstring"] + pub fn luaL_tolstring_(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char; + + pub fn luaL_newstate() -> *mut lua_State; + + // TODO: luaL_findtable +} + +// +// Some useful macros (implemented as Rust functions) +// + +#[inline(always)] +pub unsafe fn luaL_argcheck(L: *mut lua_State, cond: c_int, arg: c_int, extramsg: *const c_char) { + if cond == 0 { + luaL_argerror(L, arg, extramsg); + } +} + +#[inline(always)] +pub unsafe fn luaL_argexpected(L: *mut lua_State, cond: c_int, arg: c_int, tname: *const c_char) { + if cond == 0 { + luaL_typeerror(L, arg, tname); + } +} + +#[inline(always)] +pub unsafe fn luaL_checkstring(L: *mut lua_State, n: c_int) -> *const c_char { + luaL_checklstring(L, n, ptr::null_mut()) +} + +#[inline(always)] +pub unsafe fn luaL_optstring(L: *mut lua_State, n: c_int, d: *const c_char) -> *const c_char { + luaL_optlstring(L, n, d, ptr::null_mut()) +} + +// TODO: luaL_opt + +#[inline(always)] +pub unsafe fn luaL_typename(L: *mut lua_State, i: c_int) -> *const c_char { + lua::lua_typename(L, lua::lua_type(L, i)) +} + +#[inline(always)] +pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) { + lua::lua_getfield_(L, LUA_REGISTRYINDEX, n); +} + +#[inline(always)] +pub unsafe fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int { + assert_eq!(t, LUA_REGISTRYINDEX); + let r = lua::lua_ref(L, -1); + lua::lua_pop(L, 1); + r +} + +#[inline(always)] +pub unsafe fn luaL_unref(L: *mut lua_State, t: c_int, r#ref: c_int) { + assert_eq!(t, LUA_REGISTRYINDEX); + lua::lua_unref(L, r#ref) +} + +pub unsafe fn luaL_loadbufferx( + L: *mut lua_State, + data: *const c_char, + mut size: usize, + name: *const c_char, + mode: *const c_char, +) -> c_int { + let chunk_is_text = (*data as u8) >= b'\n'; + if !mode.is_null() { + let modeb = CStr::from_ptr(mode).to_bytes(); + if !chunk_is_text && !modeb.contains(&b'b') { + lua::lua_pushfstring( + L, + cstr!("attempt to load a binary chunk (mode is '%s')"), + mode, + ); + return LUA_ERRSYNTAX; + } else if chunk_is_text && !modeb.contains(&b't') { + lua::lua_pushfstring( + L, + cstr!("attempt to load a text chunk (mode is '%s')"), + mode, + ); + return LUA_ERRSYNTAX; + } + } + + if chunk_is_text { + let data = luacode::luau_compile(data, size, ptr::null_mut(), &mut size); + let layout = alloc::Layout::from_size_align_unchecked(size, super::super::SYS_MIN_ALIGN); + let ok = lua::luau_load(L, name, data, size, 0) == 0; + alloc::dealloc(data as *mut u8, layout); + if !ok { + return LUA_ERRSYNTAX; + } + } else { + if lua::luau_load(L, name, data, size, 0) != 0 { + return LUA_ERRSYNTAX; + } + } + LUA_OK +} + +#[inline(always)] +pub unsafe fn luaL_loadbuffer( + L: *mut lua_State, + data: *const c_char, + size: usize, + name: *const c_char, +) -> c_int { + luaL_loadbufferx(L, data, size, name, ptr::null()) +} + +// +// TODO: Generic Buffer Manipulation +// diff --git a/src/ffi/luau/lua.rs b/src/ffi/luau/lua.rs index f7d37e4..9c7e361 100644 --- a/src/ffi/luau/lua.rs +++ b/src/ffi/luau/lua.rs @@ -69,6 +69,9 @@ pub type lua_Unsigned = c_uint; pub type lua_CFunction = unsafe extern "C" fn(L: *mut lua_State) -> c_int; pub type lua_Continuation = unsafe extern "C" fn(L: *mut lua_State, status: c_int) -> c_int; +/// Type for userdata destructor functions. +pub type lua_Udestructor = unsafe extern "C" fn(*mut c_void); + /// Type for memory-allocation functions. pub type lua_Alloc = unsafe extern "C" fn( L: *mut lua_State, @@ -121,14 +124,15 @@ extern "C" { pub fn lua_lessthan(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int; pub fn lua_tonumberx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Number; - pub fn lua_tointegerx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer; + #[link_name = "lua_tointegerx"] + pub fn lua_tointegerx_(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer; pub fn lua_tounsignedx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Unsigned; pub fn lua_tovector(L: *mut lua_State, idx: c_int) -> *const c_float; pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char; pub fn lua_tostringatom(L: *mut lua_State, idx: c_int, atom: *mut c_int) -> *const c_char; pub fn lua_namecallatom(L: *mut lua_State, atom: *mut c_int) -> *const c_char; - pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> c_int; + pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize; pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option; pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void; pub fn lua_touserdatatagged(L: *mut lua_State, idx: c_int, tag: c_int) -> *mut c_void; @@ -144,8 +148,10 @@ extern "C" { pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer); pub fn lua_pushunsigned(L: *mut lua_State, n: lua_Unsigned); pub fn lua_pushvector(L: *mut lua_State, x: c_float, y: c_float, z: c_float); - pub fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize); - pub fn lua_pushstring(L: *mut lua_State, s: *const c_char); + #[link_name = "lua_pushlstring"] + pub fn lua_pushlstring_(L: *mut lua_State, s: *const c_char, l: usize); + #[link_name = "lua_pushstring"] + pub fn lua_pushstring_(L: *mut lua_State, s: *const c_char); // lua_pushvfstring #[link_name = "lua_pushfstringL"] pub fn lua_pushfstring(L: *mut lua_State, fmt: *const c_char, ...) -> *const c_char; @@ -163,11 +169,15 @@ extern "C" { // // Get functions (Lua -> stack) // - pub fn lua_gettable(L: *mut lua_State, idx: c_int); - pub fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char); + #[link_name = "lua_gettable"] + pub fn lua_gettable_(L: *mut lua_State, idx: c_int); + #[link_name = "lua_getfield"] + pub fn lua_getfield_(L: *mut lua_State, idx: c_int, k: *const c_char); pub fn lua_rawgetfield(L: *mut lua_State, idx: c_int, k: *const c_char); - pub fn lua_rawget(L: *mut lua_State, idx: c_int); - pub fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: c_int); + #[link_name = "lua_rawget"] + pub fn lua_rawget_(L: *mut lua_State, idx: c_int); + #[link_name = "lua_rawgeti"] + pub fn lua_rawgeti_(L: *mut lua_State, idx: c_int, n: c_int); pub fn lua_createtable(L: *mut lua_State, narr: c_int, nrec: c_int); pub fn lua_setreadonly(L: *mut lua_State, idx: c_int, enabled: c_int); @@ -175,7 +185,7 @@ extern "C" { pub fn lua_setsafeenv(L: *mut lua_State, idx: c_int, enabled: c_int); pub fn lua_newuserdatatagged(L: *mut lua_State, sz: usize, tag: c_int) -> *mut c_void; - pub fn lua_newuserdatadtor(L: *mut lua_State, sz: usize, dtor: fn(*mut c_void)); + pub fn lua_newuserdatadtor(L: *mut lua_State, sz: usize, dtor: lua_Udestructor) -> *mut c_void; pub fn lua_getmetatable(L: *mut lua_State, objindex: c_int) -> c_int; pub fn lua_getfenv(L: *mut lua_State, idx: c_int); @@ -185,7 +195,8 @@ extern "C" { pub fn lua_settable(L: *mut lua_State, idx: c_int); pub fn lua_setfield(L: *mut lua_State, idx: c_int, k: *const c_char); pub fn lua_rawset(L: *mut lua_State, idx: c_int); - pub fn lua_rawseti(L: *mut lua_State, idx: c_int, n: c_int); + #[link_name = "lua_rawseti"] + pub fn lua_rawseti_(L: *mut lua_State, idx: c_int, n: c_int); pub fn lua_setmetatable(L: *mut lua_State, objindex: c_int) -> c_int; pub fn lua_setfenv(L: *mut lua_State, idx: c_int) -> c_int; @@ -207,7 +218,8 @@ extern "C" { // pub fn lua_yield(L: *mut lua_State, nresults: c_int) -> c_int; pub fn lua_break(L: *mut lua_State) -> c_int; - pub fn lua_resume(L: *mut lua_State, from: *mut lua_State, narg: c_int) -> c_int; + #[link_name = "lua_resume"] + pub fn lua_resume_(L: *mut lua_State, from: *mut lua_State, narg: c_int) -> c_int; pub fn lua_resumeerror(L: *mut lua_State, from: *mut lua_State) -> c_int; pub fn lua_status(L: *mut lua_State) -> c_int; pub fn lua_isyieldable(L: *mut lua_State) -> c_int; @@ -241,7 +253,7 @@ extern "C" { pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int; pub fn lua_concat(L: *mut lua_State, n: c_int); // TODO: lua_encodepointer, lua_clock - pub fn lua_setuserdatadtor(L: *mut lua_State, tag: c_int, dtor: fn(*mut c_void)); + pub fn lua_setuserdatadtor(L: *mut lua_State, tag: c_int, dtor: Option); } // @@ -258,14 +270,15 @@ extern "C" { // // Some useful macros (implemented as Rust functions) // + #[inline(always)] pub unsafe fn lua_tonumber(L: *mut lua_State, i: c_int) -> lua_Number { lua_tonumberx(L, i, ptr::null_mut()) } #[inline(always)] -pub unsafe fn lua_tointeger(L: *mut lua_State, i: c_int) -> lua_Integer { - lua_tointegerx(L, i, ptr::null_mut()) +pub unsafe fn lua_tointeger_(L: *mut lua_State, i: c_int) -> lua_Integer { + lua_tointegerx_(L, i, ptr::null_mut()) } #[inline(always)] @@ -339,14 +352,22 @@ pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int { pub unsafe fn lua_pushliteral(L: *mut lua_State, s: &'static str) { use std::ffi::CString; let c_str = CString::new(s).unwrap(); - lua_pushlstring(L, c_str.as_ptr(), c_str.as_bytes().len()) + lua_pushlstring_(L, c_str.as_ptr(), c_str.as_bytes().len()) } -pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction, debugname: *const c_char) { +pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) { + lua_pushcclosurek(L, f, ptr::null(), 0, None) +} + +pub unsafe fn lua_pushcfunctiond(L: *mut lua_State, f: lua_CFunction, debugname: *const c_char) { lua_pushcclosurek(L, f, debugname, 0, None) } -pub unsafe fn lua_pushcclosure( +pub unsafe fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, nup: c_int) { + lua_pushcclosurek(L, f, ptr::null(), nup, None) +} + +pub unsafe fn lua_pushcclosured( L: *mut lua_State, f: lua_CFunction, debugname: *const c_char, @@ -361,8 +382,8 @@ pub unsafe fn lua_setglobal(L: *mut lua_State, var: *const c_char) { } #[inline(always)] -pub unsafe fn lua_getglobal(L: *mut lua_State, var: *const c_char) { - lua_getfield(L, LUA_GLOBALSINDEX, var) +pub unsafe fn lua_getglobal_(L: *mut lua_State, var: *const c_char) { + lua_getfield_(L, LUA_GLOBALSINDEX, var) } #[inline(always)] diff --git a/src/ffi/luau/luacode.rs b/src/ffi/luau/luacode.rs new file mode 100644 index 0000000..3f24b65 --- /dev/null +++ b/src/ffi/luau/luacode.rs @@ -0,0 +1,22 @@ +//! Contains definitions from `luacode.h`. + +use std::os::raw::{c_char, c_int}; + +#[repr(C)] +pub struct lua_CompileOptions { + pub optimizationLevel: c_int, + pub debugLevel: c_int, + pub coverageLevel: c_int, + pub vectorLib: *const c_char, + pub vectorCtor: *const c_char, + pub mutableGlobals: *mut *const c_char, +} + +extern "C" { + pub fn luau_compile( + source: *const c_char, + size: usize, + options: *mut lua_CompileOptions, + outsize: *mut usize, + ) -> *mut c_char; +} diff --git a/src/ffi/luau/lualib.rs b/src/ffi/luau/lualib.rs index bde8a29..2f1ba7e 100644 --- a/src/ffi/luau/lualib.rs +++ b/src/ffi/luau/lualib.rs @@ -1,88 +1,8 @@ //! Contains definitions from `lualib.h`. -use std::os::raw::{c_char, c_float, c_int, c_void}; +use std::os::raw::c_int; -use super::lua::{ - lua_CFunction, lua_Integer, lua_Number, lua_State, lua_Unsigned, lua_getfield, - LUA_REGISTRYINDEX, -}; - -#[repr(C)] -pub struct luaL_Reg { - pub name: *const c_char, - pub func: lua_CFunction, -} - -extern "C" { - pub fn luaL_register(L: *mut lua_State, libname: *const c_char, l: *const luaL_Reg); - pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int; - pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int; - // TODO: luaL_typeerrorL, luaL_argerrorL - pub fn luaL_checklstring(L: *mut lua_State, narg: c_int, l: *mut usize) -> *const c_char; - pub fn luaL_optlstring( - L: *mut lua_State, - narg: c_int, - def: *const c_char, - l: *mut usize, - ) -> *const c_char; - pub fn luaL_checknumber(L: *mut lua_State, narg: c_int) -> lua_Number; - pub fn luaL_optnumber(L: *mut lua_State, narg: c_int, def: lua_Number) -> lua_Number; - - pub fn luaL_checkboolean(L: *mut lua_State, narg: c_int) -> c_int; - pub fn luaL_optboolean(L: *mut lua_State, narg: c_int, def: c_int) -> c_int; - - pub fn luaL_checkinteger(L: *mut lua_State, narg: c_int) -> lua_Integer; - pub fn luaL_optinteger(L: *mut lua_State, narg: c_int, def: lua_Integer) -> lua_Integer; - pub fn luaL_checkunsigned(L: *mut lua_State, narg: c_int) -> lua_Unsigned; - pub fn luaL_optunsigned(L: *mut lua_State, narg: c_int, def: lua_Unsigned) -> lua_Unsigned; - - pub fn luaL_checkvector(L: *mut lua_State, narg: c_int) -> *const c_float; - pub fn luaL_optvector(L: *mut lua_State, narg: c_int, def: *const c_float) -> *const c_float; - - pub fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char); - pub fn luaL_checktype(L: *mut lua_State, narg: c_int, t: c_int); - pub fn luaL_checkany(L: *mut lua_State, narg: c_int); - - pub fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int; - pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void; - - pub fn luaL_where(L: *mut lua_State, lvl: c_int); - - #[link_name = "luaL_errorL"] - pub fn luaL_error(L: *mut lua_State, fmt: *const c_char, ...) -> !; - - pub fn luaL_checkoption( - L: *mut lua_State, - narg: c_int, - def: *const c_char, - lst: *const *const c_char, - ) -> c_int; - - pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char; - - pub fn luaL_newstate() -> *mut lua_State; - - // TODO: luaL_findtable -} - -// -// Some useful macros (implemented as Rust functions) -// - -// TODO: luaL_argcheck, luaL_argexpected, luaL_checkstring, luaL_optstring, luaL_typename, luaL_opt - -#[inline(always)] -pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) { - lua_getfield(L, LUA_REGISTRYINDEX, n); -} - -// -// TODO: Generic Buffer Manipulation -// - -// -// Builtin libraries -// +use super::lua::lua_State; pub const LUA_COLIBNAME: &str = "coroutine"; pub const LUA_TABLIBNAME: &str = "table"; diff --git a/src/ffi/luau/mod.rs b/src/ffi/luau/mod.rs index bbdb276..feec60d 100644 --- a/src/ffi/luau/mod.rs +++ b/src/ffi/luau/mod.rs @@ -1,7 +1,11 @@ //! Low level bindings to Luau. +pub use self::lauxlib::*; pub use self::lua::*; +pub use self::luacode::*; pub use self::lualib::*; +pub mod lauxlib; pub mod lua; +pub mod luacode; pub mod lualib; diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 9f88188..8382474 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -42,6 +42,7 @@ pub const SYS_MIN_ALIGN: usize = 4; // Hack to avoid stripping a few unused Lua symbols that could be imported // by C modules in unsafe mode +#[cfg(not(feature = "luau"))] pub(crate) fn keep_lua_symbols() { let mut symbols: Vec<*const extern "C" fn()> = Vec::new(); symbols.push(lua_atpanic as _); @@ -59,14 +60,25 @@ mod lauxlib; mod lua; mod lualib; -#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] +#[cfg(any( + feature = "lua52", + feature = "lua51", + feature = "luajit", + feature = "luau" +))] mod compat53; +#[cfg(feature = "lua54")] +pub mod lua54; + +#[cfg(feature = "lua53")] +pub mod lua53; + +#[cfg(feature = "lua52")] +pub mod lua52; + #[cfg(any(feature = "lua51", feature = "luajit"))] pub mod lua51; -#[cfg(feature = "lua52")] -pub mod lua52; -#[cfg(feature = "lua53")] -pub mod lua53; -#[cfg(feature = "lua54")] -pub mod lua54; + +#[cfg(feature = "luau")] +pub mod luau; diff --git a/src/function.rs b/src/function.rs index efc9da0..8c35ef1 100644 --- a/src/function.rs +++ b/src/function.rs @@ -1,6 +1,5 @@ -use std::os::raw::{c_int, c_void}; +use std::os::raw::c_int; use std::ptr; -use std::slice; use crate::error::{Error, Result}; use crate::ffi; @@ -214,7 +213,11 @@ impl<'lua> Function<'lua> { /// /// If `strip` is true, the binary representation may not include all debug information /// about the function, to save space. + #[cfg(not(feature = "luau"))] pub fn dump(&self, strip: bool) -> Vec { + use std::os::raw::c_void; + use std::slice; + unsafe extern "C" fn writer( _state: *mut ffi::lua_State, buf: *const c_void, diff --git a/src/lib.rs b/src/lib.rs index afc7fa9..ee851d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,6 +85,7 @@ mod conversion; mod error; mod ffi; mod function; +#[cfg(not(feature = "luau"))] mod hook; mod lua; mod multi; @@ -104,6 +105,7 @@ pub use crate::{ffi::lua_CFunction, ffi::lua_State}; pub use crate::error::{Error, ExternalError, ExternalResult, Result}; pub use crate::function::Function; +#[cfg(not(feature = "luau"))] pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack, HookTriggers}; pub use crate::lua::{AsChunk, Chunk, ChunkMode, GCMode, Lua, LuaOptions}; pub use crate::multi::Variadic; diff --git a/src/lua.rs b/src/lua.rs index c4a539c..87a343d 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -14,6 +14,7 @@ use rustc_hash::FxHashMap; use crate::error::{Error, Result}; use crate::ffi; use crate::function::Function; +#[cfg(not(feature = "luau"))] use crate::hook::{Debug, HookTriggers}; use crate::scope::Scope; use crate::stdlib::StdLib; @@ -21,8 +22,8 @@ use crate::string::String; use crate::table::Table; use crate::thread::Thread; use crate::types::{ - Callback, CallbackUpvalue, DestructedUserdataMT, HookCallback, Integer, LightUserData, LuaRef, - MaybeSend, Number, RegistryKey, + Callback, CallbackUpvalue, DestructedUserdataMT, /*HookCallback,*/ Integer, LightUserData, + LuaRef, MaybeSend, Number, RegistryKey, }; use crate::userdata::{ AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods, @@ -35,6 +36,9 @@ use crate::util::{ }; use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value}; +#[cfg(not(feature = "luau"))] +use crate::types::HookCallback; + #[cfg(not(feature = "lua54"))] use crate::util::push_userdata; #[cfg(feature = "lua54")] @@ -103,6 +107,7 @@ struct ExtraData { #[cfg(feature = "async")] ref_waker_idx: c_int, + #[cfg(not(feature = "luau"))] hook_callback: Option, #[cfg(feature = "lua54")] warn_callback: Option, @@ -310,6 +315,7 @@ impl Lua { let mut lua = unsafe { Self::inner_new(libs, options) }; + #[cfg(not(feature = "luau"))] if libs.contains(StdLib::PACKAGE) { mlua_expect!(lua.disable_c_modules(), "Error during disabling C modules"); } @@ -328,12 +334,16 @@ impl Lua { /// /// [`StdLib`]: crate::StdLib pub unsafe fn unsafe_new_with(libs: StdLib, options: LuaOptions) -> Lua { + #[cfg(not(feature = "luau"))] ffi::keep_lua_symbols(); Self::inner_new(libs, options) } unsafe fn inner_new(libs: StdLib, options: LuaOptions) -> Lua { - #[cfg_attr(any(feature = "lua51", feature = "luajit"), allow(dead_code))] + #[cfg_attr( + any(feature = "lua51", feature = "luajit", feature = "luau"), + allow(dead_code) + )] unsafe extern "C" fn allocator( extra_data: *mut c_void, ptr: *mut c_void, @@ -398,9 +408,14 @@ impl Lua { #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] let state = ffi::lua_newstate(allocator, mem_info as *mut c_void); - #[cfg(any(feature = "lua51", feature = "luajit"))] + #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] let state = ffi::luaL_newstate(); + // #[cfg(feature = "luau")] + // { + // ffi::luaL_sandbox(state); + // } + ffi::luaL_requiref(state, cstr!("_G"), ffi::luaopen_base, 1); ffi::lua_pop(state, 1); @@ -427,7 +442,7 @@ impl Lua { #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] ffi::lua_rawgeti(lua.state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS); - #[cfg(any(feature = "lua51", feature = "luajit"))] + #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] ffi::lua_pushvalue(lua.state, ffi::LUA_GLOBALSINDEX); ffi::lua_pushcfunction(lua.state, safe_pcall); @@ -447,6 +462,9 @@ impl Lua { extra.recycled_thread_cache = Vec::with_capacity(options.thread_cache_size); } + #[cfg(feature = "luau")] + mlua_expect!(lua.prepare_luau_state(), "Error preparing Luau state"); + lua } @@ -534,6 +552,7 @@ impl Lua { recycled_thread_cache: Vec::new(), #[cfg(feature = "async")] ref_waker_idx, + #[cfg(not(feature = "luau"))] hook_callback: None, #[cfg(feature = "lua54")] warn_callback: None, @@ -600,9 +619,12 @@ impl Lua { // If `package` library loaded into a safe lua state then disable C modules let extra = unsafe { &mut *self.extra.get() }; - let curr_libs = extra.libs; - if self.safe && (curr_libs ^ (curr_libs | libs)).contains(StdLib::PACKAGE) { - mlua_expect!(self.disable_c_modules(), "Error during disabling C modules"); + #[cfg(not(feature = "luau"))] + { + let curr_libs = extra.libs; + if self.safe && (curr_libs ^ (curr_libs | libs)).contains(StdLib::PACKAGE) { + mlua_expect!(self.disable_c_modules(), "Error during disabling C modules"); + } } extra.libs |= libs; @@ -795,6 +817,7 @@ impl Lua { /// /// [`HookTriggers`]: crate::HookTriggers /// [`HookTriggers.every_nth_instruction`]: crate::HookTriggers::every_nth_instruction + #[cfg(not(feature = "luau"))] pub fn set_hook(&self, triggers: HookTriggers, callback: F) -> Result<()> where F: 'static + MaybeSend + FnMut(&Lua, Debug) -> Result<()>, @@ -832,6 +855,7 @@ impl Lua { /// Remove any hook previously set by `set_hook`. This function has no effect if a hook was not /// previously set. + #[cfg(not(feature = "luau"))] pub fn remove_hook(&self) { // If main_state is not available, then sethook wasn't called. let state = match self.main_state { @@ -909,6 +933,7 @@ impl Lua { /// function that has called level `n` (except for tail calls, which do not count in the stack). /// /// [`Debug`]: crate::hook::Debug + #[cfg(not(feature = "luau"))] pub fn inspect_stack(&self, level: usize) -> Option { unsafe { let mut ar: ffi::lua_Debug = mem::zeroed(); @@ -961,7 +986,12 @@ impl Lua { /// Returns true if the garbage collector is currently running automatically. /// /// Requires `feature = "lua54/lua53/lua52"` - #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] + #[cfg(any( + feature = "lua54", + feature = "lua53", + feature = "lua52", + feature = "luau" + ))] pub fn gc_is_running(&self) -> bool { let state = self.main_state.unwrap_or(self.state); unsafe { ffi::lua_gc(state, ffi::LUA_GCISRUNNING, 0) != 0 } @@ -1018,6 +1048,7 @@ impl Lua { /// [documentation][lua_doc]. /// /// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#2.5 + #[cfg(not(feature = "luau"))] pub fn gc_set_pause(&self, pause: c_int) -> c_int { let state = self.main_state.unwrap_or(self.state); unsafe { ffi::lua_gc(state, ffi::LUA_GCSETPAUSE, pause) } @@ -1040,6 +1071,7 @@ impl Lua { /// More information can be found in the Lua [documentation][lua_doc]. /// /// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#2.5.1 + #[cfg(not(feature = "luau"))] pub fn gc_inc(&self, pause: c_int, step_multiplier: c_int, step_size: c_int) -> GCMode { let state = self.main_state.unwrap_or(self.state); @@ -1138,6 +1170,7 @@ impl Lua { } Some(ChunkMode::Binary) => cstr!("b"), Some(ChunkMode::Text) => cstr!("t"), + #[cfg(not(feature = "luau"))] None if source.starts_with(ffi::LUA_SIGNATURE) && self.safe => { return Err(Error::SafetyError( "binary chunks are disabled in safe mode".to_string(), @@ -1158,7 +1191,7 @@ impl Lua { self.push_value(env)?; #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] ffi::lua_setupvalue(self.state, -2, 1); - #[cfg(any(feature = "lua51", feature = "luajit"))] + #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] ffi::lua_setfenv(self.state, -2); } Ok(Function(self.pop_ref())) @@ -1488,7 +1521,7 @@ impl Lua { assert_stack(self.state, 1); #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS); - #[cfg(any(feature = "lua51", feature = "luajit"))] + #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] ffi::lua_pushvalue(self.state, ffi::LUA_GLOBALSINDEX); Table(self.pop_ref()) } @@ -2329,7 +2362,12 @@ impl Lua { where 'lua: 'callback, { - #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] + #[cfg(any( + feature = "lua54", + feature = "lua53", + feature = "lua52", + feature = "luau" + ))] unsafe { let libs = (*self.extra.get()).libs; if !libs.contains(StdLib::COROUTINE) { @@ -2551,6 +2589,7 @@ impl Lua { } } + #[cfg(not(feature = "luau"))] fn disable_c_modules(&self) -> Result<()> { let package: Table = self.globals().get("package")?; @@ -2577,6 +2616,42 @@ impl Lua { Ok(()) } + #[cfg(feature = "luau")] + unsafe fn prepare_luau_state(&self) -> Result<()> { + use std::ffi::CStr; + + // Since Luau has some missing standard function, we re-implement them here + // They are: collectgarbage, loadstring, require + + unsafe extern "C" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_int { + let option = ffi::luaL_optstring(state, 1, cstr!("collect")); + let option = CStr::from_ptr(option); + match option.to_str() { + Ok("collect") => { + ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0); + return 0; + } + Ok("count") => { + let n = ffi::lua_gc(state, ffi::LUA_GCCOUNT, 0); + ffi::lua_pushnumber(state, n as ffi::lua_Number); + return 1; + } + // TODO: More variants + _ => ffi::luaL_error( + state, + cstr!("collectgarbage must be called with 'count' or 'collect'"), + ), + } + } + + self.globals().raw_set( + "collectgarbage", + self.create_c_function(lua_collectgarbage)?, + )?; + + Ok(()) + } + pub(crate) unsafe fn make_from_ptr(state: *mut ffi::lua_State) -> Option { let _sg = StackGuard::new(state); assert_stack(state, 1); @@ -2740,7 +2815,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> { // For source code, first try interpreting the lua as an expression by adding // "return", then as a statement. This is the same thing the // actual lua repl does. - if self.source.starts_with(ffi::LUA_SIGNATURE) { + if self.source[0] < b'\n' { self.call(()) } else if let Ok(function) = self.lua.load_chunk( &self.expression_source(), @@ -2768,7 +2843,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> { 'lua: 'fut, R: FromLuaMulti<'lua> + 'fut, { - if self.source.starts_with(ffi::LUA_SIGNATURE) { + if self.source[0] < b'\n' { self.call_async(()) } else if let Ok(function) = self.lua.load_chunk( &self.expression_source(), @@ -2886,9 +2961,9 @@ where let prealloc_failure = match extra.wrapped_failures_cache.pop() { Some(index) => PreallocatedFailure::Cached(index), None => { - let ud = ffi::lua_newuserdata(state, mem::size_of::()); + let ud = WrappedFailure::new_userdata(state); ffi::lua_rotate(state, 1, 1); - PreallocatedFailure::New(ud as *mut WrappedFailure) + PreallocatedFailure::New(ud) } }; @@ -3006,7 +3081,12 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result< #[cfg(feature = "luajit")] let _gc_guard = GcGuard::new(state); - #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] + #[cfg(any( + feature = "lua54", + feature = "lua53", + feature = "lua52", + feature = "luau" + ))] { if libs.contains(StdLib::COROUTINE) { requiref(state, ffi::LUA_COLIBNAME, ffi::luaopen_coroutine, 1)?; @@ -3019,6 +3099,7 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result< ffi::lua_pop(state, 1); } + #[cfg(not(feature = "luau"))] if libs.contains(StdLib::IO) { requiref(state, ffi::LUA_IOLIBNAME, ffi::luaopen_io, 1)?; ffi::lua_pop(state, 1); @@ -3034,7 +3115,7 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result< ffi::lua_pop(state, 1); } - #[cfg(any(feature = "lua54", feature = "lua53"))] + #[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))] { if libs.contains(StdLib::UTF8) { requiref(state, ffi::LUA_UTF8LIBNAME, ffi::luaopen_utf8, 1)?; @@ -3042,7 +3123,7 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result< } } - #[cfg(feature = "lua52")] + #[cfg(any(feature = "lua52", feature = "luau"))] { if libs.contains(StdLib::BIT) { requiref(state, ffi::LUA_BITLIBNAME, ffi::luaopen_bit32, 1)?; @@ -3068,6 +3149,7 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result< ffi::lua_pop(state, 1); } + #[cfg(not(feature = "luau"))] if libs.contains(StdLib::PACKAGE) { requiref(state, ffi::LUA_LOADLIBNAME, ffi::luaopen_package, 1)?; ffi::lua_pop(state, 1); @@ -3089,7 +3171,6 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result< Ok(()) } -// We move `extra` (`MutexGuard`) here to correctly drop it if panic unsafe fn ref_stack_pop(extra: &mut ExtraData) -> c_int { if let Some(free) = extra.ref_free.pop() { ffi::lua_replace(extra.ref_thread, free); @@ -3238,7 +3319,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet .push((meta.into(), Self::box_method_mut(method))); } - #[cfg(all(feature = "async", not(feature = "lua51")))] + #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] fn add_async_meta_method(&mut self, meta: S, method: M) where T: Clone, @@ -3274,7 +3355,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet .push((meta.into(), Self::box_function_mut(function))); } - #[cfg(all(feature = "async", not(feature = "lua51")))] + #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] fn add_async_meta_function(&mut self, meta: S, function: F) where S: Into, diff --git a/src/scope.rs b/src/scope.rs index ea31f0b..ffaa400 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -3,7 +3,6 @@ use std::cell::{Cell, RefCell}; use std::marker::PhantomData; use std::mem; use std::os::raw::{c_int, c_void}; -use std::ptr; use std::rc::Rc; #[cfg(feature = "serialize")] @@ -18,8 +17,8 @@ use crate::userdata::{ AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods, }; use crate::util::{ - assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table, rawset_field, - take_userdata, StackGuard, + assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table, push_userdata, + rawset_field, take_userdata, StackGuard, }; use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value}; @@ -206,7 +205,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { ffi::lua_pushnil(state); ffi::lua_setiuservalue(state, -2, i as c_int); } - #[cfg(any(feature = "lua53", feature = "lua52"))] + #[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))] { ffi::lua_pushnil(state); ffi::lua_setuservalue(state, -2); @@ -264,7 +263,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>( scope: &Scope<'lua, 'scope>, data: Rc>, - data_ptr: *const c_void, + ud_ptr: *const c_void, method: NonStaticMethod<'callback, T>, ) -> Result> { // On methods that actually receive the userdata, we fake a type check on the passed in @@ -280,7 +279,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { let _sg = StackGuard::new(lua.state); check_stack(lua.state, 2)?; lua.push_userdata_ref(&ud.0)?; - if get_userdata(lua.state, -1) as *const _ == data_ptr { + if get_userdata(lua.state, -1) as *const _ == ud_ptr { return Ok(()); } } @@ -336,8 +335,9 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { let _sg = StackGuard::new(lua.state); check_stack(lua.state, 13)?; + #[cfg(not(feature = "luau"))] #[allow(clippy::let_and_return)] - let data_ptr = protect_lua!(lua.state, 0, 1, |state| { + let ud_ptr = protect_lua!(lua.state, 0, 1, |state| { let ud = ffi::lua_newuserdata(state, mem::size_of::>>>()); @@ -350,13 +350,22 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { ud })?; + #[cfg(feature = "luau")] + let ud_ptr = { + push_userdata::>>>( + lua.state, + UserDataCell::new(data.clone()), + )?; + ffi::lua_touserdata(lua.state, -1) + }; + // Prepare metatable, add meta methods first and then meta fields let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1; push_table(lua.state, 0, meta_methods_nrec as c_int)?; for (k, m) in ud_methods.meta_methods { let data = data.clone(); - lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?; + lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?; rawset_field(lua.state, -2, k.validate()?.name())?; } for (k, f) in ud_fields.meta_fields { @@ -371,7 +380,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { push_table(lua.state, 0, field_getters_nrec as c_int)?; for (k, m) in ud_fields.field_getters { let data = data.clone(); - lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?; + lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?; rawset_field(lua.state, -2, &k)?; } field_getters_index = Some(ffi::lua_absindex(lua.state, -1)); @@ -383,7 +392,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { push_table(lua.state, 0, field_setters_nrec as c_int)?; for (k, m) in ud_fields.field_setters { let data = data.clone(); - lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?; + lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?; rawset_field(lua.state, -2, &k)?; } field_setters_index = Some(ffi::lua_absindex(lua.state, -1)); @@ -396,7 +405,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { push_table(lua.state, 0, methods_nrec as c_int)?; for (k, m) in ud_methods.methods { let data = data.clone(); - lua.push_value(Value::Function(wrap_method(self, data, data_ptr, m)?))?; + lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?; rawset_field(lua.state, -2, &k)?; } methods_index = Some(ffi::lua_absindex(lua.state, -1)); @@ -417,7 +426,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { let mt_ptr = ffi::lua_topointer(lua.state, -1); // Write userdata just before attaching metatable with `__gc` metamethod - ptr::write(data_ptr as _, UserDataCell::new(data)); + #[cfg(not(feature = "luau"))] + std::ptr::write(ud_ptr as _, UserDataCell::new(data)); ffi::lua_setmetatable(lua.state, -2); let ud = AnyUserData(lua.pop_ref()); lua.register_userdata_metatable(mt_ptr, None); @@ -446,7 +456,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { ffi::lua_pushnil(state); ffi::lua_setiuservalue(state, -2, i as c_int); } - #[cfg(any(feature = "lua53", feature = "lua52"))] + #[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))] { ffi::lua_pushnil(state); ffi::lua_setuservalue(state, -2); @@ -532,7 +542,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { // First, get the environment table #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] ffi::lua_getupvalue(state, -1, 1); - #[cfg(any(feature = "lua51", feature = "luajit"))] + #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] ffi::lua_getfenv(state, -1); // Second, get the `get_poll()` closure using the corresponding key @@ -727,7 +737,7 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l )); } - #[cfg(all(feature = "async", not(feature = "lua51")))] + #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] fn add_async_meta_method(&mut self, _meta: S, _method: M) where T: Clone, @@ -772,7 +782,7 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l )); } - #[cfg(all(feature = "async", not(feature = "lua51")))] + #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] fn add_async_meta_function(&mut self, _meta: S, _function: F) where S: Into, diff --git a/src/stdlib.rs b/src/stdlib.rs index fb0f1b8..fd34c43 100644 --- a/src/stdlib.rs +++ b/src/stdlib.rs @@ -9,11 +9,17 @@ impl StdLib { /// [`coroutine`](https://www.lua.org/manual/5.4/manual.html#6.2) library /// /// Requires `feature = "lua54/lua53/lua52"` - #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] + #[cfg(any( + feature = "lua54", + feature = "lua53", + feature = "lua52", + feature = "luau" + ))] pub const COROUTINE: StdLib = StdLib(1); /// [`table`](https://www.lua.org/manual/5.4/manual.html#6.6) library pub const TABLE: StdLib = StdLib(1 << 1); /// [`io`](https://www.lua.org/manual/5.4/manual.html#6.8) library + #[cfg(not(feature = "luau"))] pub const IO: StdLib = StdLib(1 << 2); /// [`os`](https://www.lua.org/manual/5.4/manual.html#6.9) library pub const OS: StdLib = StdLib(1 << 3); @@ -22,16 +28,17 @@ impl StdLib { /// [`utf8`](https://www.lua.org/manual/5.4/manual.html#6.5) library /// /// Requires `feature = "lua54/lua53"` - #[cfg(any(feature = "lua54", feature = "lua53"))] + #[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))] pub const UTF8: StdLib = StdLib(1 << 5); /// [`bit`](https://www.lua.org/manual/5.2/manual.html#6.7) library /// /// Requires `feature = "lua52/luajit"` - #[cfg(any(feature = "lua52", feature = "luajit", doc))] + #[cfg(any(feature = "lua52", feature = "luajit", feature = "luau", doc))] pub const BIT: StdLib = StdLib(1 << 6); /// [`math`](https://www.lua.org/manual/5.4/manual.html#6.7) library pub const MATH: StdLib = StdLib(1 << 7); /// [`package`](https://www.lua.org/manual/5.4/manual.html#6.3) library + #[cfg(not(feature = "luau"))] pub const PACKAGE: StdLib = StdLib(1 << 8); /// [`jit`](http://luajit.org/ext_jit.html) library /// diff --git a/src/types.rs b/src/types.rs index cd4b13f..ed7305b 100644 --- a/src/types.rs +++ b/src/types.rs @@ -11,6 +11,7 @@ use futures_core::future::LocalBoxFuture; use crate::error::Result; use crate::ffi; +#[cfg(not(feature = "luau"))] use crate::hook::Debug; use crate::lua::Lua; use crate::util::{assert_stack, StackGuard}; @@ -48,11 +49,10 @@ pub(crate) struct AsyncPollUpvalue<'lua> { pub(crate) lua: Lua, pub(crate) fut: LocalBoxFuture<'lua, Result>>, } - -#[cfg(feature = "send")] +#[cfg(all(feature = "send", not(feature = "luau")))] pub(crate) type HookCallback = Arc Result<()> + Send>>; -#[cfg(not(feature = "send"))] +#[cfg(all(not(feature = "send"), not(feature = "luau")))] pub(crate) type HookCallback = Arc Result<()>>>; #[cfg(all(feature = "send", feature = "lua54"))] diff --git a/src/userdata.rs b/src/userdata.rs index bf0706c..0680779 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -419,7 +419,7 @@ pub trait UserDataMethods<'lua, T: UserData> { /// Requires `feature = "async"` /// /// [`add_meta_method`]: #method.add_meta_method - #[cfg(all(feature = "async", not(feature = "lua51")))] + #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] fn add_async_meta_method(&mut self, name: S, method: M) where @@ -461,7 +461,7 @@ pub trait UserDataMethods<'lua, T: UserData> { /// Requires `feature = "async"` /// /// [`add_meta_function`]: #method.add_meta_function - #[cfg(all(feature = "async", not(feature = "lua51")))] + #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] fn add_async_meta_function(&mut self, name: S, function: F) where @@ -839,7 +839,7 @@ impl<'lua> AnyUserData<'lua> { ffi::lua_pushnil(lua.state); ffi::lua_setiuservalue(lua.state, -2, i as c_int); } - #[cfg(any(feature = "lua53", feature = "lua52"))] + #[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))] { ffi::lua_pushnil(lua.state); ffi::lua_setuservalue(lua.state, -2); @@ -865,6 +865,7 @@ impl<'lua> AnyUserData<'lua> { /// /// [`get_user_value`]: #method.get_user_value /// [`set_nth_user_value`]: #method.set_nth_user_value + // #[cfg(not(feature = "luau"))] #[inline] pub fn set_user_value>(&self, v: V) -> Result<()> { self.set_nth_user_value(1, v) @@ -876,6 +877,7 @@ impl<'lua> AnyUserData<'lua> { /// /// [`set_user_value`]: #method.set_user_value /// [`get_nth_user_value`]: #method.get_nth_user_value + // #[cfg(not(feature = "luau"))] #[inline] pub fn get_user_value>(&self) -> Result { self.get_nth_user_value(1) @@ -891,6 +893,7 @@ impl<'lua> AnyUserData<'lua> { /// For other Lua versions this functionality is provided using a wrapping table. /// /// [`get_nth_user_value`]: #method.get_nth_user_value + // #[cfg(not(feature = "luau"))] pub fn set_nth_user_value>(&self, n: usize, v: V) -> Result<()> { if n < 1 || n > u16::MAX as usize { return Err(Error::RuntimeError( @@ -945,6 +948,7 @@ impl<'lua> AnyUserData<'lua> { /// For other Lua versions this functionality is provided using a wrapping table. /// /// [`set_nth_user_value`]: #method.set_nth_user_value + // #[cfg(not(feature = "luau"))] pub fn get_nth_user_value>(&self, n: usize) -> Result { if n < 1 || n > u16::MAX as usize { return Err(Error::RuntimeError( @@ -986,6 +990,7 @@ impl<'lua> AnyUserData<'lua> { /// The value can be retrieved with [`get_named_user_value`]. /// /// [`get_named_user_value`]: #method.get_named_user_value + // #[cfg(not(feature = "luau"))] pub fn set_named_user_value(&self, name: &S, v: V) -> Result<()> where S: AsRef<[u8]> + ?Sized, @@ -1025,6 +1030,7 @@ impl<'lua> AnyUserData<'lua> { /// Returns an associated value by name set by [`set_named_user_value`]. /// /// [`set_named_user_value`]: #method.set_named_user_value + // #[cfg(not(feature = "luau"))] pub fn get_named_user_value(&self, name: &S) -> Result where S: AsRef<[u8]> + ?Sized, diff --git a/src/util.rs b/src/util.rs index 75caa9f..b2c25d8 100644 --- a/src/util.rs +++ b/src/util.rs @@ -278,6 +278,7 @@ where } // Internally uses 3 stack spaces, does not call checkstack. +#[cfg(not(feature = "luau"))] #[inline] pub unsafe fn push_userdata(state: *mut ffi::lua_State, t: T) -> Result<()> { let ud = protect_lua!(state, 0, 1, |state| { @@ -287,6 +288,27 @@ pub unsafe fn push_userdata(state: *mut ffi::lua_State, t: T) -> Result<()> { Ok(()) } +// Internally uses 3 stack spaces, does not call checkstack. +#[cfg(feature = "luau")] +#[inline] +pub unsafe fn push_userdata(state: *mut ffi::lua_State, t: T) -> Result<()> { + unsafe extern "C" fn destructor(ud: *mut c_void) { + let ud = ud as *mut T; + if *(ud.offset(1) as *mut u8) == 0 { + ptr::drop_in_place(ud); + } + } + + let ud = protect_lua!(state, 0, 1, |state| { + let size = mem::size_of::() + 1; + ffi::lua_newuserdatadtor(state, size, destructor::) as *mut T + })?; + ptr::write(ud, t); + *(ud.offset(1) as *mut u8) = 0; // Mark as not destructed + + Ok(()) +} + // Internally uses 3 stack spaces, does not call checkstack. #[cfg(feature = "lua54")] #[inline] @@ -316,8 +338,11 @@ pub unsafe fn take_userdata(state: *mut ffi::lua_State) -> T { // after the first call to __gc. get_destructed_userdata_metatable(state); ffi::lua_setmetatable(state, -2); - let ud = get_userdata(state, -1); + let ud = get_userdata::(state, -1); ffi::lua_pop(state, 1); + if cfg!(feature = "luau") { + *(ud.offset(1) as *mut u8) = 1; // Mark as destructed + } ptr::read(ud) } @@ -508,8 +533,11 @@ pub unsafe fn init_userdata_metatable( rawset_field(state, -2, "__newindex")?; } - ffi::lua_pushcfunction(state, userdata_destructor::); - rawset_field(state, -2, "__gc")?; + #[cfg(not(feature = "luau"))] + { + ffi::lua_pushcfunction(state, userdata_destructor::); + rawset_field(state, -2, "__gc")?; + } ffi::lua_pushboolean(state, 0); rawset_field(state, -2, "__metatable")?; @@ -519,6 +547,7 @@ pub unsafe fn init_userdata_metatable( Ok(()) } +#[cfg(not(feature = "luau"))] pub unsafe extern "C" fn userdata_destructor(state: *mut ffi::lua_State) -> c_int { // It's probably NOT a good idea to catch Rust panics in finalizer // Lua 5.4 ignores it, other versions generates `LUA_ERRGCMM` without calling message handler @@ -553,7 +582,7 @@ where // We cannot shadow Rust errors with Lua ones, we pre-allocate enough memory // to store a wrapped error or panic *before* we proceed. - let ud = ffi::lua_newuserdata(state, mem::size_of::()); + let ud = WrappedFailure::new_userdata(state); ffi::lua_rotate(state, 1, 1); match catch_unwind(AssertUnwindSafe(|| f(nargs))) { @@ -706,6 +735,8 @@ pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua None } } + #[cfg(feature = "luau")] + Some(ffi::lua_mainthread(state)) } // Initialize the internal (with __gc method) metatable for a type T. @@ -718,8 +749,11 @@ pub unsafe fn init_gc_metatable( push_table(state, 0, 3)?; - ffi::lua_pushcfunction(state, userdata_destructor::); - rawset_field(state, -2, "__gc")?; + #[cfg(not(feature = "luau"))] + { + ffi::lua_pushcfunction(state, userdata_destructor::); + rawset_field(state, -2, "__gc")?; + } ffi::lua_pushboolean(state, 0); rawset_field(state, -2, "__metatable")?; @@ -879,10 +913,28 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> { } pub(crate) enum WrappedFailure { + None, Error(Error), Panic(Option>), } +impl WrappedFailure { + pub(crate) unsafe fn new_userdata(state: *mut ffi::lua_State) -> *mut Self { + let size = mem::size_of::(); + #[cfg(feature = "luau")] + let ud = { + unsafe extern "C" fn destructor(p: *mut c_void) { + ptr::drop_in_place(p as *mut WrappedFailure); + } + ffi::lua_newuserdatadtor(state, size, destructor) as *mut Self + }; + #[cfg(not(feature = "luau"))] + let ud = ffi::lua_newuserdata(state, size) as *mut Self; + ptr::write(ud, WrappedFailure::None); + ud + } +} + // Converts the given lua value to a string in a reasonable format without causing a Lua error or // panicking. pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> String { diff --git a/tests/async.rs b/tests/async.rs index dead884..2bfde03 100644 --- a/tests/async.rs +++ b/tests/async.rs @@ -299,7 +299,7 @@ async fn test_async_userdata() -> Result<()> { Ok(format!("elapsed:{}ms", n)) }); - #[cfg(not(feature = "lua51"))] + #[cfg(not(any(feature = "lua51", feature = "luau")))] methods.add_async_meta_method(mlua::MetaMethod::Call, |_, data, ()| async move { let n = data.0.load(Ordering::Relaxed); Delay::new(Duration::from_millis(n)).await; @@ -363,7 +363,7 @@ async fn test_async_userdata() -> Result<()> { .exec_async() .await?; - #[cfg(not(feature = "lua51"))] + #[cfg(not(any(feature = "lua51", feature = "luau")))] lua.load( r#" userdata:set_value(15) diff --git a/tests/function.rs b/tests/function.rs index a7bdd9f..f245c37 100644 --- a/tests/function.rs +++ b/tests/function.rs @@ -93,6 +93,7 @@ fn test_c_function() -> Result<()> { Ok(()) } +#[cfg(not(feature = "luau"))] #[test] fn test_dump() -> Result<()> { let lua = unsafe { Lua::unsafe_new() }; diff --git a/tests/hooks.rs b/tests/hooks.rs index 3e8e876..8c44b5d 100644 --- a/tests/hooks.rs +++ b/tests/hooks.rs @@ -1,3 +1,5 @@ +#![cfg(not(feature = "luau"))] + use std::cell::RefCell; use std::ops::Deref; use std::str; diff --git a/tests/memory.rs b/tests/memory.rs index 45332f4..36d094e 100644 --- a/tests/memory.rs +++ b/tests/memory.rs @@ -41,7 +41,12 @@ fn test_gc_control() -> Result<()> { #[cfg(feature = "lua54")] assert_eq!(lua.gc_gen(0, 0), mlua::GCMode::Incremental); - #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] + #[cfg(any( + feature = "lua54", + feature = "lua53", + feature = "lua52", + feature = "luau" + ))] { assert!(lua.gc_is_running()); lua.gc_stop(); diff --git a/tests/tests.rs b/tests/tests.rs index 6c6b132..2d99911 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -11,6 +11,7 @@ use mlua::{ UserData, Value, Variadic, }; +#[cfg(not(feature = "luau"))] #[test] fn test_safety() -> Result<()> { let lua = Lua::new(); @@ -120,6 +121,7 @@ fn test_exec() -> Result<()> { "#, ) .eval()?; + println!("checkpoint"); assert!(module.contains_key("func")?); assert_eq!( module.get::<_, Function>("func")?.call::<_, String>(())?, @@ -150,6 +152,7 @@ fn test_eval() -> Result<()> { Ok(()) } +#[cfg(not(feature = "luau"))] #[test] fn test_load_mode() -> Result<()> { let lua = unsafe { Lua::unsafe_new() }; @@ -250,15 +253,7 @@ fn test_error() -> Result<()> { } } - impl error::Error for TestError { - fn description(&self) -> &str { - "test error" - } - - fn cause(&self) -> Option<&dyn error::Error> { - None - } - } + impl error::Error for TestError {} let lua = Lua::new(); @@ -295,8 +290,8 @@ fn test_error() -> Result<()> { end, 3) local function handler(err) - if string.match(_VERSION, ' 5%.1$') or string.match(_VERSION, ' 5%.2$') then - -- Special case for Lua 5.1/5.2 + if string.match(_VERSION, ' 5%.1$') or string.match(_VERSION, ' 5%.2$') or _VERSION == "Luau" then + -- Special case for Lua 5.1/5.2 and Luau local caps = string.match(err, ': (%d+)$') if caps then err = caps @@ -1096,7 +1091,11 @@ fn test_context_thread() -> Result<()> { ))] f.call::<_, ()>(lua.current_thread())?; - #[cfg(any(feature = "lua51", all(feature = "luajit", not(feature = "luajit52"))))] + #[cfg(any( + feature = "lua51", + all(feature = "luajit", not(feature = "luajit52")), + feature = "luau" + ))] f.call::<_, ()>(Nil)?; Ok(()) @@ -1170,6 +1169,7 @@ fn test_load_from_function() -> Result<()> { Ok(()) } +#[cfg(not(feature = "luau"))] #[test] fn test_inspect_stack() -> Result<()> { let lua = Lua::new(); diff --git a/tests/thread.rs b/tests/thread.rs index ab687ad..5873680 100644 --- a/tests/thread.rs +++ b/tests/thread.rs @@ -155,7 +155,8 @@ fn test_coroutine_from_closure() -> Result<()> { feature = "lua54", feature = "lua53", feature = "lua52", - feature = "luajit" + feature = "luajit", + feature = "luau" ))] let thrd: Thread = lua.load("coroutine.create(main)").eval()?; #[cfg(feature = "lua51")] diff --git a/tests/userdata.rs b/tests/userdata.rs index cba2638..c78b1db 100644 --- a/tests/userdata.rs +++ b/tests/userdata.rs @@ -350,7 +350,7 @@ fn test_userdata_take() -> Result<()> { } #[test] -fn test_destroy_userdata() -> Result<()> { +fn test_userdata_destroy() -> Result<()> { struct MyUserdata(Arc<()>); impl UserData for MyUserdata {} @@ -358,12 +358,15 @@ fn test_destroy_userdata() -> Result<()> { let rc = Arc::new(()); let lua = Lua::new(); - lua.globals().set("userdata", MyUserdata(rc.clone()))?; + let ud = lua.create_userdata(MyUserdata(rc.clone()))?; + ud.set_user_value(MyUserdata(rc.clone()))?; + lua.globals().set("userdata", ud)?; - assert_eq!(Arc::strong_count(&rc), 2); + assert_eq!(Arc::strong_count(&rc), 3); - // should destroy all objects - let _ = lua.globals().raw_remove("userdata")?; + // Should destroy all objects + lua.globals().raw_remove("userdata")?; + lua.gc_collect()?; lua.gc_collect()?; assert_eq!(Arc::strong_count(&rc), 1);