From d3f44354e0b8ad976aa9247fe3315f7144b3d599 Mon Sep 17 00:00:00 2001 From: Alex Orlenko Date: Wed, 16 Jun 2021 22:13:11 +0100 Subject: [PATCH] Revert commit ced808d5ab I think this experiment is unsuccessful and does not work well in a module mode with dynamic symbols resolution and mixing between different mlua instances. Overall the Rust bug has been fixed and we can wait for the "C-unwind" feature become stable. --- build/main.rs | 5 - src/ffi/mod.rs | 2 - src/ffi/safe.rs | 278 ----------- src/ffi/shim/compat-5.3.c | 953 -------------------------------------- src/ffi/shim/compat-5.3.h | 424 ----------------- src/ffi/shim/shim.c | 515 -------------------- src/function.rs | 29 +- src/hook.rs | 5 +- src/lua.rs | 227 +++++---- src/scope.rs | 46 +- src/serde/mod.rs | 18 +- src/serde/ser.rs | 8 +- src/table.rs | 47 +- src/thread.rs | 4 +- src/userdata.rs | 6 +- src/util.rs | 447 ++++++++++++++---- 16 files changed, 594 insertions(+), 2420 deletions(-) delete mode 100644 src/ffi/safe.rs delete mode 100644 src/ffi/shim/compat-5.3.c delete mode 100644 src/ffi/shim/compat-5.3.h delete mode 100644 src/ffi/shim/shim.c diff --git a/build/main.rs b/build/main.rs index ac605c1..0bc4e1e 100644 --- a/build/main.rs +++ b/build/main.rs @@ -242,11 +242,6 @@ fn main() { let mut shim_cc = cc::Build::new(); shim_cc .include(include_dir) - .define("COMPAT53_INCLUDE_SOURCE", None); - #[cfg(feature = "luajit")] - shim_cc.define("COMPAT53_LUAJIT", None); - shim_cc - .file("src/ffi/shim/shim.c") .file("src/ffi/shim/symbols.c") .compile("shim"); diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 4b9ff32..95ac6bf 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -297,5 +297,3 @@ mod lua; mod luaconf; mod lualib; mod symbols; - -pub mod safe; diff --git a/src/ffi/safe.rs b/src/ffi/safe.rs deleted file mode 100644 index 77931dd..0000000 --- a/src/ffi/safe.rs +++ /dev/null @@ -1,278 +0,0 @@ -use std::ffi::CString; -use std::os::raw::{c_char, c_int, c_void}; - -use crate::error::Result; -use crate::util::protect_lua; - -use super::lua::{lua_CFunction, lua_Debug, lua_Integer, lua_State}; - -extern "C" { - #[link_name = "MLUA_WRAPPED_ERROR_SIZE"] - pub static mut WRAPPED_ERROR_SIZE: usize; - #[link_name = "MLUA_WRAPPED_PANIC_SIZE"] - pub static mut WRAPPED_PANIC_SIZE: usize; - #[link_name = "MLUA_WRAPPED_ERROR_KEY"] - pub static mut WRAPPED_ERROR_KEY: *const c_void; - #[link_name = "MLUA_WRAPPED_PANIC_KEY"] - pub static mut WRAPPED_PANIC_KEY: *const c_void; - - pub fn lua_call_mlua_hook_proc(L: *mut lua_State, ar: *mut lua_Debug); - - pub fn meta_index_impl(state: *mut lua_State) -> c_int; - pub fn meta_newindex_impl(state: *mut lua_State) -> c_int; - pub fn bind_call_impl(state: *mut lua_State) -> c_int; - pub fn error_traceback(state: *mut lua_State) -> c_int; - pub fn lua_nopanic_pcall(state: *mut lua_State) -> c_int; - pub fn lua_nopanic_xpcall(state: *mut lua_State) -> c_int; - - fn lua_gc_s(L: *mut lua_State) -> c_int; - fn luaL_ref_s(L: *mut lua_State) -> c_int; - fn lua_pushlstring_s(L: *mut lua_State) -> c_int; - fn lua_tolstring_s(L: *mut lua_State) -> c_int; - fn lua_newthread_s(L: *mut lua_State) -> c_int; - fn lua_newuserdata_s(L: *mut lua_State) -> c_int; - fn lua_newwrappederror_s(L: *mut lua_State) -> c_int; - fn lua_pushcclosure_s(L: *mut lua_State) -> c_int; - fn lua_pushrclosure_s(L: *mut lua_State) -> c_int; - fn luaL_requiref_s(L: *mut lua_State) -> c_int; - fn error_traceback_s(L: *mut lua_State) -> c_int; - - fn lua_newtable_s(L: *mut lua_State) -> c_int; - fn lua_createtable_s(L: *mut lua_State) -> c_int; - fn lua_gettable_s(L: *mut lua_State) -> c_int; - fn lua_settable_s(L: *mut lua_State) -> c_int; - fn lua_geti_s(L: *mut lua_State) -> c_int; - fn lua_rawset_s(L: *mut lua_State) -> c_int; - fn lua_rawseti_s(L: *mut lua_State) -> c_int; - fn lua_rawsetp_s(L: *mut lua_State) -> c_int; - fn lua_rawsetfield_s(L: *mut lua_State) -> c_int; - fn lua_rawinsert_s(L: *mut lua_State) -> c_int; - fn lua_rawremove_s(L: *mut lua_State) -> c_int; - fn luaL_len_s(L: *mut lua_State) -> c_int; - fn lua_next_s(L: *mut lua_State) -> c_int; -} - -#[repr(C)] -struct StringArg { - data: *const c_char, - len: usize, -} - -// -// Common functions -// - -// Uses 4 stack spaces -pub unsafe fn lua_gc(state: *mut lua_State, what: c_int, data: c_int) -> Result { - super::lua_pushinteger(state, what as lua_Integer); - super::lua_pushinteger(state, data as lua_Integer); - protect_lua(state, 2, lua_gc_s)?; - let ret = super::lua_tointeger(state, -1) as c_int; - super::lua_pop(state, 1); - Ok(ret) -} - -// Uses 3 stack spaces -pub unsafe fn luaL_ref(state: *mut lua_State, table: c_int) -> Result { - super::lua_pushvalue(state, table); - super::lua_rotate(state, -2, 1); - protect_lua(state, 2, luaL_ref_s)?; - let ret = super::lua_tointeger(state, -1) as c_int; - super::lua_pop(state, 1); - Ok(ret) -} - -// Uses 3 stack spaces -pub unsafe fn lua_pushstring + ?Sized>(state: *mut lua_State, s: &S) -> Result<()> { - let s = s.as_ref(); - let s = StringArg { - data: s.as_ptr() as *const c_char, - len: s.len(), - }; - super::lua_pushlightuserdata(state, &s as *const StringArg as *mut c_void); - protect_lua(state, 1, lua_pushlstring_s) -} - -// Uses 4 stack spaces -pub unsafe fn lua_tolstring( - state: *mut lua_State, - index: c_int, - len: *mut usize, -) -> Result<*const c_char> { - let index = super::lua_absindex(state, index); - super::lua_pushvalue(state, index); - super::lua_pushlightuserdata(state, len as *mut c_void); - protect_lua(state, 2, lua_tolstring_s)?; - let s = super::lua_touserdata(state, -1); - super::lua_pop(state, 1); - super::lua_replace(state, index); - Ok(s as *const c_char) -} - -// Uses 2 stack spaces -pub unsafe fn lua_newthread(state: *mut lua_State) -> Result<*mut lua_State> { - protect_lua(state, 0, lua_newthread_s)?; - Ok(super::lua_tothread(state, -1)) -} - -// Uses 3 stack spaces -pub unsafe fn lua_newuserdata(state: *mut lua_State, size: usize) -> Result<*mut c_void> { - super::lua_pushinteger(state, size as lua_Integer); - protect_lua(state, 1, lua_newuserdata_s)?; - Ok(super::lua_touserdata(state, -1)) -} - -// Uses 2 stack spaces -pub unsafe fn lua_newwrappederror(state: *mut lua_State) -> Result<*mut c_void> { - protect_lua(state, 0, lua_newwrappederror_s)?; - Ok(super::lua_touserdata(state, -1)) -} - -// Uses 3 stack spaces -pub unsafe fn lua_pushcclosure(state: *mut lua_State, f: lua_CFunction, n: c_int) -> Result<()> { - super::lua_pushlightuserdata(state, f as *mut c_void); - protect_lua(state, n + 1, lua_pushcclosure_s) -} - -// Uses 3 stack spaces -pub unsafe fn lua_pushrclosure(state: *mut lua_State, f: lua_CFunction, n: c_int) -> Result<()> { - super::lua_pushlightuserdata(state, f as *mut c_void); - if n > 0 { - super::lua_rotate(state, -n - 1, 1); - } - protect_lua(state, n + 1, lua_pushrclosure_s) -} - -// Uses 5 stack spaces -pub unsafe fn luaL_requiref + ?Sized>( - state: *mut lua_State, - modname: &S, - openf: lua_CFunction, - glb: c_int, -) -> Result<()> { - let modname = mlua_expect!(CString::new(modname.as_ref()), "modname contains nil bytes"); - super::lua_pushlightuserdata(state, modname.as_ptr() as *mut c_void); - super::lua_pushlightuserdata(state, openf as *mut c_void); - super::lua_pushinteger(state, glb as lua_Integer); - protect_lua(state, 3, luaL_requiref_s) -} - -// Uses 3 stack spaces -pub unsafe fn error_traceback2(state: *mut lua_State, state2: *mut lua_State) -> Result<()> { - mlua_assert!( - state != state2, - "error_traceback2 must be used with two different states" - ); - super::lua_pushlightuserdata(state, state2); - protect_lua(state, 1, error_traceback_s) -} - -// -// Table functions -// - -// Uses 2 stack spaces -pub unsafe fn lua_newtable(state: *mut lua_State) -> Result<()> { - protect_lua(state, 0, lua_newtable_s) -} - -// Uses 4 stack spaces -pub unsafe fn lua_createtable(state: *mut lua_State, narr: c_int, nrec: c_int) -> Result<()> { - super::lua_pushinteger(state, narr as lua_Integer); - super::lua_pushinteger(state, nrec as lua_Integer); - protect_lua(state, 2, lua_createtable_s) -} - -// Uses 3 stack spaces -pub unsafe fn lua_gettable(state: *mut lua_State, table: c_int) -> Result<()> { - super::lua_pushvalue(state, table); - super::lua_rotate(state, -2, 1); - protect_lua(state, 2, lua_gettable_s) -} - -// Uses 3 stack spaces -pub unsafe fn lua_settable(state: *mut lua_State, table: c_int) -> Result<()> { - super::lua_pushvalue(state, table); - super::lua_rotate(state, -3, 1); - protect_lua(state, 3, lua_settable_s) -} - -// Uses 4 stack spaces -pub unsafe fn lua_geti(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result { - super::lua_pushvalue(state, table); - super::lua_pushinteger(state, i); - protect_lua(state, 2, lua_geti_s).map(|_| super::lua_type(state, -1)) -} - -// Uses 3 stack spaces -pub unsafe fn lua_rawset(state: *mut lua_State, table: c_int) -> Result<()> { - super::lua_pushvalue(state, table); - super::lua_rotate(state, -3, 1); - protect_lua(state, 3, lua_rawset_s) -} - -// Uses 4 stack spaces -pub unsafe fn lua_rawseti(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<()> { - super::lua_pushvalue(state, table); - super::lua_rotate(state, -2, 1); - super::lua_pushinteger(state, i); - protect_lua(state, 3, lua_rawseti_s) -} - -// Uses 4 stack spaces -pub unsafe fn lua_rawsetp(state: *mut lua_State, table: c_int, ptr: *const c_void) -> Result<()> { - super::lua_pushvalue(state, table); - super::lua_rotate(state, -2, 1); - super::lua_pushlightuserdata(state, ptr as *mut c_void); - protect_lua(state, 3, lua_rawsetp_s) -} - -// Uses 4 stack spaces -pub unsafe fn lua_rawsetfield(state: *mut lua_State, table: c_int, field: &S) -> Result<()> -where - S: AsRef<[u8]> + ?Sized, -{ - let field = field.as_ref(); - let s = StringArg { - data: field.as_ptr() as *const c_char, - len: field.len(), - }; - super::lua_pushvalue(state, table); - super::lua_pushlightuserdata(state, &s as *const StringArg as *mut c_void); - super::lua_rotate(state, -3, 2); - protect_lua(state, 3, lua_rawsetfield_s) -} - -// Uses 4 stack spaces -pub unsafe fn lua_rawinsert(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<()> { - super::lua_pushvalue(state, table); - super::lua_rotate(state, -2, 1); - super::lua_pushinteger(state, i); - protect_lua(state, 3, lua_rawinsert_s) -} - -// Uses 4 stack spaces -pub unsafe fn lua_rawremove(state: *mut lua_State, table: c_int, i: lua_Integer) -> Result<()> { - super::lua_pushvalue(state, table); - super::lua_pushinteger(state, i); - protect_lua(state, 2, lua_rawremove_s) -} - -// Uses 3 stack spaces -pub unsafe fn luaL_len(state: *mut lua_State, table: c_int) -> Result { - super::lua_pushvalue(state, table); - protect_lua(state, 1, luaL_len_s)?; - let ret = super::lua_tointeger(state, -1); - super::lua_pop(state, 1); - Ok(ret) -} - -// Uses 3 stack spaces -pub unsafe fn lua_next(state: *mut lua_State, table: c_int) -> Result { - super::lua_pushvalue(state, table); - super::lua_rotate(state, -2, 1); - protect_lua(state, 2, lua_next_s)?; - let ret = super::lua_tointeger(state, -1); - super::lua_pop(state, 1); - Ok(ret) -} diff --git a/src/ffi/shim/compat-5.3.c b/src/ffi/shim/compat-5.3.c deleted file mode 100644 index 4d7390d..0000000 --- a/src/ffi/shim/compat-5.3.c +++ /dev/null @@ -1,953 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "compat-5.3.h" - -/* don't compile it again if it already is included via compat53.h */ -#ifndef COMPAT53_C_ -#define COMPAT53_C_ - - - -/* definitions for Lua 5.1 only */ -#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 - -#ifndef COMPAT53_FOPEN_NO_LOCK -# if defined(_MSC_VER) -# define COMPAT53_FOPEN_NO_LOCK 1 -# else /* otherwise */ -# define COMPAT53_FOPEN_NO_LOCK 0 -# endif /* VC++ only so far */ -#endif /* No-lock fopen_s usage if possible */ - -#if defined(_MSC_VER) && COMPAT53_FOPEN_NO_LOCK -# include -#endif /* VC++ _fsopen for share-allowed file read */ - -#ifndef COMPAT53_HAVE_STRERROR_R -# if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \ - (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600) || \ - defined(__APPLE__) -# define COMPAT53_HAVE_STRERROR_R 1 -# else /* none of the defines matched: define to 0 */ -# define COMPAT53_HAVE_STRERROR_R 0 -# endif /* have strerror_r of some form */ -#endif /* strerror_r */ - -#ifndef COMPAT53_HAVE_STRERROR_S -# if defined(_MSC_VER) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && \ - defined(__STDC_LIB_EXT1__) && __STDC_LIB_EXT1__) -# define COMPAT53_HAVE_STRERROR_S 1 -# else /* not VC++ or C11 */ -# define COMPAT53_HAVE_STRERROR_S 0 -# endif /* strerror_s from VC++ or C11 */ -#endif /* strerror_s */ - -#ifndef COMPAT53_LUA_FILE_BUFFER_SIZE -# define COMPAT53_LUA_FILE_BUFFER_SIZE 4096 -#endif /* Lua File Buffer Size */ - - -static char* compat53_strerror (int en, char* buff, size_t sz) { -#if COMPAT53_HAVE_STRERROR_R - /* use strerror_r here, because it's available on these specific platforms */ - if (sz > 0) { - buff[0] = '\0'; - /* we don't care whether the GNU version or the XSI version is used: */ - if (strerror_r(en, buff, sz)) { - /* Yes, we really DO want to ignore the return value! - * GCC makes that extra hard, not even a (void) cast will do. */ - } - if (buff[0] == '\0') { - /* Buffer is unchanged, so we probably have called GNU strerror_r which - * returned a static constant string. Chances are that strerror will - * return the same static constant string and therefore be thread-safe. */ - return strerror(en); - } - } - return buff; /* sz is 0 *or* strerror_r wrote into the buffer */ -#elif COMPAT53_HAVE_STRERROR_S - /* for MSVC and other C11 implementations, use strerror_s since it's - * provided by default by the libraries */ - strerror_s(buff, sz, en); - return buff; -#else - /* fallback, but strerror is not guaranteed to be threadsafe due to modifying - * errno itself and some impls not locking a static buffer for it ... but most - * known systems have threadsafe errno: this might only change if the locale - * is changed out from under someone while this function is being called */ - (void)buff; - (void)sz; - return strerror(en); -#endif -} - - -COMPAT53_API int lua_absindex (lua_State *L, int i) { - if (i < 0 && i > LUA_REGISTRYINDEX) - i += lua_gettop(L) + 1; - return i; -} - - -static void compat53_call_lua (lua_State *L, char const code[], size_t len, - int nargs, int nret) { - lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)code); - if (lua_type(L, -1) != LUA_TFUNCTION) { - lua_pop(L, 1); - if (luaL_loadbuffer(L, code, len, "=none")) - lua_error(L); - lua_pushvalue(L, -1); - lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)code); - } - lua_insert(L, -nargs-1); - lua_call(L, nargs, nret); -} - - -static const char compat53_arith_code[] = - "local op,a,b=...\n" - "if op==0 then return a+b\n" - "elseif op==1 then return a-b\n" - "elseif op==2 then return a*b\n" - "elseif op==3 then return a/b\n" - "elseif op==4 then return a%b\n" - "elseif op==5 then return a^b\n" - "elseif op==6 then return -a\n" - "end\n"; - -COMPAT53_API void lua_arith (lua_State *L, int op) { - if (op < LUA_OPADD || op > LUA_OPUNM) - luaL_error(L, "invalid 'op' argument for lua_arith"); - luaL_checkstack(L, 5, "not enough stack slots"); - if (op == LUA_OPUNM) - lua_pushvalue(L, -1); - lua_pushnumber(L, op); - lua_insert(L, -3); - compat53_call_lua(L, compat53_arith_code, - sizeof(compat53_arith_code)-1, 3, 1); -} - - -static const char compat53_compare_code[] = - "local a,b=...\n" - "return a<=b\n"; - -COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op) { - int result = 0; - switch (op) { - case LUA_OPEQ: - return lua_equal(L, idx1, idx2); - case LUA_OPLT: - return lua_lessthan(L, idx1, idx2); - case LUA_OPLE: - luaL_checkstack(L, 5, "not enough stack slots"); - idx1 = lua_absindex(L, idx1); - idx2 = lua_absindex(L, idx2); - lua_pushvalue(L, idx1); - lua_pushvalue(L, idx2); - compat53_call_lua(L, compat53_compare_code, - sizeof(compat53_compare_code)-1, 2, 1); - result = lua_toboolean(L, -1); - lua_pop(L, 1); - return result; - default: - luaL_error(L, "invalid 'op' argument for lua_compare"); - } - return 0; -} - - -COMPAT53_API void lua_copy (lua_State *L, int from, int to) { - int abs_to = lua_absindex(L, to); - luaL_checkstack(L, 1, "not enough stack slots"); - lua_pushvalue(L, from); - lua_replace(L, abs_to); -} - - -COMPAT53_API void lua_len (lua_State *L, int i) { - switch (lua_type(L, i)) { - case LUA_TSTRING: - lua_pushnumber(L, (lua_Number)lua_objlen(L, i)); - break; - case LUA_TTABLE: - if (!luaL_callmeta(L, i, "__len")) - lua_pushnumber(L, (lua_Number)lua_objlen(L, i)); - break; - case LUA_TUSERDATA: - if (luaL_callmeta(L, i, "__len")) - break; - /* FALLTHROUGH */ - default: - luaL_error(L, "attempt to get length of a %s value", - lua_typename(L, lua_type(L, i))); - } -} - - -COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p) { - int abs_i = lua_absindex(L, i); - lua_pushlightuserdata(L, (void*)p); - lua_rawget(L, abs_i); - return lua_type(L, -1); -} - -COMPAT53_API void lua_rawsetp (lua_State *L, int i, const void *p) { - int abs_i = lua_absindex(L, i); - luaL_checkstack(L, 1, "not enough stack slots"); - lua_pushlightuserdata(L, (void*)p); - lua_insert(L, -2); - lua_rawset(L, abs_i); -} - - -COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum) { - lua_Number n = lua_tonumber(L, i); - if (isnum != NULL) { - *isnum = (n != 0 || lua_isnumber(L, i)); - } - return n; -} - - -COMPAT53_API void luaL_checkversion (lua_State *L) { - (void)L; -} - - -COMPAT53_API void luaL_checkstack (lua_State *L, int sp, const char *msg) { - if (!lua_checkstack(L, sp+LUA_MINSTACK)) { - if (msg != NULL) - luaL_error(L, "stack overflow (%s)", msg); - else { - lua_pushliteral(L, "stack overflow"); - lua_error(L); - } - } -} - - -COMPAT53_API int luaL_getsubtable (lua_State *L, int i, const char *name) { - int abs_i = lua_absindex(L, i); - luaL_checkstack(L, 3, "not enough stack slots"); - lua_pushstring(L, name); - lua_gettable(L, abs_i); - if (lua_istable(L, -1)) - return 1; - lua_pop(L, 1); - lua_newtable(L); - lua_pushstring(L, name); - lua_pushvalue(L, -2); - lua_settable(L, abs_i); - return 0; -} - - -COMPAT53_API lua_Integer luaL_len (lua_State *L, int i) { - lua_Integer res = 0; - int isnum = 0; - luaL_checkstack(L, 1, "not enough stack slots"); - lua_len(L, i); - res = lua_tointegerx(L, -1, &isnum); - lua_pop(L, 1); - if (!isnum) - luaL_error(L, "object length is not an integer"); - return res; -} - - -COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { - luaL_checkstack(L, nup+1, "too many upvalues"); - for (; l->name != NULL; l++) { /* fill the table with given functions */ - int i; - lua_pushstring(L, l->name); - for (i = 0; i < nup; i++) /* copy upvalues to the top */ - lua_pushvalue(L, -(nup + 1)); - lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ - lua_settable(L, -(nup + 3)); /* table must be below the upvalues, the name and the closure */ - } - lua_pop(L, nup); /* remove upvalues */ -} - - -COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname) { - luaL_checkstack(L, 1, "not enough stack slots"); - luaL_getmetatable(L, tname); - lua_setmetatable(L, -2); -} - - -COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname) { - void *p = lua_touserdata(L, i); - luaL_checkstack(L, 2, "not enough stack slots"); - if (p == NULL || !lua_getmetatable(L, i)) - return NULL; - else { - int res = 0; - luaL_getmetatable(L, tname); - res = lua_rawequal(L, -1, -2); - lua_pop(L, 2); - if (!res) - p = NULL; - } - return p; -} - - -static int compat53_countlevels (lua_State *L) { - lua_Debug ar; - int li = 1, le = 1; - /* find an upper bound */ - while (lua_getstack(L, le, &ar)) { li = le; le *= 2; } - /* do a binary search */ - while (li < le) { - int m = (li + le)/2; - if (lua_getstack(L, m, &ar)) li = m + 1; - else le = m; - } - return le - 1; -} - -static int compat53_findfield (lua_State *L, int objidx, int level) { - if (level == 0 || !lua_istable(L, -1)) - return 0; /* not found */ - lua_pushnil(L); /* start 'next' loop */ - while (lua_next(L, -2)) { /* for each pair in table */ - if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */ - if (lua_rawequal(L, objidx, -1)) { /* found object? */ - lua_pop(L, 1); /* remove value (but keep name) */ - return 1; - } - else if (compat53_findfield(L, objidx, level - 1)) { /* try recursively */ - lua_remove(L, -2); /* remove table (but keep name) */ - lua_pushliteral(L, "."); - lua_insert(L, -2); /* place '.' between the two names */ - lua_concat(L, 3); - return 1; - } - } - lua_pop(L, 1); /* remove value */ - } - return 0; /* not found */ -} - -static int compat53_pushglobalfuncname (lua_State *L, lua_Debug *ar) { - int top = lua_gettop(L); - lua_getinfo(L, "f", ar); /* push function */ - lua_pushvalue(L, LUA_GLOBALSINDEX); - if (compat53_findfield(L, top + 1, 2)) { - lua_copy(L, -1, top + 1); /* move name to proper place */ - lua_pop(L, 2); /* remove pushed values */ - return 1; - } - else { - lua_settop(L, top); /* remove function and global table */ - return 0; - } -} - -static void compat53_pushfuncname (lua_State *L, lua_Debug *ar) { - if (*ar->namewhat != '\0') /* is there a name? */ - lua_pushfstring(L, "function " LUA_QS, ar->name); - else if (*ar->what == 'm') /* main? */ - lua_pushliteral(L, "main chunk"); - else if (*ar->what == 'C') { - if (compat53_pushglobalfuncname(L, ar)) { - lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1)); - lua_remove(L, -2); /* remove name */ - } - else - lua_pushliteral(L, "?"); - } - else - lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); -} - -#define COMPAT53_LEVELS1 12 /* size of the first part of the stack */ -#define COMPAT53_LEVELS2 10 /* size of the second part of the stack */ - -COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1, - const char *msg, int level) { - lua_Debug ar; - int top = lua_gettop(L); - int numlevels = compat53_countlevels(L1); - int mark = (numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2) ? COMPAT53_LEVELS1 : 0; - if (msg) lua_pushfstring(L, "%s\n", msg); - lua_pushliteral(L, "stack traceback:"); - while (lua_getstack(L1, level++, &ar)) { - 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, "Slnt", &ar); - lua_pushfstring(L, "\n\t%s:", ar.short_src); - if (ar.currentline > 0) - lua_pushfstring(L, "%d:", ar.currentline); - lua_pushliteral(L, " in "); - compat53_pushfuncname(L, &ar); - lua_concat(L, lua_gettop(L) - top); - } - } - lua_concat(L, lua_gettop(L) - top); -} - - -COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname) { - const char *serr = NULL; - int en = errno; /* calls to Lua API may change this value */ - char buf[512] = { 0 }; - if (stat) { - lua_pushboolean(L, 1); - return 1; - } - else { - lua_pushnil(L); - serr = compat53_strerror(en, buf, sizeof(buf)); - if (fname) - lua_pushfstring(L, "%s: %s", fname, serr); - else - lua_pushstring(L, serr); - lua_pushnumber(L, (lua_Number)en); - return 3; - } -} - - -static int compat53_checkmode (lua_State *L, const char *mode, const char *modename, int err) { - if (mode && strchr(mode, modename[0]) == NULL) { - lua_pushfstring(L, "attempt to load a %s chunk (mode is '%s')", modename, mode); - return err; - } - return LUA_OK; -} - - -typedef struct { - lua_Reader reader; - void *ud; - int has_peeked_data; - const char *peeked_data; - size_t peeked_data_size; -} compat53_reader_data; - - -static const char *compat53_reader (lua_State *L, void *ud, size_t *size) { - compat53_reader_data *data = (compat53_reader_data *)ud; - if (data->has_peeked_data) { - data->has_peeked_data = 0; - *size = data->peeked_data_size; - return data->peeked_data; - } else - return data->reader(L, data->ud, size); -} - - -COMPAT53_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *source, const char *mode) { - int status = LUA_OK; - compat53_reader_data compat53_data = { 0, NULL, 1, 0, 0 }; - compat53_data.reader = reader; - compat53_data.ud = data; - compat53_data.peeked_data = reader(L, data, &(compat53_data.peeked_data_size)); - if (compat53_data.peeked_data && compat53_data.peeked_data_size && - compat53_data.peeked_data[0] == LUA_SIGNATURE[0]) /* binary file? */ - status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX); - else - status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX); - if (status != LUA_OK) - return status; - /* we need to call the original 5.1 version of lua_load! */ -#undef lua_load - return lua_load(L, compat53_reader, &compat53_data, source); -#define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53) -} - - -typedef struct { - int n; /* number of pre-read characters */ - FILE *f; /* file being read */ - char buff[COMPAT53_LUA_FILE_BUFFER_SIZE]; /* area for reading file */ -} compat53_LoadF; - - -static const char *compat53_getF (lua_State *L, void *ud, size_t *size) { - compat53_LoadF *lf = (compat53_LoadF *)ud; - (void)L; /* not used */ - if (lf->n > 0) { /* are there pre-read characters to be read? */ - *size = lf->n; /* return them (chars already in buffer) */ - lf->n = 0; /* no more pre-read characters */ - } - else { /* read a block from file */ - /* 'fread' can return > 0 *and* set the EOF flag. If next call to - 'compat53_getF' called 'fread', it might still wait for user input. - The next check avoids this problem. */ - if (feof(lf->f)) return NULL; - *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */ - } - return lf->buff; -} - - -static int compat53_errfile (lua_State *L, const char *what, int fnameindex) { - char buf[512] = {0}; - const char *serr = compat53_strerror(errno, buf, sizeof(buf)); - const char *filename = lua_tostring(L, fnameindex) + 1; - lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); - lua_remove(L, fnameindex); - return LUA_ERRFILE; -} - - -static int compat53_skipBOM (compat53_LoadF *lf) { - const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */ - int c; - lf->n = 0; - do { - c = getc(lf->f); - if (c == EOF || c != *(const unsigned char *)p++) return c; - lf->buff[lf->n++] = (char)c; /* to be read by the parser */ - } while (*p != '\0'); - lf->n = 0; /* prefix matched; discard it */ - return getc(lf->f); /* return next character */ -} - - -/* -** reads the first character of file 'f' and skips an optional BOM mark -** in its beginning plus its first line if it starts with '#'. Returns -** true if it skipped the first line. In any case, '*cp' has the -** first "valid" character of the file (after the optional BOM and -** a first-line comment). -*/ -static int compat53_skipcomment (compat53_LoadF *lf, int *cp) { - int c = *cp = compat53_skipBOM(lf); - if (c == '#') { /* first line is a comment (Unix exec. file)? */ - do { /* skip first line */ - c = getc(lf->f); - } while (c != EOF && c != '\n'); - *cp = getc(lf->f); /* skip end-of-line, if present */ - return 1; /* there was a comment */ - } - else return 0; /* no comment */ -} - - -COMPAT53_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode) { - compat53_LoadF lf; - int status, readstatus; - int c; - int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ - if (filename == NULL) { - lua_pushliteral(L, "=stdin"); - lf.f = stdin; - } - else { - lua_pushfstring(L, "@%s", filename); -#if defined(_MSC_VER) - /* This code is here to stop a deprecation error that stops builds - * if a certain macro is defined. While normally not caring would - * be best, some header-only libraries and builds can't afford to - * dictate this to the user. A quick check shows that fopen_s this - * goes back to VS 2005, and _fsopen goes back to VS 2003 .NET, - * possibly even before that so we don't need to do any version - * number checks, since this has been there since forever. */ - - /* TO USER: if you want the behavior of typical fopen_s/fopen, - * which does lock the file on VC++, define the macro used below to 0 */ -#if COMPAT53_FOPEN_NO_LOCK - lf.f = _fsopen(filename, "r", _SH_DENYNO); /* do not lock the file in any way */ - if (lf.f == NULL) - return compat53_errfile(L, "open", fnameindex); -#else /* use default locking version */ - if (fopen_s(&lf.f, filename, "r") != 0) - return compat53_errfile(L, "open", fnameindex); -#endif /* Locking vs. No-locking fopen variants */ -#else - lf.f = fopen(filename, "r"); /* default stdlib doesn't forcefully lock files here */ - if (lf.f == NULL) return compat53_errfile(L, "open", fnameindex); -#endif - } - if (compat53_skipcomment(&lf, &c)) /* read initial portion */ - lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ - if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ -#if defined(_MSC_VER) - if (freopen_s(&lf.f, filename, "rb", lf.f) != 0) - return compat53_errfile(L, "reopen", fnameindex); -#else - lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ - if (lf.f == NULL) return compat53_errfile(L, "reopen", fnameindex); -#endif - compat53_skipcomment(&lf, &c); /* re-read initial portion */ - } - if (c != EOF) - lf.buff[lf.n++] = (char)c; /* 'c' is the first character of the stream */ - status = lua_load(L, &compat53_getF, &lf, lua_tostring(L, -1), mode); - readstatus = ferror(lf.f); - if (filename) fclose(lf.f); /* close file (even in case of errors) */ - if (readstatus) { - lua_settop(L, fnameindex); /* ignore results from 'lua_load' */ - return compat53_errfile(L, "read", fnameindex); - } - lua_remove(L, fnameindex); - return status; -} - - -COMPAT53_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode) { - int status = LUA_OK; - if (sz > 0 && buff[0] == LUA_SIGNATURE[0]) { - status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX); - } - else { - status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX); - } - if (status != LUA_OK) - return status; - return luaL_loadbuffer(L, buff, sz, name); -} - - -#if !defined(l_inspectstat) && \ - (defined(unix) || defined(__unix) || defined(__unix__) || \ - defined(__TOS_AIX__) || defined(_SYSTYPE_BSD) || \ - (defined(__APPLE__) && defined(__MACH__))) -/* some form of unix; check feature macros in unistd.h for details */ -# include -/* check posix version; the relevant include files and macros probably - * were available before 2001, but I'm not sure */ -# if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L -# include -# define l_inspectstat(stat,what) \ - if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \ - else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; } -# endif -#endif - -/* provide default (no-op) version */ -#if !defined(l_inspectstat) -# define l_inspectstat(stat,what) ((void)0) -#endif - - -COMPAT53_API int luaL_execresult (lua_State *L, int stat) { - const char *what = "exit"; - if (stat == -1) - return luaL_fileresult(L, 0, NULL); - else { - l_inspectstat(stat, what); - if (*what == 'e' && stat == 0) - lua_pushboolean(L, 1); - else - lua_pushnil(L); - lua_pushstring(L, what); - lua_pushinteger(L, stat); - return 3; - } -} - - -COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B) { - /* make it crash if used via pointer to a 5.1-style luaL_Buffer */ - B->b.p = NULL; - B->b.L = NULL; - B->b.lvl = 0; - /* reuse the buffer from the 5.1-style luaL_Buffer though! */ - B->ptr = B->b.buffer; - B->capacity = LUAL_BUFFERSIZE; - B->nelems = 0; - B->L2 = L; -} - - -COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s) { - if (B->capacity - B->nelems < s) { /* needs to grow */ - char* newptr = NULL; - size_t newcap = B->capacity * 2; - if (newcap - B->nelems < s) - newcap = B->nelems + s; - if (newcap < B->capacity) /* overflow */ - luaL_error(B->L2, "buffer too large"); - newptr = (char*)lua_newuserdata(B->L2, newcap); - memcpy(newptr, B->ptr, B->nelems); - if (B->ptr != B->b.buffer) - lua_replace(B->L2, -2); /* remove old buffer */ - B->ptr = newptr; - B->capacity = newcap; - } - return B->ptr+B->nelems; -} - - -COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l) { - memcpy(luaL_prepbuffsize(B, l), s, l); - luaL_addsize(B, l); -} - - -COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B) { - size_t len = 0; - const char *s = lua_tolstring(B->L2, -1, &len); - if (!s) - luaL_error(B->L2, "cannot convert value to string"); - if (B->ptr != B->b.buffer) - lua_insert(B->L2, -2); /* userdata buffer must be at stack top */ - luaL_addlstring(B, s, len); - lua_remove(B->L2, B->ptr != B->b.buffer ? -2 : -1); -} - - -void luaL_pushresult (luaL_Buffer_53 *B) { - lua_pushlstring(B->L2, B->ptr, B->nelems); - if (B->ptr != B->b.buffer) - lua_replace(B->L2, -2); /* remove userdata buffer */ -} - - -#endif /* Lua 5.1 */ - - - -/* definitions for Lua 5.1 and Lua 5.2 */ -#if defined( LUA_VERSION_NUM ) && LUA_VERSION_NUM <= 502 - - -COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i) { - index = lua_absindex(L, index); - lua_pushinteger(L, i); - lua_gettable(L, index); - return lua_type(L, -1); -} - - -#ifndef LUA_EXTRASPACE -#define LUA_EXTRASPACE (sizeof(void*)) -#endif - -COMPAT53_API void *lua_getextraspace (lua_State *L) { - int is_main = 0; - void *ptr = NULL; - luaL_checkstack(L, 4, "not enough stack slots available"); - lua_pushliteral(L, "__compat53_extraspace"); - lua_pushvalue(L, -1); - lua_rawget(L, LUA_REGISTRYINDEX); - if (!lua_istable(L, -1)) { - lua_pop(L, 1); - lua_createtable(L, 0, 2); - lua_createtable(L, 0, 1); - lua_pushliteral(L, "k"); - lua_setfield(L, -2, "__mode"); - lua_setmetatable(L, -2); - lua_pushvalue(L, -2); - lua_pushvalue(L, -2); - lua_rawset(L, LUA_REGISTRYINDEX); - } - lua_replace(L, -2); - is_main = lua_pushthread(L); - lua_rawget(L, -2); - ptr = lua_touserdata(L, -1); - if (!ptr) { - lua_pop(L, 1); - ptr = lua_newuserdata(L, LUA_EXTRASPACE); - if (is_main) { - memset(ptr, '\0', LUA_EXTRASPACE); - lua_pushthread(L); - lua_pushvalue(L, -2); - lua_rawset(L, -4); - lua_pushboolean(L, 1); - lua_pushvalue(L, -2); - lua_rawset(L, -4); - } else { - void* mptr = NULL; - lua_pushboolean(L, 1); - lua_rawget(L, -3); - mptr = lua_touserdata(L, -1); - if (mptr) - memcpy(ptr, mptr, LUA_EXTRASPACE); - else - memset(ptr, '\0', LUA_EXTRASPACE); - lua_pop(L, 1); - lua_pushthread(L); - lua_pushvalue(L, -2); - lua_rawset(L, -4); - } - } - lua_pop(L, 2); - return ptr; -} - - -COMPAT53_API int lua_isinteger (lua_State *L, int index) { - if (lua_type(L, index) == LUA_TNUMBER) { - lua_Number n = lua_tonumber(L, index); - lua_Integer i = lua_tointeger(L, index); - if (i == n) - return 1; - } - return 0; -} - - -COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum) { - int ok = 0; - lua_Number n = lua_tonumberx(L, i, &ok); - if (ok) { - if (n == (lua_Integer)n) { - if (isnum) - *isnum = 1; - return (lua_Integer)n; - } - } - if (isnum) - *isnum = 0; - return 0; -} - - -static void compat53_reverse (lua_State *L, int a, int b) { - for (; a < b; ++a, --b) { - lua_pushvalue(L, a); - lua_pushvalue(L, b); - lua_replace(L, a); - lua_replace(L, b); - } -} - - -COMPAT53_API void lua_rotate (lua_State *L, int idx, int n) { - int n_elems = 0; - idx = lua_absindex(L, idx); - n_elems = lua_gettop(L)-idx+1; - if (n < 0) - n += n_elems; - if ( n > 0 && n < n_elems) { - luaL_checkstack(L, 2, "not enough stack slots available"); - n = n_elems - n; - compat53_reverse(L, idx, idx+n-1); - compat53_reverse(L, idx+n, idx+n_elems-1); - compat53_reverse(L, idx, idx+n_elems-1); - } -} - - -COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i) { - luaL_checkstack(L, 1, "not enough stack slots available"); - index = lua_absindex(L, index); - lua_pushinteger(L, i); - lua_insert(L, -2); - lua_settable(L, index); -} - - -#if !defined(lua_str2number) -# define lua_str2number(s, p) strtod((s), (p)) -#endif - -COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s) { - char* endptr; - lua_Number n = lua_str2number(s, &endptr); - if (endptr != s) { - while (*endptr != '\0' && isspace((unsigned char)*endptr)) - ++endptr; - if (*endptr == '\0') { - lua_pushnumber(L, n); - return endptr - s + 1; - } - } - return 0; -} - - -COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { - if (!luaL_callmeta(L, idx, "__tostring")) { - int t = lua_type(L, idx), tt = 0; - char const* name = NULL; - switch (t) { - case LUA_TNIL: - lua_pushliteral(L, "nil"); - break; - case LUA_TSTRING: - case LUA_TNUMBER: - lua_pushvalue(L, idx); - break; - case LUA_TBOOLEAN: - if (lua_toboolean(L, idx)) - lua_pushliteral(L, "true"); - else - lua_pushliteral(L, "false"); - break; - default: - tt = luaL_getmetafield(L, idx, "__name"); - name = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : lua_typename(L, t); - lua_pushfstring(L, "%s: %p", name, lua_topointer(L, idx)); - if (tt != LUA_TNIL) - lua_replace(L, -2); - break; - } - } else { - if (!lua_isstring(L, -1)) - luaL_error(L, "'__tostring' must return a string"); - } - return lua_tolstring(L, -1, len); -} - - -COMPAT53_API void luaL_requiref (lua_State *L, const char *modname, - lua_CFunction openf, int glb) { - luaL_checkstack(L, 3, "not enough stack slots available"); - luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED"); - if (lua_getfield(L, -1, modname) == LUA_TNIL) { - lua_pop(L, 1); - lua_pushcfunction(L, openf); - lua_pushstring(L, modname); -#ifndef COMPAT53_LUAJIT - lua_call(L, 1, 1); - lua_pushvalue(L, -1); - lua_setfield(L, -3, modname); -#else - lua_call(L, 1, 0); - lua_getfield(L, -1, modname); -#endif /* COMPAT53_LUAJIT */ - } - if (glb) { - lua_pushvalue(L, -1); - lua_setglobal(L, modname); - } - lua_replace(L, -2); -} - - -#endif /* Lua 5.1 and 5.2 */ - - -#endif /* COMPAT53_C_ */ - - -/********************************************************************* -* This file contains parts of Lua 5.2's and Lua 5.3's source code: -* -* Copyright (C) 1994-2014 Lua.org, PUC-Rio. -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be -* included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*********************************************************************/ - diff --git a/src/ffi/shim/compat-5.3.h b/src/ffi/shim/compat-5.3.h deleted file mode 100644 index b730a4b..0000000 --- a/src/ffi/shim/compat-5.3.h +++ /dev/null @@ -1,424 +0,0 @@ -#ifndef COMPAT53_H_ -#define COMPAT53_H_ - -#include -#include -#include -#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP) -extern "C" { -#endif -#include -#include -#include -#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP) -} -#endif - - -#undef COMPAT53_INCLUDE_SOURCE -#if defined(COMPAT53_PREFIX) -/* - change the symbol names of functions to avoid linker conflicts - * - compat-5.3.c needs to be compiled (and linked) separately - */ -# if !defined(COMPAT53_API) -# define COMPAT53_API extern -# endif -#else /* COMPAT53_PREFIX */ -/* - make all functions static and include the source. - * - compat-5.3.c doesn't need to be compiled (and linked) separately - */ -# define COMPAT53_PREFIX compat53 -# undef COMPAT53_API -# if defined(__GNUC__) || defined(__clang__) -# define COMPAT53_API __attribute__((__unused__)) static -# else -# define COMPAT53_API static -# endif -# define COMPAT53_INCLUDE_SOURCE -#endif /* COMPAT53_PREFIX */ - -#define COMPAT53_CONCAT_HELPER(a, b) a##b -#define COMPAT53_CONCAT(a, b) COMPAT53_CONCAT_HELPER(a, b) - - - -/* declarations for Lua 5.1 */ -#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 - -/* XXX not implemented: - * lua_arith (new operators) - * lua_upvalueid - * lua_upvaluejoin - * lua_version - * lua_yieldk - */ - -#ifndef LUA_OK -# define LUA_OK 0 -#endif -#ifndef LUA_OPADD -# define LUA_OPADD 0 -#endif -#ifndef LUA_OPSUB -# define LUA_OPSUB 1 -#endif -#ifndef LUA_OPMUL -# define LUA_OPMUL 2 -#endif -#ifndef LUA_OPDIV -# define LUA_OPDIV 3 -#endif -#ifndef LUA_OPMOD -# define LUA_OPMOD 4 -#endif -#ifndef LUA_OPPOW -# define LUA_OPPOW 5 -#endif -#ifndef LUA_OPUNM -# define LUA_OPUNM 6 -#endif -#ifndef LUA_OPEQ -# define LUA_OPEQ 0 -#endif -#ifndef LUA_OPLT -# define LUA_OPLT 1 -#endif -#ifndef LUA_OPLE -# define LUA_OPLE 2 -#endif - -/* LuaJIT/Lua 5.1 does not have the updated - * error codes for thread status/function returns (but some patched versions do) - * define it only if it's not found - */ -#if !defined(LUA_ERRGCMM) -/* Use + 2 because in some versions of Lua (Lua 5.1) - * LUA_ERRFILE is defined as (LUA_ERRERR+1) - * so we need to avoid it (LuaJIT might have something at this - * integer value too) - */ -# define LUA_ERRGCMM (LUA_ERRERR + 2) -#endif /* LUA_ERRGCMM define */ - -typedef size_t lua_Unsigned; - -typedef struct luaL_Buffer_53 { - luaL_Buffer b; /* make incorrect code crash! */ - char *ptr; - size_t nelems; - size_t capacity; - lua_State *L2; -} luaL_Buffer_53; -#define luaL_Buffer luaL_Buffer_53 - -/* In PUC-Rio 5.1, userdata is a simple FILE* - * In LuaJIT, it's a struct where the first member is a FILE* - * We can't support the `closef` member - */ -typedef struct luaL_Stream { - FILE *f; -} luaL_Stream; - -#define lua_absindex COMPAT53_CONCAT(COMPAT53_PREFIX, _absindex) -COMPAT53_API int lua_absindex (lua_State *L, int i); - -#define lua_arith COMPAT53_CONCAT(COMPAT53_PREFIX, _arith) -COMPAT53_API void lua_arith (lua_State *L, int op); - -#define lua_compare COMPAT53_CONCAT(COMPAT53_PREFIX, _compare) -COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op); - -#define lua_copy COMPAT53_CONCAT(COMPAT53_PREFIX, _copy) -COMPAT53_API void lua_copy (lua_State *L, int from, int to); - -#define lua_getuservalue(L, i) \ - (lua_getfenv((L), (i)), lua_type((L), -1)) -#define lua_setuservalue(L, i) \ - (luaL_checktype((L), -1, LUA_TTABLE), lua_setfenv((L), (i))) - -#define lua_len COMPAT53_CONCAT(COMPAT53_PREFIX, _len) -COMPAT53_API void lua_len (lua_State *L, int i); - -#define lua_pushstring(L, s) \ - (lua_pushstring((L), (s)), lua_tostring((L), -1)) - -#define lua_pushlstring(L, s, len) \ - ((((len) == 0) ? lua_pushlstring((L), "", 0) : lua_pushlstring((L), (s), (len))), lua_tostring((L), -1)) - -#ifndef luaL_newlibtable -# define luaL_newlibtable(L, l) \ - (lua_createtable((L), 0, sizeof((l))/sizeof(*(l))-1)) -#endif -#ifndef luaL_newlib -# define luaL_newlib(L, l) \ - (luaL_newlibtable((L), (l)), luaL_register((L), NULL, (l))) -#endif - -#define lua_pushglobaltable(L) \ - lua_pushvalue((L), LUA_GLOBALSINDEX) - -#define lua_rawgetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawgetp) -COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p); - -#define lua_rawsetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawsetp) -COMPAT53_API void lua_rawsetp(lua_State *L, int i, const void *p); - -#define lua_rawlen(L, i) lua_objlen((L), (i)) - -#define lua_tointeger(L, i) lua_tointegerx((L), (i), NULL) - -#define lua_tonumberx COMPAT53_CONCAT(COMPAT53_PREFIX, _tonumberx) -COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum); - -#define luaL_checkversion COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkversion) -COMPAT53_API void luaL_checkversion (lua_State *L); - -#define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53) -COMPAT53_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char* source, const char* mode); - -#define luaL_loadfilex COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadfilex) -COMPAT53_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode); - -#define luaL_loadbufferx COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadbufferx) -COMPAT53_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode); - -#define luaL_checkstack COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkstack_53) -COMPAT53_API void luaL_checkstack (lua_State *L, int sp, const char *msg); - -#define luaL_getsubtable COMPAT53_CONCAT(COMPAT53_PREFIX, L_getsubtable) -COMPAT53_API int luaL_getsubtable (lua_State* L, int i, const char *name); - -#define luaL_len COMPAT53_CONCAT(COMPAT53_PREFIX, L_len) -COMPAT53_API lua_Integer luaL_len (lua_State *L, int i); - -#define luaL_setfuncs COMPAT53_CONCAT(COMPAT53_PREFIX, L_setfuncs) -COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup); - -#define luaL_setmetatable COMPAT53_CONCAT(COMPAT53_PREFIX, L_setmetatable) -COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname); - -#define luaL_testudata COMPAT53_CONCAT(COMPAT53_PREFIX, L_testudata) -COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname); - -#define luaL_traceback COMPAT53_CONCAT(COMPAT53_PREFIX, L_traceback) -COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level); - -#define luaL_fileresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_fileresult) -COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname); - -#define luaL_execresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_execresult) -COMPAT53_API int luaL_execresult (lua_State *L, int stat); - -#define lua_callk(L, na, nr, ctx, cont) \ - ((void)(ctx), (void)(cont), lua_call((L), (na), (nr))) -#define lua_pcallk(L, na, nr, err, ctx, cont) \ - ((void)(ctx), (void)(cont), lua_pcall((L), (na), (nr), (err))) - -#define lua_resume(L, from, nargs) \ - ((void)(from), lua_resume((L), (nargs))) - -#define luaL_buffinit COMPAT53_CONCAT(COMPAT53_PREFIX, _buffinit_53) -COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B); - -#define luaL_prepbuffsize COMPAT53_CONCAT(COMPAT53_PREFIX, _prepbufsize_53) -COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s); - -#define luaL_addlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _addlstring_53) -COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l); - -#define luaL_addvalue COMPAT53_CONCAT(COMPAT53_PREFIX, _addvalue_53) -COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B); - -#define luaL_pushresult COMPAT53_CONCAT(COMPAT53_PREFIX, _pushresult_53) -COMPAT53_API void luaL_pushresult (luaL_Buffer_53 *B); - -#undef luaL_buffinitsize -#define luaL_buffinitsize(L, B, s) \ - (luaL_buffinit((L), (B)), luaL_prepbuffsize((B), (s))) - -#undef luaL_prepbuffer -#define luaL_prepbuffer(B) \ - luaL_prepbuffsize((B), LUAL_BUFFERSIZE) - -#undef luaL_addchar -#define luaL_addchar(B, c) \ - ((void)((B)->nelems < (B)->capacity || luaL_prepbuffsize((B), 1)), \ - ((B)->ptr[(B)->nelems++] = (c))) - -#undef luaL_addsize -#define luaL_addsize(B, s) \ - ((B)->nelems += (s)) - -#undef luaL_addstring -#define luaL_addstring(B, s) \ - luaL_addlstring((B), (s), strlen((s))) - -#undef luaL_pushresultsize -#define luaL_pushresultsize(B, s) \ - (luaL_addsize((B), (s)), luaL_pushresult((B))) - -#if defined(LUA_COMPAT_APIINTCASTS) -#define lua_pushunsigned(L, n) \ - lua_pushinteger((L), (lua_Integer)(n)) -#define lua_tounsignedx(L, i, is) \ - ((lua_Unsigned)lua_tointegerx((L), (i), (is))) -#define lua_tounsigned(L, i) \ - lua_tounsignedx((L), (i), NULL) -#define luaL_checkunsigned(L, a) \ - ((lua_Unsigned)luaL_checkinteger((L), (a))) -#define luaL_optunsigned(L, a, d) \ - ((lua_Unsigned)luaL_optinteger((L), (a), (lua_Integer)(d))) -#endif - -#endif /* Lua 5.1 only */ - - - -/* declarations for Lua 5.1 and 5.2 */ -#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502 - -typedef int lua_KContext; - -typedef int (*lua_KFunction)(lua_State *L, int status, lua_KContext ctx); - -#define lua_dump(L, w, d, s) \ - ((void)(s), lua_dump((L), (w), (d))) - -#define lua_getfield(L, i, k) \ - (lua_getfield((L), (i), (k)), lua_type((L), -1)) - -#define lua_gettable(L, i) \ - (lua_gettable((L), (i)), lua_type((L), -1)) - -#define lua_geti COMPAT53_CONCAT(COMPAT53_PREFIX, _geti) -COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i); - -#define lua_getextraspace COMPAT53_CONCAT(COMPAT53_PREFIX, _getextraspace) -COMPAT53_API void *lua_getextraspace (lua_State *L); - -#define lua_isinteger COMPAT53_CONCAT(COMPAT53_PREFIX, _isinteger) -COMPAT53_API int lua_isinteger (lua_State *L, int index); - -#define lua_tointegerx COMPAT53_CONCAT(COMPAT53_PREFIX, _tointegerx_53) -COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum); - -#define lua_numbertointeger(n, p) \ - ((*(p) = (lua_Integer)(n)), 1) - -#define lua_rawget(L, i) \ - (lua_rawget((L), (i)), lua_type((L), -1)) - -#define lua_rawgeti(L, i, n) \ - (lua_rawgeti((L), (i), (n)), lua_type((L), -1)) - -#define lua_rotate COMPAT53_CONCAT(COMPAT53_PREFIX, _rotate) -COMPAT53_API void lua_rotate (lua_State *L, int idx, int n); - -#define lua_seti COMPAT53_CONCAT(COMPAT53_PREFIX, _seti) -COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i); - -#define lua_stringtonumber COMPAT53_CONCAT(COMPAT53_PREFIX, _stringtonumber) -COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s); - -#define luaL_tolstring COMPAT53_CONCAT(COMPAT53_PREFIX, L_tolstring) -COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len); - -#define luaL_getmetafield(L, o, e) \ - (luaL_getmetafield((L), (o), (e)) ? lua_type((L), -1) : LUA_TNIL) - -#define luaL_newmetatable(L, tn) \ - (luaL_newmetatable((L), (tn)) ? (lua_pushstring((L), (tn)), lua_setfield((L), -2, "__name"), 1) : 0) - -#define luaL_requiref COMPAT53_CONCAT(COMPAT53_PREFIX, L_requiref_53) -COMPAT53_API void luaL_requiref (lua_State *L, const char *modname, - lua_CFunction openf, int glb ); - -#endif /* Lua 5.1 and Lua 5.2 */ - - - -/* declarations for Lua 5.2 */ -#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 502 - -/* XXX not implemented: - * lua_isyieldable - * lua_arith (new operators) - * lua_pushfstring (new formats) - */ - -#define lua_getglobal(L, n) \ - (lua_getglobal((L), (n)), lua_type((L), -1)) - -#define lua_getuservalue(L, i) \ - (lua_getuservalue((L), (i)), lua_type((L), -1)) - -#define lua_pushlstring(L, s, len) \ - (((len) == 0) ? lua_pushlstring((L), "", 0) : lua_pushlstring((L), (s), (len))) - -#define lua_rawgetp(L, i, p) \ - (lua_rawgetp((L), (i), (p)), lua_type((L), -1)) - -#define LUA_KFUNCTION(_name) \ - static int (_name)(lua_State *L, int status, lua_KContext ctx); \ - static int (_name ## _52)(lua_State *L) { \ - lua_KContext ctx; \ - int status = lua_getctx(L, &ctx); \ - return (_name)(L, status, ctx); \ - } \ - static int (_name)(lua_State *L, int status, lua_KContext ctx) - -#define lua_pcallk(L, na, nr, err, ctx, cont) \ - lua_pcallk((L), (na), (nr), (err), (ctx), cont ## _52) - -#define lua_callk(L, na, nr, ctx, cont) \ - lua_callk((L), (na), (nr), (ctx), cont ## _52) - -#define lua_yieldk(L, nr, ctx, cont) \ - lua_yieldk((L), (nr), (ctx), cont ## _52) - -#ifdef lua_call -# undef lua_call -# define lua_call(L, na, nr) \ - (lua_callk)((L), (na), (nr), 0, NULL) -#endif - -#ifdef lua_pcall -# undef lua_pcall -# define lua_pcall(L, na, nr, err) \ - (lua_pcallk)((L), (na), (nr), (err), 0, NULL) -#endif - -#ifdef lua_yield -# undef lua_yield -# define lua_yield(L, nr) \ - (lua_yieldk)((L), (nr), 0, NULL) -#endif - -#endif /* Lua 5.2 only */ - - - -/* other Lua versions */ -#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 501 || LUA_VERSION_NUM > 504 - -# error "unsupported Lua version (i.e. not Lua 5.1, 5.2, 5.3, or 5.4)" - -#endif /* other Lua versions except 5.1, 5.2, 5.3, and 5.4 */ - - - -/* helper macro for defining continuation functions (for every version - * *except* Lua 5.2) */ -#ifndef LUA_KFUNCTION -#define LUA_KFUNCTION(_name) \ - static int (_name)(lua_State *L, int status, lua_KContext ctx) -#endif - - -#if defined(COMPAT53_INCLUDE_SOURCE) -# include "compat-5.3.c" -#endif - - -#endif /* COMPAT53_H_ */ - diff --git a/src/ffi/shim/shim.c b/src/ffi/shim/shim.c deleted file mode 100644 index fc77047..0000000 --- a/src/ffi/shim/shim.c +++ /dev/null @@ -1,515 +0,0 @@ -// The MIT License (MIT) -// -// Copyright (c) 2019-2021 A. Orlenko -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -#include -#include - -#include "compat-5.3.h" - -size_t MLUA_WRAPPED_ERROR_SIZE = 0; -size_t MLUA_WRAPPED_PANIC_SIZE = 0; - -const void *MLUA_WRAPPED_ERROR_KEY = NULL; -const void *MLUA_WRAPPED_PANIC_KEY = NULL; - -extern void wrapped_error_traceback(lua_State *L, int error_idx, - int traceback_idx); - -extern int mlua_hook_proc(lua_State *L, lua_Debug *ar); - -#define max(a, b) (a > b ? a : b) - -// I believe luaL_traceback < 5.4 requires this much free stack to not error. -// 5.4 uses luaL_Buffer -const int LUA_TRACEBACK_STACK = 11; - -typedef struct { - const char *data; - size_t len; -} StringArg; - -static void handle_wrapped_error(lua_State *L) { - if (lua_checkstack(L, LUA_TRACEBACK_STACK) != 0) { - luaL_traceback(L, L, NULL, 0); - // Convert to CallbackError and attach traceback - wrapped_error_traceback(L, -2, -1); - lua_pop(L, 1); - } else { - // Convert to CallbackError with error message as a traceback - wrapped_error_traceback(L, -1, 0); - } -} - -// A wrapper around Rust function to protect from triggering longjmp in Rust. -// Rust callback expected to return positive number of output values or -// -1 in case of error, -2 in case of panic. -static int lua_call_rust(lua_State *L) { - int nargs = lua_gettop(L); - - // We need one extra stack space to store preallocated memory, and at least 2 - // stack spaces overall for handling error metatables in rust fn - int extra_stack = 1; - if (nargs < 2) { - extra_stack = 2 - nargs; - } - - luaL_checkstack(L, extra_stack, - "not enough stack space for callback error handling"); - - // We cannot shadow rust errors with Lua ones, we pre-allocate enough memory - // to store a wrapped error or panic *before* we proceed. - lua_newuserdata(L, max(MLUA_WRAPPED_ERROR_SIZE, MLUA_WRAPPED_PANIC_SIZE)); - lua_rotate(L, 1, 1); - - lua_CFunction rust_callback = lua_touserdata(L, lua_upvalueindex(1)); - - int ret = rust_callback(L); - if (ret < 0) { - if (ret == -1 /* WrappedError */) { - handle_wrapped_error(L); - } - lua_error(L); - } - - return ret; -} - -void lua_call_mlua_hook_proc(lua_State *L, lua_Debug *ar) { - luaL_checkstack(L, 2, "not enough stack space for callback error handling"); - lua_newuserdata(L, max(MLUA_WRAPPED_ERROR_SIZE, MLUA_WRAPPED_PANIC_SIZE)); - lua_rotate(L, 1, 1); - int ret = mlua_hook_proc(L, ar); - if (ret < 0) { - if (ret == -1 /* WrappedError */) { - handle_wrapped_error(L); - } - lua_error(L); - } -} - -static inline lua_Integer lua_popinteger(lua_State *L) { - lua_Integer index = lua_tointeger(L, -1); - lua_pop(L, 1); - return index; -} - -// -// Common functions -// - -int lua_gc_s(lua_State *L) { - int data = lua_popinteger(L); - int what = lua_popinteger(L); - int ret = lua_gc(L, what, data); - lua_pushinteger(L, ret); - return 1; -} - -int luaL_ref_s(lua_State *L) { - int ret = luaL_ref(L, -2); - lua_pushinteger(L, ret); - return 1; -} - -int lua_pushlstring_s(lua_State *L) { - StringArg *s = lua_touserdata(L, -1); - lua_pop(L, 1); - lua_pushlstring(L, s->data, s->len); - return 1; -} - -int lua_tolstring_s(lua_State *L) { - void *len = lua_touserdata(L, -1); - lua_pop(L, 1); - const char *s = lua_tolstring(L, -1, len); - lua_pushlightuserdata(L, (void *)s); - return 2; -} - -int lua_newthread_s(lua_State *L) { - lua_newthread(L); - return 1; -} - -int lua_newuserdata_s(lua_State *L) { - size_t size = lua_tointeger(L, -1); - lua_pop(L, 1); - lua_newuserdata(L, size); - return 1; -} - -int lua_newwrappederror_s(lua_State *L) { - lua_newuserdata(L, MLUA_WRAPPED_ERROR_SIZE); - return 1; -} - -int lua_pushcclosure_s(lua_State *L) { - int n = lua_gettop(L) - 1; - lua_CFunction fn = lua_touserdata(L, -1); - lua_pop(L, 1); - lua_pushcclosure(L, fn, n); - return 1; -} - -int lua_pushrclosure_s(lua_State *L) { - int n = lua_gettop(L); - lua_pushcclosure(L, lua_call_rust, n); - return 1; -} - -int luaL_requiref_s(lua_State *L) { - const char *modname = lua_touserdata(L, -3); - lua_CFunction openf = lua_touserdata(L, -2); - int glb = lua_tointeger(L, -1); - lua_pop(L, 3); - luaL_requiref(L, modname, openf, glb); - return 1; -} - -// -// Table functions -// - -int lua_newtable_s(lua_State *L) { - lua_createtable(L, 0, 0); - return 1; -} - -int lua_createtable_s(lua_State *L) { - int nrec = lua_popinteger(L); - int narr = lua_popinteger(L); - lua_createtable(L, narr, nrec); - return 1; -} - -int lua_gettable_s(lua_State *L) { - lua_gettable(L, -2); - return 1; -} - -int lua_settable_s(lua_State *L) { - lua_settable(L, -3); - return 0; -} - -int lua_geti_s(lua_State *L) { - lua_Integer index = lua_popinteger(L); - lua_geti(L, -1, index); - return 1; -} - -int lua_rawset_s(lua_State *L) { - lua_rawset(L, -3); - return 0; -} - -int lua_rawseti_s(lua_State *L) { - lua_Integer index = lua_popinteger(L); - lua_rawseti(L, -2, index); - return 0; -} - -int lua_rawsetp_s(lua_State *L) { - void *p = lua_touserdata(L, -1); - lua_pop(L, 1); - lua_rawsetp(L, -2, p); - return 0; -} - -int lua_rawsetfield_s(lua_State *L) { - StringArg *s = lua_touserdata(L, -2); - lua_pushlstring(L, s->data, s->len); - lua_replace(L, -3); - lua_rawset(L, -3); - return 0; -} - -int lua_rawinsert_s(lua_State *L) { - lua_Integer index = lua_popinteger(L); - lua_Integer size = lua_rawlen(L, -2); - - for (lua_Integer i = size; i >= index; i--) { - // table[i+1] = table[i] - lua_rawgeti(L, -2, i); - lua_rawseti(L, -3, i + 1); - } - lua_rawseti(L, -2, index); - - return 0; -} - -int lua_rawremove_s(lua_State *L) { - lua_Integer index = lua_popinteger(L); - lua_Integer size = lua_rawlen(L, -1); - - for (lua_Integer i = index; i < size; i++) { - lua_rawgeti(L, -1, i + 1); - lua_rawseti(L, -2, i); - } - lua_pushnil(L); - lua_rawseti(L, -2, size); - - return 0; -} - -int luaL_len_s(lua_State *L) { - lua_pushinteger(L, luaL_len(L, -1)); - return 1; -} - -int lua_next_s(lua_State *L) { - int ret = lua_next(L, -2); - lua_pushinteger(L, ret); - return ret == 0 ? 1 : 3; -} - -// -// Moved from Rust to C -// - -// Wrapper to lookup in `field_getters` first, then `methods`, ending -// original `__index`. Used only if `field_getters` or `methods` set. -int meta_index_impl(lua_State *state) { - // stack: self, key - luaL_checkstack(state, 2, NULL); - - // lookup in `field_getters` table - if (lua_isnil(state, lua_upvalueindex(2)) == 0) { - lua_pushvalue(state, -1); // `key` arg - if (lua_rawget(state, lua_upvalueindex(2)) != LUA_TNIL) { - lua_insert(state, -3); // move function - lua_pop(state, 1); // remove `key` - lua_call(state, 1, 1); - return 1; - } - lua_pop(state, 1); // pop the nil value - } - // lookup in `methods` table - if (lua_isnil(state, lua_upvalueindex(3)) == 0) { - lua_pushvalue(state, -1); // `key` arg - if (lua_rawget(state, lua_upvalueindex(3)) != LUA_TNIL) { - lua_insert(state, -3); - lua_pop(state, 2); - return 1; - } - lua_pop(state, 1); // pop the nil value - } - - // lookup in `__index` - lua_pushvalue(state, lua_upvalueindex(1)); - switch (lua_type(state, -1)) { - case LUA_TNIL: - lua_pop(state, 1); // pop the nil value - const char *field = lua_tostring(state, -1); - luaL_error(state, "attempt to get an unknown field '%s'", field); - break; - - case LUA_TTABLE: - lua_insert(state, -2); - lua_gettable(state, -2); - break; - - case LUA_TFUNCTION: - lua_insert(state, -3); - lua_call(state, 2, 1); - break; - } - - return 1; -} - -// Similar to `meta_index_impl`, checks `field_setters` table first, then -// `__newindex` metamethod. Used only if `field_setters` set. -int meta_newindex_impl(lua_State *state) { - // stack: self, key, value - luaL_checkstack(state, 2, NULL); - - // lookup in `field_setters` table - lua_pushvalue(state, -2); // `key` arg - if (lua_rawget(state, lua_upvalueindex(2)) != LUA_TNIL) { - lua_remove(state, -3); // remove `key` - lua_insert(state, -3); // move function - lua_call(state, 2, 0); - return 0; - } - lua_pop(state, 1); // pop the nil value - - // lookup in `__newindex` - lua_pushvalue(state, lua_upvalueindex(1)); - switch (lua_type(state, -1)) { - case LUA_TNIL: - lua_pop(state, 1); // pop the nil value - const char *field = lua_tostring(state, -2); - luaL_error(state, "attempt to set an unknown field '%s'", field); - break; - - case LUA_TTABLE: - lua_insert(state, -3); - lua_settable(state, -3); - break; - - case LUA_TFUNCTION: - lua_insert(state, -4); - lua_call(state, 3, 0); - break; - } - - return 0; -} - -// See Function::bind -int bind_call_impl(lua_State *state) { - int nargs = lua_gettop(state); - int nbinds = lua_tointeger(state, lua_upvalueindex(2)); - luaL_checkstack(state, nbinds + 2, NULL); - - lua_settop(state, nargs + nbinds + 1); - lua_rotate(state, -(nargs + nbinds + 1), nbinds + 1); - - lua_pushvalue(state, lua_upvalueindex(1)); - lua_replace(state, 1); - - for (int i = 0; i < nbinds; i++) { - lua_pushvalue(state, lua_upvalueindex(i + 3)); - lua_replace(state, i + 2); - } - - lua_call(state, nargs + nbinds, LUA_MULTRET); - return lua_gettop(state); -} - -// Returns 1 if a value at index `index` is a special wrapped struct identified -// by `key` -int is_wrapped_struct(lua_State *state, int index, const void *key) { - if (key == NULL) { - // Not yet initialized? - return 0; - } - - void *ud = lua_touserdata(state, index); - if (ud == NULL || lua_getmetatable(state, index) == 0) { - return 0; - } - lua_rawgetp(state, LUA_REGISTRYINDEX, key); - int res = lua_rawequal(state, -1, -2); - lua_pop(state, 2); - return res; -} - -// Takes an error at the top of the stack and converts Lua errors into a string -// with attached traceback. If the error is a WrappedError or WrappedPanic, does -// not modify it. This function does its best to avoid triggering another error -// and shadowing previous rust errors. -int error_traceback(lua_State *state) { - if (lua_checkstack(state, 2) == 0) { - // If we don't have enough stack space to even check the error type, do - // nothing so we don't risk shadowing a rust panic. - return 1; - } - - if (MLUA_WRAPPED_PANIC_KEY == NULL || - is_wrapped_struct(state, -1, MLUA_WRAPPED_PANIC_KEY) || - is_wrapped_struct(state, -1, MLUA_WRAPPED_ERROR_KEY)) { - return 1; - } - - const char *s = luaL_tolstring(state, -1, NULL); - if (lua_checkstack(state, LUA_TRACEBACK_STACK) != 0) { - luaL_traceback(state, state, s, 1); - lua_remove(state, -2); - } - - return 1; -} - -int error_traceback_s(lua_State *L) { - lua_State *L1 = lua_touserdata(L, -1); - lua_pop(L, 1); - return error_traceback(L1); -} - -// A `pcall` implementation that does not allow Lua to catch Rust panics. -// Instead, panics automatically resumed. -int lua_nopanic_pcall(lua_State *state) { - luaL_checkstack(state, 2, NULL); - - int top = lua_gettop(state); - if (top == 0) { - lua_pushstring(state, "not enough arguments to pcall"); - lua_error(state); - } - - if (lua_pcall(state, top - 1, LUA_MULTRET, 0) == LUA_OK) { - lua_pushboolean(state, 1); - lua_insert(state, 1); - return lua_gettop(state); - } - - if (is_wrapped_struct(state, -1, MLUA_WRAPPED_PANIC_KEY)) { - lua_error(state); - } - lua_pushboolean(state, 0); - lua_insert(state, -2); - return 2; -} - -// A `xpcall` implementation that does not allow Lua to catch Rust panics. -// Instead, panics automatically resumed. - -static int xpcall_msgh(lua_State *state) { - luaL_checkstack(state, 2, NULL); - if (is_wrapped_struct(state, -1, MLUA_WRAPPED_PANIC_KEY)) { - return 1; - } - lua_pushvalue(state, lua_upvalueindex(1)); - lua_insert(state, 1); - lua_call(state, lua_gettop(state) - 1, LUA_MULTRET); - return lua_gettop(state); -} - -int lua_nopanic_xpcall(lua_State *state) { - luaL_checkstack(state, 2, NULL); - - int top = lua_gettop(state); - if (top < 2) { - lua_pushstring(state, "not enough arguments to xpcall"); - lua_error(state); - } - - lua_pushvalue(state, 2); - lua_pushcclosure(state, xpcall_msgh, 1); - lua_copy(state, 1, 2); - lua_replace(state, 1); - - if (lua_pcall(state, lua_gettop(state) - 2, LUA_MULTRET, 1) == LUA_OK) { - lua_pushboolean(state, 1); - lua_insert(state, 2); - return lua_gettop(state) - 1; - } - - if (is_wrapped_struct(state, -1, MLUA_WRAPPED_PANIC_KEY)) { - lua_error(state); - } - lua_pushboolean(state, 0); - lua_insert(state, -2); - return 2; -} diff --git a/src/function.rs b/src/function.rs index fbf192a..af4672f 100644 --- a/src/function.rs +++ b/src/function.rs @@ -1,10 +1,11 @@ use std::os::raw::{c_int, c_void}; +use std::ptr; use std::slice; use crate::error::{Error, Result}; use crate::ffi; use crate::types::LuaRef; -use crate::util::{assert_stack, check_stack, pop_error, StackGuard}; +use crate::util::{assert_stack, check_stack, error_traceback, pop_error, protect_lua, StackGuard}; use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti}; #[cfg(feature = "async")] @@ -65,7 +66,7 @@ impl<'lua> Function<'lua> { let _sg = StackGuard::new(lua.state); check_stack(lua.state, nargs + 3)?; - ffi::lua_pushcfunction(lua.state, ffi::safe::error_traceback); + ffi::lua_pushcfunction(lua.state, error_traceback); let stack_start = ffi::lua_gettop(lua.state); lua.push_ref(&self.0); for arg in args { @@ -159,6 +160,26 @@ impl<'lua> Function<'lua> { /// # } /// ``` pub fn bind>(&self, args: A) -> Result> { + unsafe extern "C" fn bind_call_impl(state: *mut ffi::lua_State) -> c_int { + let nargs = ffi::lua_gettop(state); + let nbinds = ffi::lua_tointeger(state, ffi::lua_upvalueindex(2)) as c_int; + ffi::luaL_checkstack(state, nbinds + 2, ptr::null()); + + ffi::lua_settop(state, nargs + nbinds + 1); + ffi::lua_rotate(state, -(nargs + nbinds + 1), nbinds + 1); + + ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1)); + ffi::lua_replace(state, 1); + + for i in 0..nbinds { + ffi::lua_pushvalue(state, ffi::lua_upvalueindex(i + 3)); + ffi::lua_replace(state, i + 2); + } + + ffi::lua_call(state, nargs + nbinds, ffi::LUA_MULTRET); + ffi::lua_gettop(state) + } + let lua = self.0.lua; let args = args.to_lua_multi(lua)?; @@ -177,7 +198,9 @@ impl<'lua> Function<'lua> { for arg in args { lua.push_value(arg)?; } - ffi::safe::lua_pushcclosure(lua.state, ffi::safe::bind_call_impl, nargs + 2)?; + protect_lua(lua.state, nargs + 2, 1, |state| { + ffi::lua_pushcclosure(state, bind_call_impl, nargs + 2); + })?; Ok(Function(lua.pop_ref())) } diff --git a/src/hook.rs b/src/hook.rs index d534602..3004e42 100644 --- a/src/hook.rs +++ b/src/hook.rs @@ -165,8 +165,7 @@ impl HookTriggers { } } -#[no_mangle] -pub unsafe extern "C" fn mlua_hook_proc(state: *mut lua_State, ar: *mut lua_Debug) -> c_int { +pub(crate) unsafe extern "C" fn hook_proc(state: *mut lua_State, ar: *mut lua_Debug) { callback_error(state, |_| { let debug = Debug { ar, @@ -183,7 +182,7 @@ pub unsafe extern "C" fn mlua_hook_proc(state: *mut lua_State, ar: *mut lua_Debu Err(_) => mlua_panic!("Lua should not allow hooks to be called within another hook"), }?; - Ok(0) + Ok(()) }) } diff --git a/src/lua.rs b/src/lua.rs index 6b50be8..d463dfe 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -12,7 +12,7 @@ use std::{mem, ptr, str}; use crate::error::{Error, Result}; use crate::ffi; use crate::function::Function; -use crate::hook::{Debug, HookTriggers}; +use crate::hook::{hook_proc, Debug, HookTriggers}; use crate::scope::Scope; use crate::stdlib::StdLib; use crate::string::String; @@ -27,8 +27,9 @@ use crate::userdata::{ use crate::util::{ assert_stack, callback_error, check_stack, get_destructed_userdata_metatable, get_gc_userdata, get_main_state, get_userdata, get_wrapped_error, init_error_registry, init_gc_metatable_for, - init_userdata_metatable, pop_error, push_gc_userdata, push_userdata, push_wrapped_error, - StackGuard, WrappedError, WrappedPanic, + init_userdata_metatable, pop_error, protect_lua, push_gc_userdata, push_string, push_table, + push_userdata, push_wrapped_error, rawset_field, safe_pcall, safe_xpcall, StackGuard, + WrappedPanic, }; use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value}; @@ -326,10 +327,7 @@ impl Lua { #[cfg(any(feature = "lua51", feature = "luajit"))] let state = ffi::luaL_newstate(); - mlua_expect!( - ffi::safe::luaL_requiref(state, "_G", ffi::luaopen_base, 1), - "Error during loading base lib" - ); + ffi::luaL_requiref(state, cstr!("_G"), ffi::luaopen_base, 1); ffi::lua_pop(state, 1); let mut lua = Lua::init_from_ptr(state); @@ -355,11 +353,11 @@ impl Lua { #[cfg(any(feature = "lua51", feature = "luajit"))] ffi::lua_pushvalue(lua.state, ffi::LUA_GLOBALSINDEX); - ffi::lua_pushcfunction(lua.state, ffi::safe::lua_nopanic_pcall); - ffi::safe::lua_rawsetfield(lua.state, -2, "pcall")?; + ffi::lua_pushcfunction(lua.state, safe_pcall); + rawset_field(lua.state, -2, "pcall")?; - ffi::lua_pushcfunction(lua.state, ffi::safe::lua_nopanic_xpcall); - ffi::safe::lua_rawsetfield(lua.state, -2, "xpcall")?; + ffi::lua_pushcfunction(lua.state, safe_xpcall); + rawset_field(lua.state, -2, "xpcall")?; Ok(()) })(), @@ -383,15 +381,7 @@ impl Lua { let ref_thread = mlua_expect!( (|state| { - // Before initializing the error registry, we must set Error/Panic size. - // Error/Panic keys are not needed during the registry initialization. - ffi::safe::WRAPPED_ERROR_SIZE = mem::size_of::(); - ffi::safe::WRAPPED_PANIC_SIZE = mem::size_of::(); - - let (wrapped_error_key, wrapped_panic_key) = init_error_registry(state)?; - - ffi::safe::WRAPPED_ERROR_KEY = wrapped_error_key as *const c_void; - ffi::safe::WRAPPED_PANIC_KEY = wrapped_panic_key as *const c_void; + init_error_registry(state)?; // Create the internal metatables and place them in the registry // to prevent them from being garbage collected. @@ -408,7 +398,9 @@ impl Lua { // Create empty Waker slot push_gc_userdata::>(state, None)?; let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void; - ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, waker_key)?; + protect_lua(state, 1, 0, |state| { + ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, waker_key); + })?; } // Init serde metatables @@ -417,9 +409,11 @@ impl Lua { // Create ref stack thread and place it in the registry to prevent it from being garbage // collected. - - let ref_thread = ffi::safe::lua_newthread(state)?; - ffi::safe::luaL_ref(state, ffi::LUA_REGISTRYINDEX)?; + let ref_thread = protect_lua(state, 0, 0, |state| { + let thread = ffi::lua_newthread(state); + ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX); + thread + })?; Ok::<_, Error>(ref_thread) })(main_state), @@ -449,7 +443,9 @@ impl Lua { ); let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void; mlua_expect!( - ffi::safe::lua_rawsetp(main_state, ffi::LUA_REGISTRYINDEX, extra_key), + protect_lua(main_state, 1, 0, |state| { + ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key) + }), "Error while storing extra data", ); @@ -594,12 +590,7 @@ impl Lua { unsafe { let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned"); extra.hook_callback = Some(Arc::new(RefCell::new(callback))); - ffi::lua_sethook( - state, - Some(ffi::safe::lua_call_mlua_hook_proc), - triggers.mask(), - triggers.count(), - ); + ffi::lua_sethook(state, Some(hook_proc), triggers.mask(), triggers.count()); } Ok(()) } @@ -683,7 +674,12 @@ impl Lua { /// objects. Once to finish the current gc cycle, and once to start and finish the next cycle. pub fn gc_collect(&self) -> Result<()> { let state = self.main_state.unwrap_or(self.state); - unsafe { ffi::safe::lua_gc(state, ffi::LUA_GCCOLLECT, 0).map(|_| ()) } + unsafe { + check_stack(state, 3)?; + protect_lua(state, 0, 0, |state| { + ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0); + }) + } } /// Steps the garbage collector one indivisible step. @@ -699,7 +695,12 @@ impl Lua { /// finished a collection cycle. pub fn gc_step_kbytes(&self, kbytes: c_int) -> Result { let state = self.main_state.unwrap_or(self.state); - unsafe { Ok(ffi::safe::lua_gc(state, ffi::LUA_GCSTEP, kbytes)? != 0) } + unsafe { + check_stack(state, 3)?; + protect_lua(state, 0, 0, |state| { + ffi::lua_gc(state, ffi::LUA_GCSTEP, kbytes) != 0 + }) + } } /// Sets the 'pause' value of the collector. @@ -863,7 +864,7 @@ impl Lua { unsafe { let _sg = StackGuard::new(self.state); check_stack(self.state, 3)?; - ffi::safe::lua_pushstring(self.state, s)?; + push_string(self.state, s)?; Ok(String(self.pop_ref())) } } @@ -872,8 +873,8 @@ impl Lua { pub fn create_table(&self) -> Result { unsafe { let _sg = StackGuard::new(self.state); - check_stack(self.state, 2)?; - ffi::safe::lua_newtable(self.state)?; + check_stack(self.state, 3)?; + push_table(self.state, 0, 0)?; Ok(Table(self.pop_ref())) } } @@ -885,8 +886,8 @@ impl Lua { pub fn create_table_with_capacity(&self, narr: c_int, nrec: c_int) -> Result
{ unsafe { let _sg = StackGuard::new(self.state); - check_stack(self.state, 4)?; - ffi::safe::lua_createtable(self.state, narr, nrec)?; + check_stack(self.state, 3)?; + push_table(self.state, narr, nrec)?; Ok(Table(self.pop_ref())) } } @@ -904,11 +905,11 @@ impl Lua { let iter = iter.into_iter(); let lower_bound = iter.size_hint().0; - ffi::safe::lua_createtable(self.state, 0, lower_bound as c_int)?; + push_table(self.state, 0, lower_bound as c_int)?; for (k, v) in iter { self.push_value(k.to_lua(self)?)?; self.push_value(v.to_lua(self)?)?; - ffi::safe::lua_rawset(self.state, -3)?; + protect_lua(self.state, 3, 1, |state| ffi::lua_rawset(state, -3))?; } Ok(Table(self.pop_ref())) @@ -923,14 +924,16 @@ impl Lua { { unsafe { let _sg = StackGuard::new(self.state); - check_stack(self.state, 6)?; + check_stack(self.state, 5)?; let iter = iter.into_iter(); let lower_bound = iter.size_hint().0; - ffi::safe::lua_createtable(self.state, lower_bound as c_int, 0)?; + push_table(self.state, lower_bound as c_int, 0)?; for (i, v) in iter.enumerate() { self.push_value(v.to_lua(self)?)?; - ffi::safe::lua_rawseti(self.state, -2, (i + 1) as Integer)?; + protect_lua(self.state, 2, 1, |state| { + ffi::lua_rawseti(state, -2, (i + 1) as Integer); + })?; } Ok(Table(self.pop_ref())) @@ -1086,9 +1089,9 @@ impl Lua { pub fn create_thread<'lua>(&'lua self, func: Function<'lua>) -> Result> { unsafe { let _sg = StackGuard::new(self.state); - check_stack(self.state, 2)?; + check_stack(self.state, 3)?; - let thread_state = ffi::safe::lua_newthread(self.state)?; + let thread_state = protect_lua(self.state, 0, 1, |state| ffi::lua_newthread(state))?; self.push_ref(&func.0); ffi::lua_xmove(self.state, thread_state, 1); @@ -1200,10 +1203,13 @@ impl Lua { Value::String(s) => Some(s), v => unsafe { let _sg = StackGuard::new(self.state); - check_stack(self.state, 5)?; + check_stack(self.state, 4)?; self.push_value(v)?; - if !ffi::safe::lua_tolstring(self.state, -1, ptr::null_mut())?.is_null() { + let res = protect_lua(self.state, 1, 1, |state| { + ffi::lua_tolstring(state, -1, ptr::null_mut()) + })?; + if !res.is_null() { Some(String(self.pop_ref())) } else { None @@ -1299,7 +1305,7 @@ impl Lua { check_stack(self.state, 5)?; self.push_value(t)?; - ffi::safe::lua_rawsetfield(self.state, ffi::LUA_REGISTRYINDEX, name) + rawset_field(self.state, ffi::LUA_REGISTRYINDEX, name) } } @@ -1318,7 +1324,7 @@ impl Lua { let _sg = StackGuard::new(self.state); check_stack(self.state, 3)?; - ffi::safe::lua_pushstring(self.state, name)?; + push_string(self.state, name)?; ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX); self.pop_value() @@ -1354,7 +1360,9 @@ impl Lua { check_stack(self.state, 4)?; self.push_value(t)?; - let registry_id = ffi::safe::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)?; + let registry_id = protect_lua(self.state, 1, 0, |state| { + ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX) + })?; let extra = mlua_expect!(self.extra.lock(), "extra is poisoned"); @@ -1618,20 +1626,21 @@ impl Lua { // Prepare metatable, add meta methods first and then meta fields let metatable_nrec = methods.meta_methods.len() + fields.meta_fields.len(); - ffi::safe::lua_createtable(self.state, 0, metatable_nrec as c_int)?; + push_table(self.state, 0, metatable_nrec as c_int)?; for (k, m) in methods.meta_methods { self.push_value(Value::Function(self.create_callback(m)?))?; - ffi::safe::lua_rawsetfield(self.state, -2, k.validate()?.name())?; + rawset_field(self.state, -2, k.validate()?.name())?; } for (k, f) in fields.meta_fields { self.push_value(f(self)?)?; - ffi::safe::lua_rawsetfield(self.state, -2, k.validate()?.name())?; + rawset_field(self.state, -2, k.validate()?.name())?; } // Add special `__mlua_type_id` field - let type_id_ptr = - ffi::safe::lua_newuserdata(self.state, mem::size_of::())? as *mut TypeId; + let type_id_ptr = protect_lua(self.state, 0, 1, |state| { + ffi::lua_newuserdata(state, mem::size_of::()) as *mut TypeId + })?; ptr::write(type_id_ptr, type_id); - ffi::safe::lua_rawsetfield(self.state, -2, "__mlua_type_id")?; + rawset_field(self.state, -2, "__mlua_type_id")?; let metatable_index = ffi::lua_absindex(self.state, -1); let mut extra_tables_count = 0; @@ -1639,10 +1648,10 @@ impl Lua { let mut field_getters_index = None; let field_getters_nrec = fields.field_getters.len(); if field_getters_nrec > 0 { - ffi::safe::lua_createtable(self.state, 0, field_getters_nrec as c_int)?; + push_table(self.state, 0, field_getters_nrec as c_int)?; for (k, m) in fields.field_getters { self.push_value(Value::Function(self.create_callback(m)?))?; - ffi::safe::lua_rawsetfield(self.state, -2, &k)?; + rawset_field(self.state, -2, &k)?; } field_getters_index = Some(ffi::lua_absindex(self.state, -1)); extra_tables_count += 1; @@ -1651,10 +1660,10 @@ impl Lua { let mut field_setters_index = None; let field_setters_nrec = fields.field_setters.len(); if field_setters_nrec > 0 { - ffi::safe::lua_createtable(self.state, 0, field_setters_nrec as c_int)?; + push_table(self.state, 0, field_setters_nrec as c_int)?; for (k, m) in fields.field_setters { self.push_value(Value::Function(self.create_callback(m)?))?; - ffi::safe::lua_rawsetfield(self.state, -2, &k)?; + rawset_field(self.state, -2, &k)?; } field_setters_index = Some(ffi::lua_absindex(self.state, -1)); extra_tables_count += 1; @@ -1666,15 +1675,15 @@ impl Lua { #[cfg(not(feature = "async"))] let methods_nrec = methods.methods.len(); if methods_nrec > 0 { - ffi::safe::lua_createtable(self.state, 0, methods_nrec as c_int)?; + push_table(self.state, 0, methods_nrec as c_int)?; for (k, m) in methods.methods { self.push_value(Value::Function(self.create_callback(m)?))?; - ffi::safe::lua_rawsetfield(self.state, -2, &k)?; + rawset_field(self.state, -2, &k)?; } #[cfg(feature = "async")] for (k, m) in methods.async_methods { self.push_value(Value::Function(self.create_async_callback(m)?))?; - ffi::safe::lua_rawsetfield(self.state, -2, &k)?; + rawset_field(self.state, -2, &k)?; } methods_index = Some(ffi::lua_absindex(self.state, -1)); extra_tables_count += 1; @@ -1693,7 +1702,9 @@ impl Lua { let ptr = ffi::lua_topointer(self.state, -1); ffi::lua_pushvalue(self.state, -1); - let id = ffi::safe::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)?; + let id = protect_lua(self.state, 1, 0, |state| { + ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX) + })?; let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned"); extra.registered_userdata.insert(type_id, id); @@ -1756,8 +1767,8 @@ impl Lua { { unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int { callback_error(state, |nargs| { - let upvalue_idx1 = ffi::lua_upvalueindex(2); - let upvalue_idx2 = ffi::lua_upvalueindex(3); + let upvalue_idx1 = ffi::lua_upvalueindex(1); + let upvalue_idx2 = ffi::lua_upvalueindex(2); if ffi::lua_type(state, upvalue_idx1) == ffi::LUA_TNIL || ffi::lua_type(state, upvalue_idx2) == ffi::LUA_TNIL { @@ -1797,7 +1808,9 @@ impl Lua { push_gc_userdata::(self.state, mem::transmute(func))?; push_gc_userdata(self.state, self.clone())?; - ffi::safe::lua_pushrclosure(self.state, call_callback, 2)?; + protect_lua(self.state, 2, 1, |state| { + ffi::lua_pushcclosure(state, call_callback, 2); + })?; Ok(Function(self.pop_ref())) } @@ -1821,8 +1834,8 @@ impl Lua { unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int { callback_error(state, |nargs| { - let upvalue_idx1 = ffi::lua_upvalueindex(2); - let upvalue_idx2 = ffi::lua_upvalueindex(3); + let upvalue_idx1 = ffi::lua_upvalueindex(1); + let upvalue_idx2 = ffi::lua_upvalueindex(2); if ffi::lua_type(state, upvalue_idx1) == ffi::LUA_TNIL || ffi::lua_type(state, upvalue_idx2) == ffi::LUA_TNIL { @@ -1848,7 +1861,9 @@ impl Lua { push_gc_userdata(state, fut)?; push_gc_userdata(state, lua.clone())?; - ffi::safe::lua_pushrclosure(state, poll_future, 2)?; + protect_lua(state, 2, 1, |state| { + ffi::lua_pushcclosure(state, poll_future, 2); + })?; Ok(1) }) @@ -1856,8 +1871,8 @@ impl Lua { unsafe extern "C" fn poll_future(state: *mut ffi::lua_State) -> c_int { callback_error(state, |nargs| { - let upvalue_idx1 = ffi::lua_upvalueindex(2); - let upvalue_idx2 = ffi::lua_upvalueindex(3); + let upvalue_idx1 = ffi::lua_upvalueindex(1); + let upvalue_idx2 = ffi::lua_upvalueindex(2); if ffi::lua_type(state, upvalue_idx1) == ffi::LUA_TNIL || ffi::lua_type(state, upvalue_idx2) == ffi::LUA_TNIL { @@ -1910,7 +1925,9 @@ impl Lua { push_gc_userdata::(self.state, mem::transmute(func))?; push_gc_userdata(self.state, self.clone())?; - ffi::safe::lua_pushrclosure(self.state, call_callback, 2)?; + protect_lua(self.state, 2, 1, |state| { + ffi::lua_pushcclosure(state, call_callback, 2); + })?; Function(self.pop_ref()) }; @@ -2257,43 +2274,76 @@ impl<'lua, T: AsRef<[u8]> + ?Sized> AsChunk<'lua> for T { } } +// Uses 3 stack spaces unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result<()> { + #[inline(always)] + pub unsafe fn requiref + ?Sized>( + state: *mut ffi::lua_State, + modname: &S, + openf: ffi::lua_CFunction, + glb: c_int, + ) -> Result<()> { + let modname = mlua_expect!(CString::new(modname.as_ref()), "modname contains nil bytes"); + protect_lua(state, 0, 1, |state| { + ffi::luaL_requiref(state, modname.as_ptr() as *const c_char, openf, glb) + }) + } + #[cfg(feature = "luajit")] + struct GcGuard(*mut ffi::lua_State); + + #[cfg(feature = "luajit")] + impl GcGuard { + fn new(state: *mut ffi::lua_State) -> Self { + // Stop collector during library initialization + unsafe { ffi::lua_gc(state, ffi::LUA_GCSTOP, 0) }; + GcGuard(state) + } + } + + #[cfg(feature = "luajit")] + impl Drop for GcGuard { + fn drop(&mut self) { + unsafe { ffi::lua_gc(self.0, ffi::LUA_GCRESTART, -1) }; + } + } + // Stop collector during library initialization - ffi::lua_gc(state, ffi::LUA_GCSTOP, 0); + #[cfg(feature = "luajit")] + let _gc_guard = GcGuard::new(state); #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] { if libs.contains(StdLib::COROUTINE) { - ffi::safe::luaL_requiref(state, ffi::LUA_COLIBNAME, ffi::luaopen_coroutine, 1)?; + requiref(state, ffi::LUA_COLIBNAME, ffi::luaopen_coroutine, 1)?; ffi::lua_pop(state, 1); } } if libs.contains(StdLib::TABLE) { - ffi::safe::luaL_requiref(state, ffi::LUA_TABLIBNAME, ffi::luaopen_table, 1)?; + requiref(state, ffi::LUA_TABLIBNAME, ffi::luaopen_table, 1)?; ffi::lua_pop(state, 1); } if libs.contains(StdLib::IO) { - ffi::safe::luaL_requiref(state, ffi::LUA_IOLIBNAME, ffi::luaopen_io, 1)?; + requiref(state, ffi::LUA_IOLIBNAME, ffi::luaopen_io, 1)?; ffi::lua_pop(state, 1); } if libs.contains(StdLib::OS) { - ffi::safe::luaL_requiref(state, ffi::LUA_OSLIBNAME, ffi::luaopen_os, 1)?; + requiref(state, ffi::LUA_OSLIBNAME, ffi::luaopen_os, 1)?; ffi::lua_pop(state, 1); } if libs.contains(StdLib::STRING) { - ffi::safe::luaL_requiref(state, ffi::LUA_STRLIBNAME, ffi::luaopen_string, 1)?; + requiref(state, ffi::LUA_STRLIBNAME, ffi::luaopen_string, 1)?; ffi::lua_pop(state, 1); } #[cfg(any(feature = "lua54", feature = "lua53"))] { if libs.contains(StdLib::UTF8) { - ffi::safe::luaL_requiref(state, ffi::LUA_UTF8LIBNAME, ffi::luaopen_utf8, 1)?; + requiref(state, ffi::LUA_UTF8LIBNAME, ffi::luaopen_utf8, 1)?; ffi::lua_pop(state, 1); } } @@ -2301,7 +2351,7 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result< #[cfg(feature = "lua52")] { if libs.contains(StdLib::BIT) { - ffi::safe::luaL_requiref(state, ffi::LUA_BITLIBNAME, ffi::luaopen_bit32, 1)?; + requiref(state, ffi::LUA_BITLIBNAME, ffi::luaopen_bit32, 1)?; ffi::lua_pop(state, 1); } } @@ -2309,42 +2359,39 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result< #[cfg(feature = "luajit")] { if libs.contains(StdLib::BIT) { - ffi::safe::luaL_requiref(state, ffi::LUA_BITLIBNAME, ffi::luaopen_bit, 1)?; + requiref(state, ffi::LUA_BITLIBNAME, ffi::luaopen_bit, 1)?; ffi::lua_pop(state, 1); } } if libs.contains(StdLib::MATH) { - ffi::safe::luaL_requiref(state, ffi::LUA_MATHLIBNAME, ffi::luaopen_math, 1)?; + requiref(state, ffi::LUA_MATHLIBNAME, ffi::luaopen_math, 1)?; ffi::lua_pop(state, 1); } if libs.contains(StdLib::DEBUG) { - ffi::safe::luaL_requiref(state, ffi::LUA_DBLIBNAME, ffi::luaopen_debug, 1)?; + requiref(state, ffi::LUA_DBLIBNAME, ffi::luaopen_debug, 1)?; ffi::lua_pop(state, 1); } if libs.contains(StdLib::PACKAGE) { - ffi::safe::luaL_requiref(state, ffi::LUA_LOADLIBNAME, ffi::luaopen_package, 1)?; + requiref(state, ffi::LUA_LOADLIBNAME, ffi::luaopen_package, 1)?; ffi::lua_pop(state, 1); } #[cfg(feature = "luajit")] { if libs.contains(StdLib::JIT) { - ffi::safe::luaL_requiref(state, ffi::LUA_JITLIBNAME, ffi::luaopen_jit, 1)?; + requiref(state, ffi::LUA_JITLIBNAME, ffi::luaopen_jit, 1)?; ffi::lua_pop(state, 1); } if libs.contains(StdLib::FFI) { - ffi::safe::luaL_requiref(state, ffi::LUA_FFILIBNAME, ffi::luaopen_ffi, 1)?; + requiref(state, ffi::LUA_FFILIBNAME, ffi::luaopen_ffi, 1)?; ffi::lua_pop(state, 1); } } - #[cfg(feature = "luajit")] - ffi::lua_gc(state, ffi::LUA_GCRESTART, -1); - Ok(()) } diff --git a/src/scope.rs b/src/scope.rs index ad9e90d..26325d2 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -17,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_userdata, 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}; @@ -326,27 +326,27 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { // 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; - ffi::safe::lua_createtable(lua.state, 0, meta_methods_nrec as c_int)?; + 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)?))?; - ffi::safe::lua_rawsetfield(lua.state, -2, k.validate()?.name())?; + rawset_field(lua.state, -2, k.validate()?.name())?; } for (k, f) in ud_fields.meta_fields { lua.push_value(f(mem::transmute(lua))?)?; - ffi::safe::lua_rawsetfield(lua.state, -2, k.validate()?.name())?; + rawset_field(lua.state, -2, k.validate()?.name())?; } let metatable_index = ffi::lua_absindex(lua.state, -1); let mut field_getters_index = None; let field_getters_nrec = ud_fields.field_getters.len(); if field_getters_nrec > 0 { - ffi::safe::lua_createtable(lua.state, 0, field_getters_nrec as c_int)?; + 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)?))?; - ffi::safe::lua_rawsetfield(lua.state, -2, &k)?; + rawset_field(lua.state, -2, &k)?; } field_getters_index = Some(ffi::lua_absindex(lua.state, -1)); } @@ -354,11 +354,11 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { let mut field_setters_index = None; let field_setters_nrec = ud_fields.field_setters.len(); if field_setters_nrec > 0 { - ffi::safe::lua_createtable(lua.state, 0, field_setters_nrec as c_int)?; + 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)?))?; - ffi::safe::lua_rawsetfield(lua.state, -2, &k)?; + rawset_field(lua.state, -2, &k)?; } field_setters_index = Some(ffi::lua_absindex(lua.state, -1)); } @@ -367,11 +367,11 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { let methods_nrec = ud_methods.methods.len(); if methods_nrec > 0 { // Create table used for methods lookup - ffi::safe::lua_createtable(lua.state, 0, methods_nrec as c_int)?; + 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)?))?; - ffi::safe::lua_rawsetfield(lua.state, -2, &k)?; + rawset_field(lua.state, -2, &k)?; } methods_index = Some(ffi::lua_absindex(lua.state, -1)); } @@ -456,15 +456,15 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { // We know the destructor has not run yet because we hold a reference to the callback. - ffi::lua_getupvalue(state, -1, 2); + ffi::lua_getupvalue(state, -1, 1); let ud1 = take_userdata::(state); ffi::lua_pushnil(state); - ffi::lua_setupvalue(state, -2, 2); + ffi::lua_setupvalue(state, -2, 1); - ffi::lua_getupvalue(state, -1, 3); + ffi::lua_getupvalue(state, -1, 2); let ud2 = take_userdata::(state); ffi::lua_pushnil(state); - ffi::lua_setupvalue(state, -2, 3); + ffi::lua_setupvalue(state, -2, 2); vec![Box::new(ud1), Box::new(ud2)] }); @@ -506,15 +506,15 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { ffi::lua_rawget(state, -2); // Destroy all upvalues - ffi::lua_getupvalue(state, -1, 2); + ffi::lua_getupvalue(state, -1, 1); let ud1 = take_userdata::(state); ffi::lua_pushnil(state); - ffi::lua_setupvalue(state, -2, 2); + ffi::lua_setupvalue(state, -2, 1); - ffi::lua_getupvalue(state, -1, 3); + ffi::lua_getupvalue(state, -1, 2); let ud2 = take_userdata::(state); ffi::lua_pushnil(state); - ffi::lua_setupvalue(state, -2, 3); + ffi::lua_setupvalue(state, -2, 2); ffi::lua_pop(state, 1); let mut data: Vec> = vec![Box::new(ud1), Box::new(ud2)]; @@ -522,16 +522,16 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { // Finally, get polled future and destroy it f.lua.push_ref(&poll_str.0); if ffi::lua_rawget(state, -2) == ffi::LUA_TFUNCTION { - ffi::lua_getupvalue(state, -1, 2); + ffi::lua_getupvalue(state, -1, 1); let ud3 = take_userdata::>>(state); ffi::lua_pushnil(state); - ffi::lua_setupvalue(state, -2, 2); + ffi::lua_setupvalue(state, -2, 1); data.push(Box::new(ud3)); - ffi::lua_getupvalue(state, -1, 3); + ffi::lua_getupvalue(state, -1, 2); let ud4 = take_userdata::(state); ffi::lua_pushnil(state); - ffi::lua_setupvalue(state, -2, 3); + ffi::lua_setupvalue(state, -2, 2); data.push(Box::new(ud4)); } diff --git a/src/serde/mod.rs b/src/serde/mod.rs index d9253d0..27ce492 100644 --- a/src/serde/mod.rs +++ b/src/serde/mod.rs @@ -10,7 +10,7 @@ use crate::ffi; use crate::lua::Lua; use crate::table::Table; use crate::types::LightUserData; -use crate::util::{assert_stack, check_stack, StackGuard}; +use crate::util::{assert_stack, check_stack, protect_lua, StackGuard}; use crate::value::Value; /// Trait for serializing/deserializing Lua values using Serde. @@ -200,15 +200,17 @@ impl<'lua> LuaSerdeExt<'lua> for Lua { // Uses 6 stack spaces and calls checkstack. pub(crate) unsafe fn init_metatables(state: *mut ffi::lua_State) -> Result<()> { - check_stack(state, 6)?; + check_stack(state, 3)?; + protect_lua(state, 0, 0, |state| { + ffi::lua_createtable(state, 0, 1); - ffi::safe::lua_createtable(state, 0, 1)?; + ffi::lua_pushstring(state, cstr!("__metatable")); + ffi::lua_pushboolean(state, 0); + ffi::lua_rawset(state, -3); - ffi::lua_pushboolean(state, 0); - ffi::safe::lua_rawsetfield(state, -2, "__metatable")?; - - let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *const c_void; - ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key) + let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *const c_void; + ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key); + }) } pub(crate) unsafe fn push_array_metatable(state: *mut ffi::lua_State) { diff --git a/src/serde/ser.rs b/src/serde/ser.rs index 4b774cb..4c6423c 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -9,7 +9,7 @@ use crate::lua::Lua; use crate::string::String; use crate::table::Table; use crate::types::Integer; -use crate::util::{check_stack, StackGuard}; +use crate::util::{check_stack, protect_lua, StackGuard}; use crate::value::{ToLua, Value}; /// A struct for serializing Rust values into Lua values. @@ -318,12 +318,14 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> { let value = lua.to_value_with(value, self.options)?; unsafe { let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 6)?; + check_stack(lua.state, 5)?; lua.push_ref(&self.table.0); lua.push_value(value)?; let len = ffi::lua_rawlen(lua.state, -2) as Integer; - ffi::safe::lua_rawseti(lua.state, -2, len + 1) + protect_lua(lua.state, 2, 0, |state| { + ffi::lua_rawseti(state, -2, len + 1); + }) } } diff --git a/src/table.rs b/src/table.rs index d03275d..af9aaaa 100644 --- a/src/table.rs +++ b/src/table.rs @@ -10,7 +10,7 @@ use crate::error::{Error, Result}; use crate::ffi; use crate::function::Function; use crate::types::{Integer, LuaRef}; -use crate::util::{assert_stack, check_stack, StackGuard}; +use crate::util::{assert_stack, check_stack, protect_lua, StackGuard}; use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value}; #[cfg(feature = "async")] @@ -67,7 +67,7 @@ impl<'lua> Table<'lua> { lua.push_ref(&self.0); lua.push_value(key)?; lua.push_value(value)?; - ffi::safe::lua_settable(lua.state, -3) + protect_lua(lua.state, 3, 0, |state| ffi::lua_settable(state, -3)) } } @@ -105,7 +105,7 @@ impl<'lua> Table<'lua> { lua.push_ref(&self.0); lua.push_value(key)?; - ffi::safe::lua_gettable(lua.state, -2)?; + protect_lua(lua.state, 2, 1, |state| ffi::lua_gettable(state, -2))?; lua.pop_value() }; @@ -123,9 +123,9 @@ impl<'lua> Table<'lua> { lua.push_ref(&self.0); lua.push_value(key)?; - ffi::safe::lua_gettable(lua.state, -2)?; - - Ok(ffi::lua_isnil(lua.state, -1) == 0) + protect_lua(lua.state, 2, 1, |state| { + ffi::lua_gettable(state, -2) != ffi::LUA_TNIL + }) } } @@ -198,7 +198,7 @@ impl<'lua> Table<'lua> { lua.push_ref(&self.0); lua.push_value(key)?; lua.push_value(value)?; - ffi::safe::lua_rawset(lua.state, -3) + protect_lua(lua.state, 3, 0, |state| ffi::lua_rawset(state, -3)) } } @@ -232,11 +232,18 @@ impl<'lua> Table<'lua> { let value = value.to_lua(lua)?; unsafe { let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 6)?; + check_stack(lua.state, 5)?; lua.push_ref(&self.0); lua.push_value(value)?; - ffi::safe::lua_rawinsert(lua.state, -2, idx) + protect_lua(lua.state, 2, 0, |state| { + for i in (idx..=size).rev() { + // table[i+1] = table[i] + ffi::lua_rawgeti(state, -2, i); + ffi::lua_rawseti(state, -3, i + 1); + } + ffi::lua_rawseti(state, -2, idx) + }) } } @@ -258,10 +265,17 @@ impl<'lua> Table<'lua> { } unsafe { let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 5)?; + check_stack(lua.state, 4)?; lua.push_ref(&self.0); - ffi::safe::lua_rawremove(lua.state, -1, idx) + protect_lua(lua.state, 1, 0, |state| { + for i in idx..size { + ffi::lua_rawgeti(state, -1, i + 1); + ffi::lua_rawseti(state, -2, i); + } + ffi::lua_pushnil(state); + ffi::lua_rawseti(state, -2, size); + }) } } _ => self.raw_set(key, Nil), @@ -280,7 +294,7 @@ impl<'lua> Table<'lua> { check_stack(lua.state, 4)?; lua.push_ref(&self.0); - ffi::safe::luaL_len(lua.state, -1) + protect_lua(lua.state, 1, 0, |state| ffi::luaL_len(state, -1)) } } @@ -657,7 +671,10 @@ where lua.push_ref(&self.table); lua.push_value(prev_key)?; - if ffi::safe::lua_next(lua.state, -2)? != 0 { + let next = protect_lua(lua.state, 2, ffi::LUA_MULTRET, |state| { + ffi::lua_next(state, -2) + })?; + if next != 0 { let value = lua.pop_value(); let key = lua.pop_value(); Ok(Some(( @@ -709,13 +726,13 @@ where let res = (|| unsafe { let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 1 + if self.raw { 0 } else { 4 })?; + check_stack(lua.state, 1 + if self.raw { 0 } else { 3 })?; lua.push_ref(&self.table); let res = if self.raw { ffi::lua_rawgeti(lua.state, -1, index) } else { - ffi::safe::lua_geti(lua.state, -1, index)? + protect_lua(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index))? }; match res { ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None), diff --git a/src/thread.rs b/src/thread.rs index 68e1a68..632c341 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -4,7 +4,7 @@ use std::os::raw::c_int; use crate::error::{Error, Result}; use crate::ffi; use crate::types::LuaRef; -use crate::util::{assert_stack, check_stack, pop_error, StackGuard}; +use crate::util::{assert_stack, check_stack, error_traceback, pop_error, protect_lua, StackGuard}; use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti}; #[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored"), doc))] @@ -135,7 +135,7 @@ impl<'lua> Thread<'lua> { let ret = ffi::lua_resume(thread_state, lua.state, nargs, &mut nresults as *mut c_int); if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD { - ffi::safe::error_traceback2(lua.state, thread_state)?; + protect_lua(lua.state, 0, 0, |_| error_traceback(thread_state))?; return Err(pop_error(thread_state, ret)); } diff --git a/src/userdata.rs b/src/userdata.rs index d9e75d3..419e8a6 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -20,7 +20,9 @@ use crate::function::Function; use crate::lua::Lua; use crate::table::{Table, TablePairs}; use crate::types::{Callback, LuaRef, MaybeSend}; -use crate::util::{check_stack, get_destructed_userdata_metatable, get_userdata, StackGuard}; +use crate::util::{ + check_stack, get_destructed_userdata_metatable, get_userdata, push_string, StackGuard, +}; use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti}; #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] @@ -926,7 +928,7 @@ impl<'lua> AnyUserData<'lua> { lua.push_userdata_ref(&self.0, true)?; // Get the special `__mlua_type_id` - ffi::safe::lua_pushstring(lua.state, "__mlua_type_id")?; + push_string(lua.state, "__mlua_type_id")?; if ffi::lua_rawget(lua.state, -2) != ffi::LUA_TUSERDATA { return Err(Error::UserDataTypeMismatch); } diff --git a/src/util.rs b/src/util.rs index f99c03f..3fe3d18 100644 --- a/src/util.rs +++ b/src/util.rs @@ -2,7 +2,7 @@ use std::any::{Any, TypeId}; use std::collections::HashMap; use std::error::Error as StdError; use std::fmt::Write; -use std::os::raw::{c_int, c_void}; +use std::os::raw::{c_char, c_int, c_void}; use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe}; use std::sync::{Arc, Mutex}; use std::{mem, ptr, slice}; @@ -17,6 +17,10 @@ static METATABLE_CACHE: Lazy>> = Lazy::new(|| { Mutex::new(HashMap::with_capacity(32)) }); +// I believe `luaL_traceback` < 5.4 requires this much free stack to not error. +// 5.4 uses `luaL_Buffer` +const LUA_TRACEBACK_STACK: c_int = 11; + // Checks that Lua has enough free stack space for future stack operations. On failure, this will // panic with an internal error message. pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) { @@ -85,27 +89,71 @@ impl Drop for StackGuard { // Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way. // Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a -// limited lua stack. `nargs` is the same as the the parameter to `lua_pcall`, and `nresults` is -// always LUA_MULTRET. Internally uses 2 extra stack spaces, and does not call checkstack. -// Provided function must *never* panic. -pub unsafe fn protect_lua( +// limited lua stack. `nargs` and `nresults` are similar to the parameters of `lua_pcall`, but the +// given function return type is not the return value count, instead the inner function return +// values are assumed to match the `nresults` param. Provided function must *not* panic, and since it +// will generally be lonjmping, should not contain any values that implements Drop. +// Internally uses 3 extra stack spaces, and does not call checkstack. +pub unsafe fn protect_lua( state: *mut ffi::lua_State, nargs: c_int, - f: unsafe extern "C" fn(*mut ffi::lua_State) -> c_int, // Must be "C-unwind" after stabilizing -) -> Result<()> { + nresults: c_int, + f: F, +) -> Result +where + F: Fn(*mut ffi::lua_State) -> R, + R: Copy, +{ + union URes { + uninit: (), + init: R, + } + + struct Params { + function: F, + result: URes, + nresults: c_int, + } + + unsafe extern "C" fn do_call(state: *mut ffi::lua_State) -> c_int + where + R: Copy, + F: Fn(*mut ffi::lua_State) -> R, + { + let params = ffi::lua_touserdata(state, -1) as *mut Params; + ffi::lua_pop(state, 1); + + (*params).result.init = ((*params).function)(state); + + if (*params).nresults == ffi::LUA_MULTRET { + ffi::lua_gettop(state) + } else { + (*params).nresults + } + } + let stack_start = ffi::lua_gettop(state) - nargs; - ffi::lua_pushcfunction(state, ffi::safe::error_traceback); - ffi::lua_pushcfunction(state, f); + ffi::lua_pushcfunction(state, error_traceback); + ffi::lua_pushcfunction(state, do_call::); if nargs > 0 { ffi::lua_rotate(state, stack_start + 1, 2); } - let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start + 1); + let mut params = Params { + function: f, + result: URes { uninit: () }, + nresults, + }; + + ffi::lua_pushlightuserdata(state, &mut params as *mut Params as *mut c_void); + let ret = ffi::lua_pcall(state, nargs + 1, nresults, stack_start + 1); ffi::lua_remove(state, stack_start + 1); if ret == ffi::LUA_OK { - Ok(()) + // `LUA_OK` is only returned when the `do_call` function has completed successfully, so + // `params.result` is definitely initialized. + Ok(params.result.init) } else { Err(pop_error(state, ret)) } @@ -162,9 +210,41 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error { } } +// Uses 3 stack spaces +pub unsafe fn push_string + ?Sized>( + state: *mut ffi::lua_State, + s: &S, +) -> Result<()> { + let s = s.as_ref(); + protect_lua(state, 0, 1, |state| { + ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len()); + }) +} + +// Uses 3 stack spaces +pub unsafe fn push_table(state: *mut ffi::lua_State, narr: c_int, nrec: c_int) -> Result<()> { + protect_lua(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec)) +} + +// Uses 4 stack spaces +pub unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &S) -> Result<()> +where + S: AsRef<[u8]> + ?Sized, +{ + let field = field.as_ref(); + ffi::lua_pushvalue(state, table); + protect_lua(state, 2, 0, |state| { + ffi::lua_pushlstring(state, field.as_ptr() as *const c_char, field.len()); + ffi::lua_rotate(state, -3, 2); + ffi::lua_rawset(state, -3) + }) +} + // Internally uses 3 stack spaces, does not call checkstack. pub unsafe fn push_userdata(state: *mut ffi::lua_State, t: T) -> Result<()> { - let ud = ffi::safe::lua_newuserdata(state, mem::size_of::())? as *mut T; + let ud = protect_lua(state, 0, 1, |state| { + ffi::lua_newuserdata(state, mem::size_of::()) as *mut T + })?; ptr::write(ud, t); Ok(()) } @@ -230,13 +310,99 @@ pub unsafe fn init_userdata_metatable( field_setters: Option, methods: Option, ) -> Result<()> { + // Wrapper to lookup in `field_getters` first, then `methods`, ending original `__index`. + // Used only if `field_getters` or `methods` set. + unsafe extern "C" fn meta_index_impl(state: *mut ffi::lua_State) -> c_int { + // stack: self, key + ffi::luaL_checkstack(state, 2, ptr::null()); + + // lookup in `field_getters` table + if ffi::lua_isnil(state, ffi::lua_upvalueindex(2)) == 0 { + ffi::lua_pushvalue(state, -1); // `key` arg + if ffi::lua_rawget(state, ffi::lua_upvalueindex(2)) != ffi::LUA_TNIL { + ffi::lua_insert(state, -3); // move function + ffi::lua_pop(state, 1); // remove `key` + ffi::lua_call(state, 1, 1); + return 1; + } + ffi::lua_pop(state, 1); // pop the nil value + } + // lookup in `methods` table + if ffi::lua_isnil(state, ffi::lua_upvalueindex(3)) == 0 { + ffi::lua_pushvalue(state, -1); // `key` arg + if ffi::lua_rawget(state, ffi::lua_upvalueindex(3)) != ffi::LUA_TNIL { + ffi::lua_insert(state, -3); + ffi::lua_pop(state, 2); + return 1; + } + ffi::lua_pop(state, 1); // pop the nil value + } + + // lookup in `__index` + ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1)); + match ffi::lua_type(state, -1) { + ffi::LUA_TNIL => { + ffi::lua_pop(state, 1); // pop the nil value + let field = ffi::lua_tostring(state, -1); + ffi::luaL_error(state, cstr!("attempt to get an unknown field '%s'"), field); + } + ffi::LUA_TTABLE => { + ffi::lua_insert(state, -2); + ffi::lua_gettable(state, -2); + } + ffi::LUA_TFUNCTION => { + ffi::lua_insert(state, -3); + ffi::lua_call(state, 2, 1); + } + _ => unreachable!(), + } + + 1 + } + + // Similar to `meta_index_impl`, checks `field_setters` table first, then `__newindex` metamethod. + // Used only if `field_setters` set. + unsafe extern "C" fn meta_newindex_impl(state: *mut ffi::lua_State) -> c_int { + // stack: self, key, value + ffi::luaL_checkstack(state, 2, ptr::null()); + + // lookup in `field_setters` table + ffi::lua_pushvalue(state, -2); // `key` arg + if ffi::lua_rawget(state, ffi::lua_upvalueindex(2)) != ffi::LUA_TNIL { + ffi::lua_remove(state, -3); // remove `key` + ffi::lua_insert(state, -3); // move function + ffi::lua_call(state, 2, 0); + return 0; + } + ffi::lua_pop(state, 1); // pop the nil value + + // lookup in `__newindex` + ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1)); + match ffi::lua_type(state, -1) { + ffi::LUA_TNIL => { + ffi::lua_pop(state, 1); // pop the nil value + let field = ffi::lua_tostring(state, -2); + ffi::luaL_error(state, cstr!("attempt to set an unknown field '%s'"), field); + } + ffi::LUA_TTABLE => { + ffi::lua_insert(state, -3); + ffi::lua_settable(state, -3); + } + ffi::LUA_TFUNCTION => { + ffi::lua_insert(state, -4); + ffi::lua_call(state, 3, 0); + } + _ => unreachable!(), + } + + 0 + } + ffi::lua_pushvalue(state, metatable); if field_getters.is_some() || methods.is_some() { - ffi::safe::lua_pushstring(state, "__index")?; - - ffi::lua_pushvalue(state, -1); - let index_type = ffi::lua_rawget(state, -3); + push_string(state, "__index")?; + let index_type = ffi::lua_rawget(state, -2); match index_type { ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => { for &idx in &[field_getters, methods] { @@ -246,35 +412,37 @@ pub unsafe fn init_userdata_metatable( ffi::lua_pushnil(state); } } - ffi::safe::lua_pushcclosure(state, ffi::safe::meta_index_impl, 3)?; + protect_lua(state, 3, 1, |state| { + ffi::lua_pushcclosure(state, meta_index_impl, 3) + })?; } _ => mlua_panic!("improper __index type {}", index_type), } - ffi::safe::lua_rawset(state, -3)?; + rawset_field(state, -2, "__index")?; } if let Some(field_setters) = field_setters { - ffi::safe::lua_pushstring(state, "__newindex")?; - - ffi::lua_pushvalue(state, -1); - let newindex_type = ffi::lua_rawget(state, -3); + push_string(state, "__newindex")?; + let newindex_type = ffi::lua_rawget(state, -2); match newindex_type { ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => { ffi::lua_pushvalue(state, field_setters); - ffi::safe::lua_pushcclosure(state, ffi::safe::meta_newindex_impl, 2)?; + protect_lua(state, 2, 1, |state| { + ffi::lua_pushcclosure(state, meta_newindex_impl, 2) + })?; } _ => mlua_panic!("improper __newindex type {}", newindex_type), } - ffi::safe::lua_rawset(state, -3)?; + rawset_field(state, -2, "__newindex")?; } - ffi::safe::lua_pushrclosure(state, userdata_destructor::, 0)?; - ffi::safe::lua_rawsetfield(state, -2, "__gc")?; + ffi::lua_pushcfunction(state, userdata_destructor::); + rawset_field(state, -2, "__gc")?; ffi::lua_pushboolean(state, 0); - ffi::safe::lua_rawsetfield(state, -2, "__metatable")?; + rawset_field(state, -2, "__metatable")?; ffi::lua_pop(state, 1); @@ -290,21 +458,39 @@ pub unsafe extern "C" fn userdata_destructor(state: *mut ffi::lua_State) -> c } // In the context of a lua callback, this will call the given function and if the given function -// returns an error, *or if the given function panics*, this will result in a call to lua_error (a -// longjmp) by a C shim. The error or panic is wrapped in such a way that when calling pop_error back -// on the rust side, it will resume the panic (or when popping a panic value from the stack). +// returns an error, *or if the given function panics*, this will result in a call to `lua_error` (a +// longjmp). The error or panic is wrapped in such a way that when calling `pop_error` back on +// the Rust side, it will resume the panic. // // This function assumes the structure of the stack at the beginning of a callback, that the only // elements on the stack are the arguments to the callback. // // This function uses some of the bottom of the stack for error handling, the given callback will be // given the number of arguments available as an argument, and should return the number of returns -// as normal, but cannot assume that the arguments available start at 1. -pub unsafe fn callback_error(state: *mut ffi::lua_State, f: F) -> c_int +// as normal, but cannot assume that the arguments available start at 0. +pub unsafe fn callback_error(state: *mut ffi::lua_State, f: F) -> R where - F: FnOnce(c_int) -> Result, + F: FnOnce(c_int) -> Result, { - let nargs = ffi::lua_gettop(state) - 1; + let nargs = ffi::lua_gettop(state); + + // We need one extra stack space to store preallocated memory, and at least 2 + // stack spaces overall for handling error metatables + let extra_stack = if nargs < 2 { 2 - nargs } else { 1 }; + ffi::luaL_checkstack( + state, + extra_stack, + cstr!("not enough stack space for callback error handling"), + ); + + // 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::().max(mem::size_of::()), + ); + ffi::lua_rotate(state, 1, 1); + match catch_unwind(AssertUnwindSafe(|| f(nargs))) { Ok(Ok(r)) => { ffi::lua_remove(state, 1); @@ -312,42 +498,120 @@ where } Ok(Err(err)) => { ffi::lua_settop(state, 1); - let error_ud = ffi::lua_touserdata(state, 1); - ptr::write(error_ud as *mut WrappedError, WrappedError(err)); + + let wrapped_error = ud as *mut WrappedError; + ptr::write(wrapped_error, WrappedError(err)); get_gc_metatable_for::(state); ffi::lua_setmetatable(state, -2); - -1 + + // Convert to CallbackError and attach traceback + let traceback = if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 { + ffi::luaL_traceback(state, state, ptr::null(), 0); + let traceback = to_string(state, -1); + ffi::lua_pop(state, 1); + traceback + } else { + "".to_string() + }; + let cause = Arc::new((*wrapped_error).0.clone()); + (*wrapped_error).0 = Error::CallbackError { traceback, cause }; + + ffi::lua_error(state) } Err(p) => { ffi::lua_settop(state, 1); - let error_ud = ffi::lua_touserdata(state, 1); - ptr::write(error_ud as *mut WrappedPanic, WrappedPanic(Some(p))); + ptr::write(ud as *mut WrappedPanic, WrappedPanic(Some(p))); get_gc_metatable_for::(state); ffi::lua_setmetatable(state, -2); - -2 + ffi::lua_error(state) } } } -// A part of the C shim for errors handling. -// Receives indexes of error and traceback (optional) in the stack. -// Converts error into a `CallbackError` and attaches the traceback provided. -#[no_mangle] -pub unsafe extern "C" fn wrapped_error_traceback( - state: *mut ffi::lua_State, - error_idx: c_int, - traceback_idx: c_int, -) { - let wrapped_error = mlua_expect!( - get_gc_userdata::(state, error_idx).as_mut(), - "cannot get " - ); - let traceback = match traceback_idx { - 0 => "".to_string(), - _ => to_string(state, traceback_idx), - }; - let cause = Arc::new(wrapped_error.0.clone()); - wrapped_error.0 = Error::CallbackError { traceback, cause }; +pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int { + if ffi::lua_checkstack(state, 2) == 0 { + // If we don't have enough stack space to even check the error type, do + // nothing so we don't risk shadowing a rust panic. + return 1; + } + + if get_gc_userdata::(state, -1).is_null() + && get_gc_userdata::(state, -1).is_null() + { + let s = ffi::luaL_tolstring(state, -1, ptr::null_mut()); + if ffi::lua_checkstack(state, LUA_TRACEBACK_STACK) != 0 { + ffi::luaL_traceback(state, state, s, 1); + ffi::lua_remove(state, -2); + } + } + + 1 +} + +// A variant of `pcall` that does not allow Lua to catch Rust panics from `callback_error`. +pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int { + ffi::luaL_checkstack(state, 2, ptr::null()); + + let top = ffi::lua_gettop(state); + if top == 0 { + ffi::lua_pushstring(state, cstr!("not enough arguments to pcall")); + ffi::lua_error(state); + } + + if ffi::lua_pcall(state, top - 1, ffi::LUA_MULTRET, 0) == ffi::LUA_OK { + ffi::lua_pushboolean(state, 1); + ffi::lua_insert(state, 1); + ffi::lua_gettop(state) + } else { + if !get_gc_userdata::(state, -1).is_null() { + ffi::lua_error(state); + } + ffi::lua_pushboolean(state, 0); + ffi::lua_insert(state, -2); + 2 + } +} + +// A variant of `xpcall` that does not allow Lua to catch Rust panics from `callback_error`. +pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int { + unsafe extern "C" fn xpcall_msgh(state: *mut ffi::lua_State) -> c_int { + ffi::luaL_checkstack(state, 2, ptr::null()); + + if !get_gc_userdata::(state, -1).is_null() { + 1 + } else { + ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1)); + ffi::lua_insert(state, 1); + ffi::lua_call(state, ffi::lua_gettop(state) - 1, ffi::LUA_MULTRET); + ffi::lua_gettop(state) + } + } + + ffi::luaL_checkstack(state, 2, ptr::null()); + + let top = ffi::lua_gettop(state); + if top < 2 { + ffi::lua_pushstring(state, cstr!("not enough arguments to xpcall")); + ffi::lua_error(state); + } + + ffi::lua_pushvalue(state, 2); + ffi::lua_pushcclosure(state, xpcall_msgh, 1); + ffi::lua_copy(state, 1, 2); + ffi::lua_replace(state, 1); + + if ffi::lua_pcall(state, ffi::lua_gettop(state) - 2, ffi::LUA_MULTRET, 1) == ffi::LUA_OK { + ffi::lua_pushboolean(state, 1); + ffi::lua_insert(state, 2); + ffi::lua_gettop(state) - 1 + } else { + if !get_gc_userdata::(state, -1).is_null() { + ffi::lua_error(state); + } + ffi::lua_pushboolean(state, 0); + ffi::lua_insert(state, -2); + 2 + } } // Returns Lua main thread for Lua >= 5.2 or checks that the passed thread is main for Lua 5.1. @@ -374,13 +638,9 @@ pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua } // Pushes a WrappedError to the top of the stack. -// Uses 2 stack spaces and does not call checkstack. +// Uses 3 stack spaces and does not call checkstack. pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> { - let error_ud = ffi::safe::lua_newwrappederror(state)? as *mut WrappedError; - ptr::write(error_ud, WrappedError(err)); - get_gc_metatable_for::(state); - ffi::lua_setmetatable(state, -2); - Ok(()) + push_gc_userdata::(state, WrappedError(err)) } // Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it, @@ -399,7 +659,7 @@ pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *co pub unsafe fn init_gc_metatable_for( state: *mut ffi::lua_State, customize_fn: Option Result<()>>, -) -> Result<*const u8> { +) -> Result<()> { check_stack(state, 6)?; let type_id = TypeId::of::(); @@ -413,21 +673,23 @@ pub unsafe fn init_gc_metatable_for( &mt_cache[&type_id] as *const u8 }; - ffi::safe::lua_createtable(state, 0, 3)?; + push_table(state, 0, 3)?; - ffi::safe::lua_pushrclosure(state, userdata_destructor::, 0)?; - ffi::safe::lua_rawsetfield(state, -2, "__gc")?; + ffi::lua_pushcfunction(state, userdata_destructor::); + rawset_field(state, -2, "__gc")?; ffi::lua_pushboolean(state, 0); - ffi::safe::lua_rawsetfield(state, -2, "__metatable")?; + rawset_field(state, -2, "__metatable")?; if let Some(f) = customize_fn { f(state)?; } - ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void)?; + protect_lua(state, 1, 0, |state| { + ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void) + })?; - Ok(ref_addr) + Ok(()) } pub unsafe fn get_gc_metatable_for(state: *mut ffi::lua_State) { @@ -441,7 +703,7 @@ pub unsafe fn get_gc_metatable_for(state: *mut ffi::lua_State) { // Initialize the error, panic, and destructed userdata metatables. // Returns address of WrappedError and WrappedPanic metatables in Lua registry. -pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const u8, *const u8)> { +pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> { check_stack(state, 7)?; // Create error and panic metatables @@ -509,44 +771,37 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const Err(Error::UserDataTypeMismatch) }?; - ffi::safe::lua_pushstring(state, &*err_buf)?; + push_string(state, &*err_buf)?; (*err_buf).clear(); Ok(1) }) } - let wrapped_error_key = init_gc_metatable_for::( + init_gc_metatable_for::( state, Some(|state| { - ffi::safe::lua_pushrclosure(state, error_tostring, 0)?; - ffi::safe::lua_rawsetfield(state, -2, "__tostring") + ffi::lua_pushcfunction(state, error_tostring); + rawset_field(state, -2, "__tostring") }), )?; - let wrapped_panic_key = init_gc_metatable_for::( + init_gc_metatable_for::( state, Some(|state| { - ffi::safe::lua_pushrclosure(state, error_tostring, 0)?; - ffi::safe::lua_rawsetfield(state, -2, "__tostring") + ffi::lua_pushcfunction(state, error_tostring); + rawset_field(state, -2, "__tostring") }), )?; // Create destructed userdata metatable unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int { - callback_error(state, |_| { - check_stack(state, 2)?; - let error_ud = ffi::safe::lua_newwrappederror(state)? as *mut WrappedError; - ptr::write(error_ud, WrappedError(Error::CallbackDestructed)); - get_gc_metatable_for::(state); - ffi::lua_setmetatable(state, -2); - Ok(-1) // to trigger lua_error - }) + callback_error(state, |_| Err(Error::CallbackDestructed)) } - ffi::safe::lua_createtable(state, 0, 26)?; - ffi::safe::lua_pushrclosure(state, destructed_error, 0)?; + push_table(state, 0, 26)?; + ffi::lua_pushcfunction(state, destructed_error); for &method in &[ "__add", "__sub", @@ -586,20 +841,24 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const "__close", ] { ffi::lua_pushvalue(state, -1); - ffi::safe::lua_rawsetfield(state, -3, method)?; + rawset_field(state, -3, method)?; } ffi::lua_pop(state, 1); let destructed_metatable_key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void; - ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, destructed_metatable_key)?; + protect_lua(state, 1, 0, |state| { + ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, destructed_metatable_key) + })?; // Create error print buffer init_gc_metatable_for::(state, None)?; push_gc_userdata(state, String::new())?; let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void; - ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key)?; + protect_lua(state, 1, 0, |state| { + ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key) + })?; - Ok((wrapped_error_key, wrapped_panic_key)) + Ok(()) } pub(crate) struct WrappedError(pub Error);