From 67e8907f196cf00d1908747e975facbad25c3f7b Mon Sep 17 00:00:00 2001 From: kyren Date: Sun, 3 Dec 2017 23:01:03 -0500 Subject: [PATCH] Couple of changes in preparation for 'm' safety: - auto formatting - add gc control to ffi - add gc_guard to util functions - use gc_guard to make util error handling functions never trigger __gc metamethod Lua errors even without __gc metatable wrapper - sort of a technicality, don't call luaL_requiref outside of the Lua constructor, as it could trigger the garbage collector when user code has had a chance to set __gc metamethods. Changes the API to load the debug table. --- src/ffi.rs | 12 ++ src/lua.rs | 347 ++++++++++++++++++++++++++------------------------- src/table.rs | 17 +-- src/tests.rs | 8 +- src/util.rs | 140 +++++++++++++-------- 5 files changed, 285 insertions(+), 239 deletions(-) diff --git a/src/ffi.rs b/src/ffi.rs index 9b03b58..707cfc9 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -51,6 +51,16 @@ pub const LUA_TFUNCTION: c_int = 6; pub const LUA_TUSERDATA: c_int = 7; pub const LUA_TTHREAD: c_int = 8; +pub const LUA_GCSTOP: c_int = 0; +pub const LUA_GCRESTART: c_int = 1; +pub const LUA_GCCOLLECT: c_int = 2; +pub const LUA_GCCOUNT: c_int = 3; +pub const LUA_GCCOUNTB: c_int = 4; +pub const LUA_GCSTEP: c_int = 5; +pub const LUA_GCSETPAUSE: c_int = 6; +pub const LUA_GCSETSTEPMUL: c_int = 7; +pub const LUA_GCISRUNNING: c_int = 9; + #[link(name = "lua5.3")] extern "C" { pub fn lua_newstate(alloc: lua_Alloc, ud: *mut c_void) -> *mut lua_State; @@ -80,6 +90,7 @@ extern "C" { pub fn lua_pushinteger(state: *mut lua_State, n: lua_Integer); pub fn lua_pushnumber(state: *mut lua_State, n: lua_Number); pub fn lua_pushlstring(state: *mut lua_State, s: *const c_char, len: usize) -> *const c_char; + pub fn lua_pushstring(state: *mut lua_State, s: *const c_char) -> *const c_char; pub fn lua_pushlightuserdata(state: *mut lua_State, data: *mut c_void); pub fn lua_pushcclosure(state: *mut lua_State, function: lua_CFunction, n: c_int); @@ -125,6 +136,7 @@ extern "C" { pub fn lua_error(state: *mut lua_State) -> !; pub fn lua_atpanic(state: *mut lua_State, panic: lua_CFunction) -> lua_CFunction; + pub fn lua_gc(state: *mut lua_State, what: c_int, data: c_int) -> c_int; pub fn luaopen_base(state: *mut lua_State) -> c_int; pub fn luaopen_coroutine(state: *mut lua_State) -> c_int; diff --git a/src/lua.rs b/src/lua.rs index f11de04..08cb816 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -449,7 +449,7 @@ impl Drop for Lua { let top = ffi::lua_gettop(self.state); if top != 0 { eprintln!("Lua stack leak detected, stack top is {}", top); - ::std::process::abort() + process::abort() } } @@ -460,122 +460,17 @@ impl Drop for Lua { } impl Lua { - /// Creates a new Lua state. - /// - /// Also loads the standard library. + /// Creates a new Lua state and loads standard library without the `debug` library. pub fn new() -> Lua { - unsafe extern "C" fn allocator( - _: *mut c_void, - ptr: *mut c_void, - _: usize, - nsize: usize, - ) -> *mut c_void { - if nsize == 0 { - libc::free(ptr as *mut libc::c_void); - ptr::null_mut() - } else { - let p = libc::realloc(ptr as *mut libc::c_void, nsize); - if p.is_null() { - // We must abort on OOM, because otherwise this will result in an unsafe - // longjmp. - eprintln!("Out of memory in Lua allocation, aborting!"); - process::abort() - } else { - p as *mut c_void - } - } - } - - unsafe { - let state = ffi::lua_newstate(allocator, ptr::null_mut()); - - stack_guard(state, 0, || { - // Do not open the debug library, currently it can be used to cause unsafety. - ffi::luaL_requiref(state, cstr!("_G"), ffi::luaopen_base, 1); - ffi::luaL_requiref(state, cstr!("coroutine"), ffi::luaopen_coroutine, 1); - ffi::luaL_requiref(state, cstr!("table"), ffi::luaopen_table, 1); - ffi::luaL_requiref(state, cstr!("io"), ffi::luaopen_io, 1); - ffi::luaL_requiref(state, cstr!("os"), ffi::luaopen_os, 1); - ffi::luaL_requiref(state, cstr!("string"), ffi::luaopen_string, 1); - ffi::luaL_requiref(state, cstr!("utf8"), ffi::luaopen_utf8, 1); - ffi::luaL_requiref(state, cstr!("math"), ffi::luaopen_math, 1); - ffi::luaL_requiref(state, cstr!("package"), ffi::luaopen_package, 1); - ffi::lua_pop(state, 9); - - // Create the userdata registry table - - ffi::lua_pushlightuserdata( - state, - &LUA_USERDATA_REGISTRY_KEY as *const u8 as *mut c_void, - ); - - push_userdata::>(state, HashMap::new()); - - ffi::lua_newtable(state); - - push_string(state, "__gc"); - ffi::lua_pushcfunction(state, userdata_destructor::>); - ffi::lua_rawset(state, -3); - - ffi::lua_setmetatable(state, -2); - - ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX); - - // Create the function metatable - - ffi::lua_pushlightuserdata( - state, - &FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void, - ); - - ffi::lua_newtable(state); - - push_string(state, "__gc"); - ffi::lua_pushcfunction(state, userdata_destructor::>); - ffi::lua_rawset(state, -3); - - push_string(state, "__metatable"); - ffi::lua_pushboolean(state, 0); - ffi::lua_rawset(state, -3); - - ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX); - - // Override pcall, xpcall, and setmetatable with versions that cannot be used to - // cause unsafety. - - ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS); - - push_string(state, "pcall"); - ffi::lua_pushcfunction(state, safe_pcall); - ffi::lua_rawset(state, -3); - - push_string(state, "xpcall"); - ffi::lua_pushcfunction(state, safe_xpcall); - ffi::lua_rawset(state, -3); - - push_string(state, "setmetatable"); - ffi::lua_pushcfunction(state, safe_setmetatable); - ffi::lua_rawset(state, -3); - - ffi::lua_pop(state, 1); - }); - - Lua { - state, - main_state: state, - ephemeral: false, - } - } + unsafe { Lua::create_lua(false) } } - /// Loads the Lua debug library. + /// Creates a new Lua state and loads the standard library including the `debug` library. /// - /// The debug library is very unsound, loading it and using it breaks all - /// the guarantees of rlua. - pub unsafe fn load_debug(&self) { - check_stack(self.state, 1); - ffi::luaL_requiref(self.state, cstr!("debug"), ffi::luaopen_debug, 1); - ffi::lua_pop(self.state, 1); + /// The debug library is very unsound, loading it and using it breaks all the guarantees of + /// rlua. + pub unsafe fn new_with_debug() -> Lua { + Lua::create_lua(true) } /// Loads a chunk of Lua code and returns it as a function. @@ -914,65 +809,6 @@ impl Lua { T::from_lua_multi(value, self) } - fn create_callback_function<'lua>(&'lua self, func: Callback<'lua>) -> Function<'lua> { - unsafe extern "C" fn callback_call_impl(state: *mut ffi::lua_State) -> c_int { - callback_error(state, || { - let lua = Lua { - state: state, - main_state: main_state(state), - ephemeral: true, - }; - - let func = get_userdata::>(state, ffi::lua_upvalueindex(1)); - let mut func = if let Ok(func) = (*func).try_borrow_mut() { - func - } else { - lua_panic!( - state, - "recursive callback function call would mutably borrow function twice" - ); - }; - - let nargs = ffi::lua_gettop(state); - let mut args = MultiValue::new(); - check_stack(state, 1); - for _ in 0..nargs { - args.push_front(lua.pop_value(state)); - } - - let results = func.deref_mut()(&lua, args)?; - let nresults = results.len() as c_int; - - check_stack(state, nresults); - - for r in results { - lua.push_value(state, r); - } - - Ok(nresults) - }) - } - - unsafe { - stack_guard(self.state, 0, move || { - check_stack(self.state, 2); - - push_userdata::>(self.state, RefCell::new(func)); - - ffi::lua_pushlightuserdata( - self.state, - &FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void, - ); - ffi::lua_gettable(self.state, ffi::LUA_REGISTRYINDEX); - ffi::lua_setmetatable(self.state, -2); - - ffi::lua_pushcclosure(self.state, callback_call_impl, 1); - - Function(self.pop_ref(self.state)) - }) - } - } - // Used 1 stack space, does not call checkstack pub(crate) unsafe fn push_value(&self, state: *mut ffi::lua_State, value: Value) { match value { @@ -1228,6 +1064,173 @@ impl Lua { id }) } + + unsafe fn create_lua(load_debug: bool) -> Lua { + unsafe extern "C" fn allocator( + _: *mut c_void, + ptr: *mut c_void, + _: usize, + nsize: usize, + ) -> *mut c_void { + if nsize == 0 { + libc::free(ptr as *mut libc::c_void); + ptr::null_mut() + } else { + let p = libc::realloc(ptr as *mut libc::c_void, nsize); + if p.is_null() { + // We must abort on OOM, because otherwise this will result in an unsafe + // longjmp. + eprintln!("Out of memory in Lua allocation, aborting!"); + process::abort() + } else { + p as *mut c_void + } + } + } + + let state = ffi::lua_newstate(allocator, ptr::null_mut()); + + stack_guard(state, 0, || { + // Do not open the debug library, it can be used to cause unsafety. + ffi::luaL_requiref(state, cstr!("_G"), ffi::luaopen_base, 1); + ffi::luaL_requiref(state, cstr!("coroutine"), ffi::luaopen_coroutine, 1); + ffi::luaL_requiref(state, cstr!("table"), ffi::luaopen_table, 1); + ffi::luaL_requiref(state, cstr!("io"), ffi::luaopen_io, 1); + ffi::luaL_requiref(state, cstr!("os"), ffi::luaopen_os, 1); + ffi::luaL_requiref(state, cstr!("string"), ffi::luaopen_string, 1); + ffi::luaL_requiref(state, cstr!("utf8"), ffi::luaopen_utf8, 1); + ffi::luaL_requiref(state, cstr!("math"), ffi::luaopen_math, 1); + ffi::luaL_requiref(state, cstr!("package"), ffi::luaopen_package, 1); + ffi::lua_pop(state, 9); + + if load_debug { + ffi::luaL_requiref(state, cstr!("debug"), ffi::luaopen_debug, 1); + ffi::lua_pop(state, 1); + } + + // Create the userdata registry table + + ffi::lua_pushlightuserdata( + state, + &LUA_USERDATA_REGISTRY_KEY as *const u8 as *mut c_void, + ); + + push_userdata::>(state, HashMap::new()); + + ffi::lua_newtable(state); + + push_string(state, "__gc"); + ffi::lua_pushcfunction(state, userdata_destructor::>); + ffi::lua_rawset(state, -3); + + ffi::lua_setmetatable(state, -2); + + ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX); + + // Create the function metatable + + ffi::lua_pushlightuserdata( + state, + &FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void, + ); + + ffi::lua_newtable(state); + + push_string(state, "__gc"); + ffi::lua_pushcfunction(state, userdata_destructor::>); + ffi::lua_rawset(state, -3); + + push_string(state, "__metatable"); + ffi::lua_pushboolean(state, 0); + ffi::lua_rawset(state, -3); + + ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX); + + // Override pcall, xpcall, and setmetatable with versions that cannot be used to + // cause unsafety. + + ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS); + + push_string(state, "pcall"); + ffi::lua_pushcfunction(state, safe_pcall); + ffi::lua_rawset(state, -3); + + push_string(state, "xpcall"); + ffi::lua_pushcfunction(state, safe_xpcall); + ffi::lua_rawset(state, -3); + + push_string(state, "setmetatable"); + ffi::lua_pushcfunction(state, safe_setmetatable); + ffi::lua_rawset(state, -3); + + ffi::lua_pop(state, 1); + }); + + Lua { + state, + main_state: state, + ephemeral: false, + } + } + + fn create_callback_function<'lua>(&'lua self, func: Callback<'lua>) -> Function<'lua> { + unsafe extern "C" fn callback_call_impl(state: *mut ffi::lua_State) -> c_int { + callback_error(state, || { + let lua = Lua { + state: state, + main_state: main_state(state), + ephemeral: true, + }; + + let func = get_userdata::>(state, ffi::lua_upvalueindex(1)); + let mut func = if let Ok(func) = (*func).try_borrow_mut() { + func + } else { + lua_panic!( + state, + "recursive callback function call would mutably borrow function twice" + ); + }; + + let nargs = ffi::lua_gettop(state); + let mut args = MultiValue::new(); + check_stack(state, 1); + for _ in 0..nargs { + args.push_front(lua.pop_value(state)); + } + + let results = func.deref_mut()(&lua, args)?; + let nresults = results.len() as c_int; + + check_stack(state, nresults); + + for r in results { + lua.push_value(state, r); + } + + Ok(nresults) + }) + } + + unsafe { + stack_guard(self.state, 0, move || { + check_stack(self.state, 2); + + push_userdata::>(self.state, RefCell::new(func)); + + ffi::lua_pushlightuserdata( + self.state, + &FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void, + ); + ffi::lua_gettable(self.state, ffi::LUA_REGISTRYINDEX); + ffi::lua_setmetatable(self.state, -2); + + ffi::lua_pushcclosure(self.state, callback_call_impl, 1); + + Function(self.pop_ref(self.state)) + }) + } + } } static LUA_USERDATA_REGISTRY_KEY: u8 = 0; diff --git a/src/table.rs b/src/table.rs index 7da679f..2717013 100644 --- a/src/table.rs +++ b/src/table.rs @@ -98,9 +98,7 @@ impl<'lua> Table<'lua> { check_stack(lua.state, 5); lua.push_ref(lua.state, &self.0); lua.push_value(lua.state, key.to_lua(lua)?); - protect_lua_call(lua.state, 2, 1, |state| { - ffi::lua_gettable(state, -2) - })?; + protect_lua_call(lua.state, 2, 1, |state| ffi::lua_gettable(state, -2))?; V::from_lua(lua.pop_value(lua.state), lua) }) } @@ -114,9 +112,7 @@ impl<'lua> Table<'lua> { check_stack(lua.state, 5); lua.push_ref(lua.state, &self.0); lua.push_value(lua.state, key.to_lua(lua)?); - protect_lua_call(lua.state, 2, 1, |state| { - ffi::lua_gettable(state, -2) - })?; + protect_lua_call(lua.state, 2, 1, |state| ffi::lua_gettable(state, -2))?; let has = ffi::lua_isnil(lua.state, -1) == 0; ffi::lua_pop(lua.state, 1); Ok(has) @@ -167,9 +163,7 @@ impl<'lua> Table<'lua> { stack_err_guard(lua.state, 0, || { check_stack(lua.state, 4); lua.push_ref(lua.state, &self.0); - protect_lua_call(lua.state, 1, 0, |state| { - ffi::luaL_len(state, -1) - }) + protect_lua_call(lua.state, 1, 0, |state| ffi::luaL_len(state, -1)) }) } } @@ -416,9 +410,8 @@ where check_stack(lua.state, 4); lua.push_ref(lua.state, &self.table); - match protect_lua_call(lua.state, 1, 1, |state| { - ffi::lua_geti(state, -1, index) - }) { + match protect_lua_call(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index)) + { Ok(ffi::LUA_TNIL) => { ffi::lua_pop(lua.state, 1); None diff --git a/src/tests.rs b/src/tests.rs index bd0f94f..06c8d88 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -15,12 +15,8 @@ fn test_load() { } #[test] -fn test_load_debug() { - let lua = Lua::new(); - lua.exec::<()>("assert(debug == nil)", None).unwrap(); - unsafe { - lua.load_debug(); - } +fn test_debug() { + let lua = unsafe { Lua::new_with_debug() }; match lua.eval("debug", None).unwrap() { Value::Table(_) => {} val => panic!("Expected table for debug library, got {:#?}", val), diff --git a/src/util.rs b/src/util.rs index 225778e..6c18218 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,4 @@ -use std::{mem, ptr, process}; +use std::{mem, process, ptr}; use std::sync::Arc; use std::ffi::CStr; use std::any::Any; @@ -97,8 +97,14 @@ where // given function return type is not the return value count, instead the inner function return // values are assumed to match the `nresults` param. Internally uses 3 extra stack spaces, and does // not call checkstack. -pub unsafe fn protect_lua_call(state: *mut ffi::lua_State, nargs: c_int, nresults: c_int, f: F) -> Result - where F: FnMut(*mut ffi::lua_State) -> R, +pub unsafe fn protect_lua_call( + state: *mut ffi::lua_State, + nargs: c_int, + nresults: c_int, + f: F, +) -> Result +where + F: FnMut(*mut ffi::lua_State) -> R, { struct Params { function: F, @@ -107,7 +113,8 @@ pub unsafe fn protect_lua_call(state: *mut ffi::lua_State, nargs: c_int, n } unsafe extern "C" fn do_call(state: *mut ffi::lua_State) -> c_int - where F: FnMut(*mut ffi::lua_State) -> R + where + F: FnMut(*mut ffi::lua_State) -> R, { let params = ffi::lua_touserdata(state, -1) as *mut Params; ffi::lua_pop(state, 1); @@ -144,6 +151,21 @@ pub unsafe fn protect_lua_call(state: *mut ffi::lua_State, nargs: c_int, n } } +// Runs the given function with the Lua garbage collector disabled. `rlua` assumes that all memory +// errors are aborts, so in this way, 'm' functions that may also cause a `__gc` metamethod error +// are guaranteed not to cause a Lua error (longjmp). The given function should never panic or +// longjmp, because this could inadverntently disable the gc. +pub unsafe fn gc_guard R>(state: *mut ffi::lua_State, f: F) -> R { + if ffi::lua_gc(state, ffi::LUA_GCISRUNNING, 0) != 0 { + ffi::lua_gc(state, ffi::LUA_GCSTOP, 0); + let r = f(); + ffi::lua_gc(state, ffi::LUA_GCRESTART, 0); + r + } else { + f() + } +} + // Pops an error off of the stack and returns it. If the error is actually a WrappedPanic, clears // the current lua stack and continues the panic. If the error on the top of the stack is actually // a WrappedError, just returns it. Otherwise, interprets the error as the appropriate lua error. @@ -165,14 +187,14 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error { lua_panic!(state, "internal error: panic was resumed twice") } } else { - let err_string = if ffi::lua_type(state, -1) == ffi::LUA_TSTRING { - // lua_tostring only throws memory errors when the type is not already a string - CStr::from_ptr(ffi::lua_tostring(state, -1)) - .to_string_lossy() - .into_owned() - } else { - "".to_owned() - }; + let err_string = gc_guard(state, || { + if let Some(s) = ffi::lua_tostring(state, -1).as_ref() { + CStr::from_ptr(s).to_string_lossy().into_owned() + } else { + "".to_owned() + } + }); + ffi::lua_pop(state, 1); match err_code { ffi::LUA_ERRRUN => Error::RuntimeError(err_string), @@ -287,7 +309,7 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int { pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int { let top = ffi::lua_gettop(state); if top == 0 { - push_string(state, "not enough arguments to pcall"); + ffi::lua_pushstring(state, cstr!("not enough arguments to pcall")); ffi::lua_error(state); } else if ffi::lua_pcall(state, top - 1, ffi::LUA_MULTRET, 0) != ffi::LUA_OK { if is_wrapped_panic(state, -1) { @@ -318,7 +340,7 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int { let top = ffi::lua_gettop(state); if top < 2 { - push_string(state, "not enough arguments to xpcall"); + ffi::lua_pushstring(state, cstr!("not enough arguments to xpcall")); ffi::lua_error(state); } @@ -345,13 +367,13 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int { // Safely call setmetatable, if a __gc function is given, will wrap it in pcall, and panic on error. pub unsafe extern "C" fn safe_setmetatable(state: *mut ffi::lua_State) -> c_int { if ffi::lua_gettop(state) < 2 { - push_string(state, "not enough arguments to setmetatable"); + ffi::lua_pushstring(state, cstr!("not enough arguments to setmetatable")); ffi::lua_error(state); } // Wrapping the __gc method in setmetatable ONLY works because Lua 5.3 only honors the __gc // method when it exists upon calling setmetatable, and ignores it if it is set later. - push_string(state, "__gc"); + ffi::lua_pushstring(state, cstr!("__gc")); if ffi::lua_istable(state, -2) == 1 && ffi::lua_rawget(state, -2) == ffi::LUA_TFUNCTION { unsafe extern "C" fn safe_gc(state: *mut ffi::lua_State) -> c_int { ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1)); @@ -369,7 +391,7 @@ pub unsafe extern "C" fn safe_setmetatable(state: *mut ffi::lua_State) -> c_int } ffi::lua_pushcclosure(state, safe_gc, 1); - push_string(state, "__gc"); + ffi::lua_pushstring(state, cstr!("__gc")); ffi::lua_insert(state, -2); ffi::lua_rawset(state, -3); } else { @@ -391,7 +413,12 @@ pub unsafe fn main_state(state: *mut ffi::lua_State) -> *mut ffi::lua_State { pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) { ffi::luaL_checkstack(state, 2, ptr::null()); - push_userdata(state, WrappedError(err)); + gc_guard(state, || { + let ud = ffi::lua_newuserdata(state, mem::size_of::>()) + as *mut Option; + ptr::write(ud, Some(WrappedError(err))) + }); + get_error_metatable(state); ffi::lua_setmetatable(state, -2); @@ -417,7 +444,11 @@ struct WrappedPanic(pub Option>); unsafe fn push_wrapped_panic(state: *mut ffi::lua_State, panic: Box) { ffi::luaL_checkstack(state, 2, ptr::null()); - push_userdata(state, WrappedPanic(Some(panic))); + gc_guard(state, || { + let ud = ffi::lua_newuserdata(state, mem::size_of::>()) + as *mut Option; + ptr::write(ud, Some(WrappedPanic(Some(panic)))) + }); get_panic_metatable(state); ffi::lua_setmetatable(state, -2); @@ -480,7 +511,14 @@ unsafe fn get_error_metatable(state: *mut ffi::lua_State) -> c_int { callback_error(state, || { if is_wrapped_error(state, -1) { let error = get_userdata::(state, -1); - push_string(state, &(*error).0.to_string()); + let error_str = (*error).0.to_string(); + gc_guard(state, || { + ffi::lua_pushlstring( + state, + error_str.as_ptr() as *const c_char, + error_str.len(), + ) + }); ffi::lua_remove(state, -2); Ok(1) @@ -501,26 +539,28 @@ unsafe fn get_error_metatable(state: *mut ffi::lua_State) -> c_int { ffi::luaL_checkstack(state, 8, ptr::null()); - ffi::lua_newtable(state); - ffi::lua_pushlightuserdata( - state, - &ERROR_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void, - ); - ffi::lua_pushvalue(state, -2); + gc_guard(state, || { + ffi::lua_newtable(state); + ffi::lua_pushlightuserdata( + state, + &ERROR_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void, + ); + ffi::lua_pushvalue(state, -2); - push_string(state, "__gc"); - ffi::lua_pushcfunction(state, userdata_destructor::); - ffi::lua_settable(state, -3); + ffi::lua_pushstring(state, cstr!("__gc")); + ffi::lua_pushcfunction(state, userdata_destructor::); + ffi::lua_settable(state, -3); - push_string(state, "__tostring"); - ffi::lua_pushcfunction(state, error_tostring); - ffi::lua_settable(state, -3); + ffi::lua_pushstring(state, cstr!("__tostring")); + ffi::lua_pushcfunction(state, error_tostring); + ffi::lua_settable(state, -3); - push_string(state, "__metatable"); - ffi::lua_pushboolean(state, 0); - ffi::lua_settable(state, -3); + ffi::lua_pushstring(state, cstr!("__metatable")); + ffi::lua_pushboolean(state, 0); + ffi::lua_settable(state, -3); - ffi::lua_settable(state, ffi::LUA_REGISTRYINDEX); + ffi::lua_settable(state, ffi::LUA_REGISTRYINDEX); + }) } ffi::LUA_TTABLE @@ -540,22 +580,24 @@ unsafe fn get_panic_metatable(state: *mut ffi::lua_State) -> c_int { ffi::luaL_checkstack(state, 8, ptr::null()); - ffi::lua_newtable(state); - ffi::lua_pushlightuserdata( - state, - &PANIC_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void, - ); - ffi::lua_pushvalue(state, -2); + gc_guard(state, || { + ffi::lua_newtable(state); + ffi::lua_pushlightuserdata( + state, + &PANIC_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void, + ); + ffi::lua_pushvalue(state, -2); - push_string(state, "__gc"); - ffi::lua_pushcfunction(state, userdata_destructor::); - ffi::lua_settable(state, -3); + push_string(state, "__gc"); + ffi::lua_pushcfunction(state, userdata_destructor::); + ffi::lua_settable(state, -3); - push_string(state, "__metatable"); - ffi::lua_pushboolean(state, 0); - ffi::lua_settable(state, -3); + push_string(state, "__metatable"); + ffi::lua_pushboolean(state, 0); + ffi::lua_settable(state, -3); - ffi::lua_settable(state, ffi::LUA_REGISTRYINDEX); + ffi::lua_settable(state, ffi::LUA_REGISTRYINDEX); + }); } ffi::LUA_TTABLE