diff --git a/Cargo.toml b/Cargo.toml index edbb1ef..9e4d961 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,12 @@ default = ["builtin-lua"] # * LUA_NUMBER as double # * LUA_EXTRASPACE is sizeof(void*) builtin-lua = ["gcc"] +# Don't define LUA_USE_APICHECK if we are in release mode. When +# debug_assertions are enabled, LUA_USE_APICHECK is enabled regardless. There +# are still a few known ways to trigger LUA_USE_APICHECK checks with rlua, but +# when these bugs are fixed, this option will go away and this behavior will be +# the default. +disable-lua-apicheck = [] [dependencies] libc = { version = "0.2" } diff --git a/build.rs b/build.rs index a1028f7..7e95a14 100644 --- a/build.rs +++ b/build.rs @@ -21,10 +21,9 @@ fn main() { config.define("LUA_USE_WINDOWS", None); } - // Enables lua api checking, which has a slight performance penalty. We - // could allow disabling this via cfg one day when there is much more - // confidence in the soundness of the API. - config.define("LUA_USE_APICHECK", None); + if cfg!(debug_assertions) || !cfg!(feature = "disable-lua-apicheck") { + config.define("LUA_USE_APICHECK", None); + } config .include("lua") diff --git a/src/util.rs b/src/util.rs index 9789d02..d8345db 100644 --- a/src/util.rs +++ b/src/util.rs @@ -24,25 +24,29 @@ pub unsafe fn stack_guard(state: *mut ffi::lua_State, change: c_int, op: F where F: FnOnce() -> R, { - let expected = ffi::lua_gettop(state) + change; - lua_internal_assert!( - state, - expected >= 0, - "too many stack values would be popped" - ); + if cfg!(debug_assertions) { + let expected = ffi::lua_gettop(state) + change; + lua_internal_assert!( + state, + expected >= 0, + "too many stack values would be popped" + ); - let res = op(); + let res = op(); - let top = ffi::lua_gettop(state); - lua_internal_assert!( - state, - ffi::lua_gettop(state) == expected, - "expected stack to be {}, got {}", - expected, - top - ); + let top = ffi::lua_gettop(state); + lua_internal_assert!( + state, + ffi::lua_gettop(state) == expected, + "expected stack to be {}, got {}", + expected, + top + ); - res + res + } else { + op() + } } // Run an operation on a lua_State and automatically clean up the stack before @@ -58,37 +62,45 @@ pub unsafe fn stack_err_guard(state: *mut ffi::lua_State, change: c_int, o where F: FnOnce() -> Result, { - let expected = ffi::lua_gettop(state) + change; - lua_internal_assert!( - state, - expected >= 0, - "too many stack values would be popped" - ); - - let res = op(); - - let top = ffi::lua_gettop(state); - if res.is_ok() { + if cfg!(debug_assertions) { + let expected = ffi::lua_gettop(state) + change; lua_internal_assert!( state, - ffi::lua_gettop(state) == expected, - "expected stack to be {}, got {}", - expected, - top + expected >= 0, + "too many stack values would be popped" ); - } else { - lua_internal_assert!( - state, - top >= expected, - "{} too many stack values popped", - top - expected - ); - if top > expected { - ffi::lua_settop(state, expected); + + let res = op(); + + let top = ffi::lua_gettop(state); + if res.is_ok() { + lua_internal_assert!( + state, + ffi::lua_gettop(state) == expected, + "expected stack to be {}, got {}", + expected, + top + ); + } else { + lua_internal_assert!( + state, + top >= expected, + "{} too many stack values popped", + top - expected + ); + if top > expected { + ffi::lua_settop(state, expected); + } } + res + } else { + let prev = ffi::lua_gettop(state) + change; + let res = op(); + if res.is_err() { + ffi::lua_settop(state, prev); + } + res } - - res } // Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.