Some changes for panic correctness, stack usage correctness, and speed
This commit is contained in:
parent
c4b3170e2b
commit
c22aae461b
|
@ -32,5 +32,4 @@ compiletest_rs = { version = "0.3", optional = true }
|
||||||
gcc = { version = "0.3.52", optional = true }
|
gcc = { version = "0.3.52", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rustyline = "1.0.0"
|
rustyline = "1.0.0"
|
||||||
|
|
|
@ -80,7 +80,7 @@ impl<'lua> Function<'lua> {
|
||||||
}
|
}
|
||||||
let nresults = ffi::lua_gettop(lua.state) - stack_start;
|
let nresults = ffi::lua_gettop(lua.state) - stack_start;
|
||||||
let mut results = MultiValue::new();
|
let mut results = MultiValue::new();
|
||||||
check_stack(lua.state, 1);
|
check_stack(lua.state, 2);
|
||||||
for _ in 0..nresults {
|
for _ in 0..nresults {
|
||||||
results.push_front(lua.pop_value(lua.state));
|
results.push_front(lua.pop_value(lua.state));
|
||||||
}
|
}
|
||||||
|
|
13
src/lua.rs
13
src/lua.rs
|
@ -565,7 +565,7 @@ impl Lua {
|
||||||
}
|
}
|
||||||
|
|
||||||
stack_err_guard(self.state, 0, || {
|
stack_err_guard(self.state, 0, || {
|
||||||
check_stack(self.state, 1);
|
check_stack(self.state, 2);
|
||||||
ffi::lua_rawgeti(
|
ffi::lua_rawgeti(
|
||||||
self.state,
|
self.state,
|
||||||
ffi::LUA_REGISTRYINDEX,
|
ffi::LUA_REGISTRYINDEX,
|
||||||
|
@ -672,7 +672,7 @@ impl Lua {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses 1 stack space, does not call checkstack
|
// Uses 2 stack spaces, does not call checkstack
|
||||||
pub(crate) unsafe fn pop_value(&self, state: *mut ffi::lua_State) -> Value {
|
pub(crate) unsafe fn pop_value(&self, state: *mut ffi::lua_State) -> Value {
|
||||||
match ffi::lua_type(state, -1) {
|
match ffi::lua_type(state, -1) {
|
||||||
ffi::LUA_TNIL => {
|
ffi::LUA_TNIL => {
|
||||||
|
@ -709,10 +709,9 @@ impl Lua {
|
||||||
ffi::LUA_TFUNCTION => Value::Function(Function(self.pop_ref(state))),
|
ffi::LUA_TFUNCTION => Value::Function(Function(self.pop_ref(state))),
|
||||||
|
|
||||||
ffi::LUA_TUSERDATA => {
|
ffi::LUA_TUSERDATA => {
|
||||||
// It should not be possible to interact with userdata types
|
// It should not be possible to interact with userdata types other than custom
|
||||||
// other than custom UserData types OR a WrappedError.
|
// UserData types OR a WrappedError. WrappedPanic should never be able to be caught
|
||||||
// WrappedPanic should never be able to be caught in lua, so it
|
// in lua, so it should never be here.
|
||||||
// should never be here.
|
|
||||||
if let Some(err) = pop_wrapped_error(state) {
|
if let Some(err) = pop_wrapped_error(state) {
|
||||||
Value::Error(err)
|
Value::Error(err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1011,7 +1010,7 @@ impl Lua {
|
||||||
|
|
||||||
let nargs = ffi::lua_gettop(state);
|
let nargs = ffi::lua_gettop(state);
|
||||||
let mut args = MultiValue::new();
|
let mut args = MultiValue::new();
|
||||||
check_stack(state, 1);
|
check_stack(state, 2);
|
||||||
for _ in 0..nargs {
|
for _ in 0..nargs {
|
||||||
args.push_front(lua.pop_value(state));
|
args.push_front(lua.pop_value(state));
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,11 @@ impl<'lua> String<'lua> {
|
||||||
stack_guard(lua.state, 0, || {
|
stack_guard(lua.state, 0, || {
|
||||||
check_stack(lua.state, 1);
|
check_stack(lua.state, 1);
|
||||||
lua.push_ref(lua.state, &self.0);
|
lua.push_ref(lua.state, &self.0);
|
||||||
assert_eq!(ffi::lua_type(lua.state, -1), ffi::LUA_TSTRING);
|
lua_internal_assert!(
|
||||||
|
lua.state,
|
||||||
|
ffi::lua_type(lua.state, -1) == ffi::LUA_TSTRING,
|
||||||
|
"string ref is not string type"
|
||||||
|
);
|
||||||
|
|
||||||
let mut size = 0;
|
let mut size = 0;
|
||||||
// This will not trigger a 'm' error, because the reference is guaranteed to be of
|
// This will not trigger a 'm' error, because the reference is guaranteed to be of
|
||||||
|
|
|
@ -347,7 +347,7 @@ where
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_guard(lua.state, 0, || {
|
stack_guard(lua.state, 0, || {
|
||||||
check_stack(lua.state, 5);
|
check_stack(lua.state, 6);
|
||||||
|
|
||||||
lua.push_ref(lua.state, &self.table);
|
lua.push_ref(lua.state, &self.table);
|
||||||
lua.push_ref(lua.state, &next_key);
|
lua.push_ref(lua.state, &next_key);
|
||||||
|
@ -409,7 +409,7 @@ where
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_guard(lua.state, 0, || {
|
stack_guard(lua.state, 0, || {
|
||||||
check_stack(lua.state, 4);
|
check_stack(lua.state, 5);
|
||||||
|
|
||||||
lua.push_ref(lua.state, &self.table);
|
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))
|
||||||
|
|
|
@ -107,7 +107,7 @@ impl<'lua> Thread<'lua> {
|
||||||
|
|
||||||
let nresults = ffi::lua_gettop(thread_state);
|
let nresults = ffi::lua_gettop(thread_state);
|
||||||
let mut results = MultiValue::new();
|
let mut results = MultiValue::new();
|
||||||
check_stack(thread_state, 1);
|
check_stack(thread_state, 2);
|
||||||
for _ in 0..nresults {
|
for _ in 0..nresults {
|
||||||
results.push_front(lua.pop_value(thread_state));
|
results.push_front(lua.pop_value(thread_state));
|
||||||
}
|
}
|
||||||
|
|
|
@ -470,7 +470,7 @@ impl<'lua> AnyUserData<'lua> {
|
||||||
let lua = self.0.lua;
|
let lua = self.0.lua;
|
||||||
unsafe {
|
unsafe {
|
||||||
stack_err_guard(lua.state, 0, || {
|
stack_err_guard(lua.state, 0, || {
|
||||||
check_stack(lua.state, 2);
|
check_stack(lua.state, 3);
|
||||||
lua.push_ref(lua.state, &self.0);
|
lua.push_ref(lua.state, &self.0);
|
||||||
ffi::lua_getuservalue(lua.state, -1);
|
ffi::lua_getuservalue(lua.state, -1);
|
||||||
let res = V::from_lua(lua.pop_value(lua.state), lua)?;
|
let res = V::from_lua(lua.pop_value(lua.state), lua)?;
|
||||||
|
|
38
src/util.rs
38
src/util.rs
|
@ -179,6 +179,7 @@ where
|
||||||
// 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.
|
||||||
|
// Uses 2 stack spaces, does not call lua_checkstack.
|
||||||
pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
|
||||||
lua_internal_assert!(
|
lua_internal_assert!(
|
||||||
state,
|
state,
|
||||||
|
@ -334,6 +335,8 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||||
|
|
||||||
// A variant of pcall that does not allow lua to catch panic errors from callback_error
|
// A variant of pcall that does not allow lua to catch panic errors from callback_error
|
||||||
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 {
|
||||||
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||||
|
|
||||||
let top = ffi::lua_gettop(state);
|
let top = ffi::lua_gettop(state);
|
||||||
if top == 0 {
|
if top == 0 {
|
||||||
ffi::lua_pushstring(state, cstr!("not enough arguments to pcall"));
|
ffi::lua_pushstring(state, cstr!("not enough arguments to pcall"));
|
||||||
|
@ -355,6 +358,8 @@ pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
|
||||||
// A variant of xpcall that does not allow lua to catch panic errors from callback_error
|
// A variant of xpcall that does not allow lua to catch panic errors from callback_error
|
||||||
pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
|
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 {
|
unsafe extern "C" fn xpcall_msgh(state: *mut ffi::lua_State) -> c_int {
|
||||||
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||||
|
|
||||||
if is_wrapped_panic(state, -1) {
|
if is_wrapped_panic(state, -1) {
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
|
@ -365,6 +370,8 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||||
|
|
||||||
let top = ffi::lua_gettop(state);
|
let top = ffi::lua_gettop(state);
|
||||||
if top < 2 {
|
if top < 2 {
|
||||||
ffi::lua_pushstring(state, cstr!("not enough arguments to xpcall"));
|
ffi::lua_pushstring(state, cstr!("not enough arguments to xpcall"));
|
||||||
|
@ -411,10 +418,11 @@ pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) {
|
||||||
ffi::lua_setmetatable(state, -2);
|
ffi::lua_setmetatable(state, -2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pops a WrappedError off of the top of the stack, if it is a WrappedError. If
|
// Pops a WrappedError off of the top of the stack, if it is a WrappedError. If it is not a
|
||||||
// it is not a WrappedError, returns None and does not pop anything.
|
// WrappedError, returns None and does not pop anything. Uses 2 stack spaces and does not call
|
||||||
|
// lua_checkstack
|
||||||
pub unsafe fn pop_wrapped_error(state: *mut ffi::lua_State) -> Option<Error> {
|
pub unsafe fn pop_wrapped_error(state: *mut ffi::lua_State) -> Option<Error> {
|
||||||
if ffi::lua_gettop(state) == 0 || !is_wrapped_error(state, -1) {
|
if !is_wrapped_error(state, -1) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let err = &*get_userdata::<WrappedError>(state, -1);
|
let err = &*get_userdata::<WrappedError>(state, -1);
|
||||||
|
@ -456,16 +464,9 @@ unsafe fn push_wrapped_panic(state: *mut ffi::lua_State, panic: Box<Any + Send>)
|
||||||
ffi::lua_setmetatable(state, -2);
|
ffi::lua_setmetatable(state, -2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the value at the given index is a WrappedError
|
// Checks if the value at the given index is a WrappedError, uses 2 stack spaces and does not call
|
||||||
|
// lua_checkstack.
|
||||||
unsafe fn is_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> bool {
|
unsafe fn is_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> bool {
|
||||||
assert_ne!(
|
|
||||||
ffi::lua_checkstack(state, 2),
|
|
||||||
0,
|
|
||||||
"somehow not enough stack space to check if a value is a WrappedError"
|
|
||||||
);
|
|
||||||
|
|
||||||
let index = ffi::lua_absindex(state, index);
|
|
||||||
|
|
||||||
let userdata = ffi::lua_touserdata(state, index);
|
let userdata = ffi::lua_touserdata(state, index);
|
||||||
if userdata.is_null() {
|
if userdata.is_null() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -481,16 +482,9 @@ unsafe fn is_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> bool {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the value at the given index is a WrappedPanic
|
// Checks if the value at the given index is a WrappedPanic. Uses 2 stack spaces and does not call
|
||||||
|
// lua_checkstack.
|
||||||
unsafe fn is_wrapped_panic(state: *mut ffi::lua_State, index: c_int) -> bool {
|
unsafe fn is_wrapped_panic(state: *mut ffi::lua_State, index: c_int) -> bool {
|
||||||
assert_ne!(
|
|
||||||
ffi::lua_checkstack(state, 2),
|
|
||||||
0,
|
|
||||||
"somehow not enough stack space to check if a value is a wrapped panic"
|
|
||||||
);
|
|
||||||
|
|
||||||
let index = ffi::lua_absindex(state, index);
|
|
||||||
|
|
||||||
let userdata = ffi::lua_touserdata(state, index);
|
let userdata = ffi::lua_touserdata(state, index);
|
||||||
if userdata.is_null() {
|
if userdata.is_null() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -510,6 +504,8 @@ unsafe fn get_error_metatable(state: *mut ffi::lua_State) -> c_int {
|
||||||
static ERROR_METATABLE_REGISTRY_KEY: u8 = 0;
|
static ERROR_METATABLE_REGISTRY_KEY: u8 = 0;
|
||||||
|
|
||||||
unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
|
||||||
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||||
|
|
||||||
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);
|
||||||
|
|
Loading…
Reference in New Issue