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.
This commit is contained in:
parent
0bd676aa81
commit
67e8907f19
12
src/ffi.rs
12
src/ffi.rs
|
@ -51,6 +51,16 @@ pub const LUA_TFUNCTION: c_int = 6;
|
||||||
pub const LUA_TUSERDATA: c_int = 7;
|
pub const LUA_TUSERDATA: c_int = 7;
|
||||||
pub const LUA_TTHREAD: c_int = 8;
|
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")]
|
#[link(name = "lua5.3")]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn lua_newstate(alloc: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
|
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_pushinteger(state: *mut lua_State, n: lua_Integer);
|
||||||
pub fn lua_pushnumber(state: *mut lua_State, n: lua_Number);
|
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_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_pushlightuserdata(state: *mut lua_State, data: *mut c_void);
|
||||||
pub fn lua_pushcclosure(state: *mut lua_State, function: lua_CFunction, n: c_int);
|
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_error(state: *mut lua_State) -> !;
|
||||||
pub fn lua_atpanic(state: *mut lua_State, panic: lua_CFunction) -> lua_CFunction;
|
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_base(state: *mut lua_State) -> c_int;
|
||||||
pub fn luaopen_coroutine(state: *mut lua_State) -> c_int;
|
pub fn luaopen_coroutine(state: *mut lua_State) -> c_int;
|
||||||
|
|
347
src/lua.rs
347
src/lua.rs
|
@ -449,7 +449,7 @@ impl Drop for Lua {
|
||||||
let top = ffi::lua_gettop(self.state);
|
let top = ffi::lua_gettop(self.state);
|
||||||
if top != 0 {
|
if top != 0 {
|
||||||
eprintln!("Lua stack leak detected, stack top is {}", top);
|
eprintln!("Lua stack leak detected, stack top is {}", top);
|
||||||
::std::process::abort()
|
process::abort()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,122 +460,17 @@ impl Drop for Lua {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Lua {
|
impl Lua {
|
||||||
/// Creates a new Lua state.
|
/// Creates a new Lua state and loads standard library without the `debug` library.
|
||||||
///
|
|
||||||
/// Also loads the standard library.
|
|
||||||
pub fn new() -> Lua {
|
pub fn new() -> Lua {
|
||||||
unsafe extern "C" fn allocator(
|
unsafe { Lua::create_lua(false) }
|
||||||
_: *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 {
|
/// Creates a new Lua state and loads the standard library including the `debug` library.
|
||||||
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::<HashMap<TypeId, c_int>>(state, HashMap::new());
|
|
||||||
|
|
||||||
ffi::lua_newtable(state);
|
|
||||||
|
|
||||||
push_string(state, "__gc");
|
|
||||||
ffi::lua_pushcfunction(state, userdata_destructor::<HashMap<TypeId, c_int>>);
|
|
||||||
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::<RefCell<Callback>>);
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Loads the Lua debug library.
|
|
||||||
///
|
///
|
||||||
/// The debug library is very unsound, loading it and using it breaks all
|
/// The debug library is very unsound, loading it and using it breaks all the guarantees of
|
||||||
/// the guarantees of rlua.
|
/// rlua.
|
||||||
pub unsafe fn load_debug(&self) {
|
pub unsafe fn new_with_debug() -> Lua {
|
||||||
check_stack(self.state, 1);
|
Lua::create_lua(true)
|
||||||
ffi::luaL_requiref(self.state, cstr!("debug"), ffi::luaopen_debug, 1);
|
|
||||||
ffi::lua_pop(self.state, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads a chunk of Lua code and returns it as a function.
|
/// Loads a chunk of Lua code and returns it as a function.
|
||||||
|
@ -914,65 +809,6 @@ impl Lua {
|
||||||
T::from_lua_multi(value, self)
|
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::<RefCell<Callback>>(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::<RefCell<Callback>>(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
|
// Used 1 stack space, does not call checkstack
|
||||||
pub(crate) unsafe fn push_value(&self, state: *mut ffi::lua_State, value: Value) {
|
pub(crate) unsafe fn push_value(&self, state: *mut ffi::lua_State, value: Value) {
|
||||||
match value {
|
match value {
|
||||||
|
@ -1228,6 +1064,173 @@ impl Lua {
|
||||||
id
|
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::<HashMap<TypeId, c_int>>(state, HashMap::new());
|
||||||
|
|
||||||
|
ffi::lua_newtable(state);
|
||||||
|
|
||||||
|
push_string(state, "__gc");
|
||||||
|
ffi::lua_pushcfunction(state, userdata_destructor::<HashMap<TypeId, c_int>>);
|
||||||
|
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::<RefCell<Callback>>);
|
||||||
|
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::<RefCell<Callback>>(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::<RefCell<Callback>>(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;
|
static LUA_USERDATA_REGISTRY_KEY: u8 = 0;
|
||||||
|
|
17
src/table.rs
17
src/table.rs
|
@ -98,9 +98,7 @@ impl<'lua> Table<'lua> {
|
||||||
check_stack(lua.state, 5);
|
check_stack(lua.state, 5);
|
||||||
lua.push_ref(lua.state, &self.0);
|
lua.push_ref(lua.state, &self.0);
|
||||||
lua.push_value(lua.state, key.to_lua(lua)?);
|
lua.push_value(lua.state, key.to_lua(lua)?);
|
||||||
protect_lua_call(lua.state, 2, 1, |state| {
|
protect_lua_call(lua.state, 2, 1, |state| ffi::lua_gettable(state, -2))?;
|
||||||
ffi::lua_gettable(state, -2)
|
|
||||||
})?;
|
|
||||||
V::from_lua(lua.pop_value(lua.state), lua)
|
V::from_lua(lua.pop_value(lua.state), lua)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -114,9 +112,7 @@ impl<'lua> Table<'lua> {
|
||||||
check_stack(lua.state, 5);
|
check_stack(lua.state, 5);
|
||||||
lua.push_ref(lua.state, &self.0);
|
lua.push_ref(lua.state, &self.0);
|
||||||
lua.push_value(lua.state, key.to_lua(lua)?);
|
lua.push_value(lua.state, key.to_lua(lua)?);
|
||||||
protect_lua_call(lua.state, 2, 1, |state| {
|
protect_lua_call(lua.state, 2, 1, |state| ffi::lua_gettable(state, -2))?;
|
||||||
ffi::lua_gettable(state, -2)
|
|
||||||
})?;
|
|
||||||
let has = ffi::lua_isnil(lua.state, -1) == 0;
|
let has = ffi::lua_isnil(lua.state, -1) == 0;
|
||||||
ffi::lua_pop(lua.state, 1);
|
ffi::lua_pop(lua.state, 1);
|
||||||
Ok(has)
|
Ok(has)
|
||||||
|
@ -167,9 +163,7 @@ impl<'lua> Table<'lua> {
|
||||||
stack_err_guard(lua.state, 0, || {
|
stack_err_guard(lua.state, 0, || {
|
||||||
check_stack(lua.state, 4);
|
check_stack(lua.state, 4);
|
||||||
lua.push_ref(lua.state, &self.0);
|
lua.push_ref(lua.state, &self.0);
|
||||||
protect_lua_call(lua.state, 1, 0, |state| {
|
protect_lua_call(lua.state, 1, 0, |state| ffi::luaL_len(state, -1))
|
||||||
ffi::luaL_len(state, -1)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -416,9 +410,8 @@ where
|
||||||
check_stack(lua.state, 4);
|
check_stack(lua.state, 4);
|
||||||
|
|
||||||
lua.push_ref(lua.state, &self.table);
|
lua.push_ref(lua.state, &self.table);
|
||||||
match protect_lua_call(lua.state, 1, 1, |state| {
|
match protect_lua_call(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index))
|
||||||
ffi::lua_geti(state, -1, index)
|
{
|
||||||
}) {
|
|
||||||
Ok(ffi::LUA_TNIL) => {
|
Ok(ffi::LUA_TNIL) => {
|
||||||
ffi::lua_pop(lua.state, 1);
|
ffi::lua_pop(lua.state, 1);
|
||||||
None
|
None
|
||||||
|
|
|
@ -15,12 +15,8 @@ fn test_load() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_load_debug() {
|
fn test_debug() {
|
||||||
let lua = Lua::new();
|
let lua = unsafe { Lua::new_with_debug() };
|
||||||
lua.exec::<()>("assert(debug == nil)", None).unwrap();
|
|
||||||
unsafe {
|
|
||||||
lua.load_debug();
|
|
||||||
}
|
|
||||||
match lua.eval("debug", None).unwrap() {
|
match lua.eval("debug", None).unwrap() {
|
||||||
Value::Table(_) => {}
|
Value::Table(_) => {}
|
||||||
val => panic!("Expected table for debug library, got {:#?}", val),
|
val => panic!("Expected table for debug library, got {:#?}", val),
|
||||||
|
|
86
src/util.rs
86
src/util.rs
|
@ -1,4 +1,4 @@
|
||||||
use std::{mem, ptr, process};
|
use std::{mem, process, ptr};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
@ -97,8 +97,14 @@ where
|
||||||
// given function return type is not the return value count, instead the inner function return
|
// 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
|
// values are assumed to match the `nresults` param. Internally uses 3 extra stack spaces, and does
|
||||||
// not call checkstack.
|
// not call checkstack.
|
||||||
pub unsafe fn protect_lua_call<F, R>(state: *mut ffi::lua_State, nargs: c_int, nresults: c_int, f: F) -> Result<R>
|
pub unsafe fn protect_lua_call<F, R>(
|
||||||
where F: FnMut(*mut ffi::lua_State) -> R,
|
state: *mut ffi::lua_State,
|
||||||
|
nargs: c_int,
|
||||||
|
nresults: c_int,
|
||||||
|
f: F,
|
||||||
|
) -> Result<R>
|
||||||
|
where
|
||||||
|
F: FnMut(*mut ffi::lua_State) -> R,
|
||||||
{
|
{
|
||||||
struct Params<F, R> {
|
struct Params<F, R> {
|
||||||
function: F,
|
function: F,
|
||||||
|
@ -107,7 +113,8 @@ pub unsafe fn protect_lua_call<F, R>(state: *mut ffi::lua_State, nargs: c_int, n
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int
|
unsafe extern "C" fn do_call<F, R>(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<F, R>;
|
let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>;
|
||||||
ffi::lua_pop(state, 1);
|
ffi::lua_pop(state, 1);
|
||||||
|
@ -144,6 +151,21 @@ pub unsafe fn protect_lua_call<F, R>(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, F: FnOnce() -> 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
|
// 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
|
// 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.
|
// 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")
|
lua_panic!(state, "internal error: panic was resumed twice")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let err_string = if ffi::lua_type(state, -1) == ffi::LUA_TSTRING {
|
let err_string = gc_guard(state, || {
|
||||||
// lua_tostring only throws memory errors when the type is not already a string
|
if let Some(s) = ffi::lua_tostring(state, -1).as_ref() {
|
||||||
CStr::from_ptr(ffi::lua_tostring(state, -1))
|
CStr::from_ptr(s).to_string_lossy().into_owned()
|
||||||
.to_string_lossy()
|
|
||||||
.into_owned()
|
|
||||||
} else {
|
} else {
|
||||||
"<non-string error>".to_owned()
|
"<unprintable error>".to_owned()
|
||||||
};
|
}
|
||||||
|
});
|
||||||
|
ffi::lua_pop(state, 1);
|
||||||
|
|
||||||
match err_code {
|
match err_code {
|
||||||
ffi::LUA_ERRRUN => Error::RuntimeError(err_string),
|
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 {
|
pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
|
||||||
let top = ffi::lua_gettop(state);
|
let top = ffi::lua_gettop(state);
|
||||||
if top == 0 {
|
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);
|
ffi::lua_error(state);
|
||||||
} else if ffi::lua_pcall(state, top - 1, ffi::LUA_MULTRET, 0) != ffi::LUA_OK {
|
} else if ffi::lua_pcall(state, top - 1, ffi::LUA_MULTRET, 0) != ffi::LUA_OK {
|
||||||
if is_wrapped_panic(state, -1) {
|
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);
|
let top = ffi::lua_gettop(state);
|
||||||
if top < 2 {
|
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);
|
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.
|
// 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 {
|
pub unsafe extern "C" fn safe_setmetatable(state: *mut ffi::lua_State) -> c_int {
|
||||||
if ffi::lua_gettop(state) < 2 {
|
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);
|
ffi::lua_error(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapping the __gc method in setmetatable ONLY works because Lua 5.3 only honors the __gc
|
// 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.
|
// 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 {
|
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 {
|
unsafe extern "C" fn safe_gc(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
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);
|
ffi::lua_pushcclosure(state, safe_gc, 1);
|
||||||
push_string(state, "__gc");
|
ffi::lua_pushstring(state, cstr!("__gc"));
|
||||||
ffi::lua_insert(state, -2);
|
ffi::lua_insert(state, -2);
|
||||||
ffi::lua_rawset(state, -3);
|
ffi::lua_rawset(state, -3);
|
||||||
} else {
|
} 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) {
|
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) {
|
||||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||||
|
|
||||||
push_userdata(state, WrappedError(err));
|
gc_guard(state, || {
|
||||||
|
let ud = ffi::lua_newuserdata(state, mem::size_of::<Option<WrappedError>>())
|
||||||
|
as *mut Option<WrappedError>;
|
||||||
|
ptr::write(ud, Some(WrappedError(err)))
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
get_error_metatable(state);
|
get_error_metatable(state);
|
||||||
ffi::lua_setmetatable(state, -2);
|
ffi::lua_setmetatable(state, -2);
|
||||||
|
@ -417,7 +444,11 @@ struct WrappedPanic(pub Option<Box<Any + Send>>);
|
||||||
unsafe fn push_wrapped_panic(state: *mut ffi::lua_State, panic: Box<Any + Send>) {
|
unsafe fn push_wrapped_panic(state: *mut ffi::lua_State, panic: Box<Any + Send>) {
|
||||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
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::<Option<WrappedPanic>>())
|
||||||
|
as *mut Option<WrappedPanic>;
|
||||||
|
ptr::write(ud, Some(WrappedPanic(Some(panic))))
|
||||||
|
});
|
||||||
|
|
||||||
get_panic_metatable(state);
|
get_panic_metatable(state);
|
||||||
ffi::lua_setmetatable(state, -2);
|
ffi::lua_setmetatable(state, -2);
|
||||||
|
@ -480,7 +511,14 @@ unsafe fn get_error_metatable(state: *mut ffi::lua_State) -> c_int {
|
||||||
callback_error(state, || {
|
callback_error(state, || {
|
||||||
if is_wrapped_error(state, -1) {
|
if is_wrapped_error(state, -1) {
|
||||||
let error = get_userdata::<WrappedError>(state, -1);
|
let error = get_userdata::<WrappedError>(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);
|
ffi::lua_remove(state, -2);
|
||||||
|
|
||||||
Ok(1)
|
Ok(1)
|
||||||
|
@ -501,6 +539,7 @@ unsafe fn get_error_metatable(state: *mut ffi::lua_State) -> c_int {
|
||||||
|
|
||||||
ffi::luaL_checkstack(state, 8, ptr::null());
|
ffi::luaL_checkstack(state, 8, ptr::null());
|
||||||
|
|
||||||
|
gc_guard(state, || {
|
||||||
ffi::lua_newtable(state);
|
ffi::lua_newtable(state);
|
||||||
ffi::lua_pushlightuserdata(
|
ffi::lua_pushlightuserdata(
|
||||||
state,
|
state,
|
||||||
|
@ -508,19 +547,20 @@ unsafe fn get_error_metatable(state: *mut ffi::lua_State) -> c_int {
|
||||||
);
|
);
|
||||||
ffi::lua_pushvalue(state, -2);
|
ffi::lua_pushvalue(state, -2);
|
||||||
|
|
||||||
push_string(state, "__gc");
|
ffi::lua_pushstring(state, cstr!("__gc"));
|
||||||
ffi::lua_pushcfunction(state, userdata_destructor::<WrappedError>);
|
ffi::lua_pushcfunction(state, userdata_destructor::<WrappedError>);
|
||||||
ffi::lua_settable(state, -3);
|
ffi::lua_settable(state, -3);
|
||||||
|
|
||||||
push_string(state, "__tostring");
|
ffi::lua_pushstring(state, cstr!("__tostring"));
|
||||||
ffi::lua_pushcfunction(state, error_tostring);
|
ffi::lua_pushcfunction(state, error_tostring);
|
||||||
ffi::lua_settable(state, -3);
|
ffi::lua_settable(state, -3);
|
||||||
|
|
||||||
push_string(state, "__metatable");
|
ffi::lua_pushstring(state, cstr!("__metatable"));
|
||||||
ffi::lua_pushboolean(state, 0);
|
ffi::lua_pushboolean(state, 0);
|
||||||
ffi::lua_settable(state, -3);
|
ffi::lua_settable(state, -3);
|
||||||
|
|
||||||
ffi::lua_settable(state, ffi::LUA_REGISTRYINDEX);
|
ffi::lua_settable(state, ffi::LUA_REGISTRYINDEX);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi::LUA_TTABLE
|
ffi::LUA_TTABLE
|
||||||
|
@ -540,6 +580,7 @@ unsafe fn get_panic_metatable(state: *mut ffi::lua_State) -> c_int {
|
||||||
|
|
||||||
ffi::luaL_checkstack(state, 8, ptr::null());
|
ffi::luaL_checkstack(state, 8, ptr::null());
|
||||||
|
|
||||||
|
gc_guard(state, || {
|
||||||
ffi::lua_newtable(state);
|
ffi::lua_newtable(state);
|
||||||
ffi::lua_pushlightuserdata(
|
ffi::lua_pushlightuserdata(
|
||||||
state,
|
state,
|
||||||
|
@ -556,6 +597,7 @@ unsafe fn get_panic_metatable(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::lua_settable(state, -3);
|
ffi::lua_settable(state, -3);
|
||||||
|
|
||||||
ffi::lua_settable(state, ffi::LUA_REGISTRYINDEX);
|
ffi::lua_settable(state, ffi::LUA_REGISTRYINDEX);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi::LUA_TTABLE
|
ffi::LUA_TTABLE
|
||||||
|
|
Loading…
Reference in New Issue