Stack assertions review

Other minor code and documentation updates
This commit is contained in:
Alex Orlenko 2021-05-02 10:38:33 +01:00
parent 463fc646bc
commit 3f55958bdd
15 changed files with 345 additions and 319 deletions

View File

@ -29,11 +29,13 @@ extern "C" {
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;
@ -118,21 +120,25 @@ pub unsafe fn lua_newuserdata(state: *mut lua_State, size: usize) -> Result<*mut
Ok(super::lua_touserdata(state, -1))
}
// Uses 4 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);
super::lua_pushinteger(state, n as lua_Integer);
protect_lua(state, n + 2, lua_pushcclosure_s)
// 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 4 stack spaces
// 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);
}
super::lua_pushinteger(state, n as lua_Integer + 1);
protect_lua(state, n + 2, lua_pushrclosure_s)
protect_lua(state, n + 1, lua_pushrclosure_s)
}
// Uses 5 stack spaces
@ -163,6 +169,11 @@ pub unsafe fn error_traceback2(state: *mut lua_State, state2: *mut lua_State) ->
// 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);

View File

@ -135,16 +135,21 @@ int lua_newuserdata_s(lua_State *L) {
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) {
lua_CFunction fn = lua_touserdata(L, -2);
lua_Integer n = lua_tointeger(L, -1);
lua_pop(L, 2);
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) {
lua_Integer n = lua_popinteger(L);
int n = lua_gettop(L);
lua_pushcclosure(L, lua_call_rust, n);
return 1;
}
@ -162,6 +167,11 @@ int luaL_requiref_s(lua_State *L) {
// 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);
@ -344,7 +354,7 @@ int meta_newindex_impl(lua_State *state) {
return 0;
}
// See function::bind
// See Function::bind
int bind_call_impl(lua_State *state) {
int nargs = lua_gettop(state);
int nbinds = lua_tointeger(state, lua_upvalueindex(2));

View File

@ -207,13 +207,9 @@ impl<'lua> Function<'lua> {
assert_stack(lua.state, 1);
lua.push_ref(&self.0);
let data_ptr = &mut data as *mut Vec<u8> as *mut c_void;
let strip = if strip { 1 } else { 0 };
ffi::lua_dump(
lua.state,
writer,
&mut data as *mut Vec<u8> as *mut c_void,
strip,
);
ffi::lua_dump(lua.state, writer, data_ptr, strip);
ffi::lua_pop(lua.state, 1);
}

View File

@ -84,9 +84,9 @@ struct MemoryInfo {
/// In Lua 5.4 GC can work in two modes: incremental and generational.
/// Previous Lua versions support only incremental GC.
///
/// More information can be found in the Lua 5.x [documentation][lua_doc].
/// More information can be found in the Lua 5.x [documentation].
///
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#2.5
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5
pub enum GCMode {
Incremental,
/// Requires `feature = "lua54"`
@ -128,11 +128,15 @@ impl Drop for Lua {
}
impl Lua {
/// Creates a new Lua state and loads the safe subset of the standard libraries.
/// Creates a new Lua state and loads the **safe** subset of the standard libraries.
///
/// # Safety
/// The created Lua state would have _some_ safety guarantees and would not allow to load unsafe
/// standard libraries or C modules.
///
/// See [`StdLib`] documentation for a list of unsafe modules that cannot be loaded.
///
/// [`StdLib`]: struct.StdLib.html
#[allow(clippy::new_without_default)]
pub fn new() -> Lua {
mlua_expect!(
@ -157,6 +161,8 @@ impl Lua {
/// The created Lua state would have _some_ safety guarantees and would not allow to load unsafe
/// standard libraries or C modules.
///
/// See [`StdLib`] documentation for a list of unsafe modules that cannot be loaded.
///
/// [`StdLib`]: struct.StdLib.html
pub fn new_with(libs: StdLib) -> Result<Lua> {
if libs.contains(StdLib::DEBUG) {
@ -188,7 +194,7 @@ impl Lua {
/// Use the [`StdLib`] flags to specifiy the libraries you want to load.
///
/// # Safety
/// The created Lua state would not have safety guarantees and would allow to load C modules.
/// The created Lua state will not have safety guarantees and allow to load C modules.
///
/// [`StdLib`]: struct.StdLib.html
pub unsafe fn unsafe_new_with(libs: StdLib) -> Lua {
@ -289,10 +295,18 @@ impl Lua {
let main_state = maybe_main_state.unwrap_or(state);
let main_state_top = ffi::lua_gettop(main_state);
let (ref_thread, wrapped_error_key, wrapped_panic_key) = mlua_expect!(
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::<WrappedError>();
ffi::safe::WRAPPED_PANIC_SIZE = mem::size_of::<WrappedPanic>();
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;
// Create the internal metatables and place them in the registry
// to prevent them from being garbage collected.
@ -317,7 +331,7 @@ impl Lua {
let ref_thread = ffi::safe::lua_newthread(state)?;
ffi::safe::luaL_ref(state, ffi::LUA_REGISTRYINDEX)?;
Ok::<_, Error>((ref_thread, wrapped_error_key, wrapped_panic_key))
Ok::<_, Error>(ref_thread)
})(main_state),
"Error during Lua construction",
);
@ -342,12 +356,9 @@ impl Lua {
push_gc_userdata(main_state, Arc::downgrade(&extra)),
"Error while storing extra data",
);
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_REGISTRY_KEY as *const u8 as *const c_void
),
ffi::safe::lua_rawsetp(main_state, ffi::LUA_REGISTRYINDEX, extra_key,),
"Error while storing extra data"
);
@ -357,11 +368,6 @@ impl Lua {
);
assert_stack(main_state, ffi::LUA_MINSTACK);
ffi::safe::WRAPPED_ERROR_SIZE = mem::size_of::<WrappedError>();
ffi::safe::WRAPPED_PANIC_SIZE = mem::size_of::<WrappedPanic>();
ffi::safe::WRAPPED_ERROR_KEY = wrapped_error_key as *const c_void;
ffi::safe::WRAPPED_PANIC_KEY = wrapped_panic_key as *const c_void;
Lua {
state,
main_state: maybe_main_state,
@ -642,15 +648,8 @@ impl Lua {
}
#[cfg(feature = "lua54")]
let prev_mode = unsafe {
ffi::lua_gc(
state,
ffi::LUA_GCSETPAUSE,
pause,
step_multiplier,
step_size,
)
};
let prev_mode =
unsafe { ffi::lua_gc(state, ffi::LUA_GCINC, pause, step_multiplier, step_size) };
#[cfg(feature = "lua54")]
match prev_mode {
ffi::LUA_GCINC => GCMode::Incremental,
@ -691,7 +690,7 @@ impl Lua {
/// [`Chunk::exec`]: struct.Chunk.html#method.exec
pub fn load<'lua, 'a, S>(&'lua self, source: &'a S) -> Chunk<'lua, 'a>
where
S: ?Sized + AsRef<[u8]>,
S: AsRef<[u8]> + ?Sized,
{
Chunk {
lua: self,
@ -711,7 +710,7 @@ impl Lua {
) -> Result<Function<'lua>> {
unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 1);
check_stack(self.state, 1)?;
let mode_str = match mode {
Some(ChunkMode::Binary) if self.safe => {
@ -756,11 +755,11 @@ impl Lua {
/// here.
pub fn create_string<S>(&self, s: &S) -> Result<String>
where
S: ?Sized + AsRef<[u8]>,
S: AsRef<[u8]> + ?Sized,
{
unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 4);
check_stack(self.state, 3)?;
ffi::safe::lua_pushstring(self.state, s)?;
Ok(String(self.pop_ref()))
}
@ -768,7 +767,12 @@ impl Lua {
/// Creates and returns a new empty table.
pub fn create_table(&self) -> Result<Table> {
self.create_table_with_capacity(0, 0)
unsafe {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 2)?;
ffi::safe::lua_newtable(self.state)?;
Ok(Table(self.pop_ref()))
}
}
/// Creates and returns a new empty table, with the specified capacity.
@ -778,14 +782,14 @@ impl Lua {
pub fn create_table_with_capacity(&self, narr: c_int, nrec: c_int) -> Result<Table> {
unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 4);
check_stack(self.state, 4)?;
ffi::safe::lua_createtable(self.state, narr, nrec)?;
Ok(Table(self.pop_ref()))
}
}
/// Creates a table and fills it with values from an iterator.
pub fn create_table_from<'lua, K, V, I>(&'lua self, cont: I) -> Result<Table<'lua>>
pub fn create_table_from<'lua, K, V, I>(&'lua self, iter: I) -> Result<Table<'lua>>
where
K: ToLua<'lua>,
V: ToLua<'lua>,
@ -793,12 +797,12 @@ impl Lua {
{
unsafe {
let _sg = StackGuard::new(self.state);
// `Lua` instance assumes that on any callback, the Lua stack has at least LUA_MINSTACK
// slots available to avoid panics.
check_stack(self.state, 5 + ffi::LUA_MINSTACK)?;
check_stack(self.state, 6)?;
ffi::safe::lua_createtable(self.state, 0, 0)?;
for (k, v) in cont {
let iter = iter.into_iter();
let lower_bound = iter.size_hint().0;
ffi::safe::lua_createtable(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)?;
@ -809,12 +813,25 @@ impl Lua {
}
/// Creates a table from an iterator of values, using `1..` as the keys.
pub fn create_sequence_from<'lua, T, I>(&'lua self, cont: I) -> Result<Table<'lua>>
pub fn create_sequence_from<'lua, T, I>(&'lua self, iter: I) -> Result<Table<'lua>>
where
T: ToLua<'lua>,
I: IntoIterator<Item = T>,
{
self.create_table_from(cont.into_iter().enumerate().map(|(k, v)| (k + 1, v)))
unsafe {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 6)?;
let iter = iter.into_iter();
let lower_bound = iter.size_hint().0;
ffi::safe::lua_createtable(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)?;
}
Ok(Table(self.pop_ref()))
}
}
/// Wraps a Rust function or closure, creating a callable Lua function handle to it.
@ -966,7 +983,7 @@ impl Lua {
pub fn create_thread<'lua>(&'lua self, func: Function<'lua>) -> Result<Thread<'lua>> {
unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2);
check_stack(self.state, 2)?;
let thread_state = ffi::safe::lua_newthread(self.state)?;
self.push_ref(&func.0);
@ -1000,7 +1017,7 @@ impl Lua {
pub fn globals(&self) -> Table {
unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2);
assert_stack(self.state, 1);
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
#[cfg(any(feature = "lua51", feature = "luajit"))]
@ -1013,6 +1030,8 @@ impl Lua {
/// for parameters given to a callback, this will be whatever Lua thread called the callback.
pub fn current_thread(&self) -> Thread {
unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 1);
ffi::lua_pushthread(self.state);
Thread(self.pop_ref())
}
@ -1078,7 +1097,7 @@ impl Lua {
Value::String(s) => Some(s),
v => unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 4);
check_stack(self.state, 5)?;
self.push_value(v)?;
if !ffi::safe::lua_tolstring(self.state, -1, ptr::null_mut())?.is_null() {
@ -1101,7 +1120,7 @@ impl Lua {
Value::Integer(i) => Some(i),
v => unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2);
check_stack(self.state, 2)?;
self.push_value(v)?;
let mut isint = 0;
@ -1125,7 +1144,7 @@ impl Lua {
Value::Number(n) => Some(n),
v => unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2);
check_stack(self.state, 2)?;
self.push_value(v)?;
let mut isnum = 0;
@ -1168,13 +1187,13 @@ impl Lua {
/// state.
pub fn set_named_registry_value<'lua, S, T>(&'lua self, name: &S, t: T) -> Result<()>
where
S: ?Sized + AsRef<[u8]>,
S: AsRef<[u8]> + ?Sized,
T: ToLua<'lua>,
{
let t = t.to_lua(self)?;
unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 5);
check_stack(self.state, 5)?;
self.push_value(t)?;
ffi::safe::lua_rawsetfield(self.state, ffi::LUA_REGISTRYINDEX, name)
@ -1189,12 +1208,12 @@ impl Lua {
/// [`set_named_registry_value`]: #method.set_named_registry_value
pub fn named_registry_value<'lua, S, T>(&'lua self, name: &S) -> Result<T>
where
S: ?Sized + AsRef<[u8]>,
S: AsRef<[u8]> + ?Sized,
T: FromLua<'lua>,
{
let value = unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 4);
check_stack(self.state, 3)?;
ffi::safe::lua_pushstring(self.state, name)?;
ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX);
@ -1211,7 +1230,7 @@ impl Lua {
/// [`set_named_registry_value`]: #method.set_named_registry_value
pub fn unset_named_registry_value<S>(&self, name: &S) -> Result<()>
where
S: ?Sized + AsRef<[u8]>,
S: AsRef<[u8]> + ?Sized,
{
self.set_named_registry_value(name, Nil)
}
@ -1229,7 +1248,7 @@ impl Lua {
let t = t.to_lua(self)?;
unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2);
check_stack(self.state, 4)?;
self.push_value(t)?;
let registry_id = ffi::safe::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)?;
@ -1250,13 +1269,13 @@ impl Lua {
///
/// [`create_registry_value`]: #method.create_registry_value
pub fn registry_value<'lua, T: FromLua<'lua>>(&'lua self, key: &RegistryKey) -> Result<T> {
let value = unsafe {
if !self.owns_registry_value(key) {
return Err(Error::MismatchedRegistryKey);
}
let value = unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2);
check_stack(self.state, 1)?;
ffi::lua_rawgeti(
self.state,
@ -1278,14 +1297,13 @@ impl Lua {
/// [`create_registry_value`]: #method.create_registry_value
/// [`expire_registry_values`]: #method.expire_registry_values
pub fn remove_registry_value(&self, key: RegistryKey) -> Result<()> {
unsafe {
if !self.owns_registry_value(&key) {
return Err(Error::MismatchedRegistryKey);
}
unsafe {
ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, key.take());
Ok(())
}
Ok(())
}
/// Returns true if the given `RegistryKey` was created by a `Lua` which shares the underlying
@ -1477,17 +1495,18 @@ impl Lua {
}
}
pub(crate) unsafe fn userdata_metatable<T: 'static + UserData>(&self) -> Result<c_int> {
pub(crate) unsafe fn push_userdata_metatable<T: 'static + UserData>(&self) -> Result<()> {
let type_id = TypeId::of::<T>();
if let Some(table_id) = mlua_expect!(self.extra.lock(), "extra is poisoned")
if let Some(&table_id) = mlua_expect!(self.extra.lock(), "extra is poisoned")
.registered_userdata
.get(&type_id)
{
return Ok(*table_id);
ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, table_id as Integer);
return Ok(());
}
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 10);
let _sg = StackGuard::new_extra(self.state, 1);
check_stack(self.state, 13)?;
let mut fields = StaticUserDataFields::default();
let mut methods = StaticUserDataMethods::default();
@ -1565,13 +1584,14 @@ impl Lua {
ffi::lua_pop(self.state, extra_tables_count);
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 mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
extra.registered_userdata.insert(type_id, id);
extra.registered_userdata_mt.insert(ptr as isize);
Ok(id)
Ok(())
}
pub(crate) fn register_userdata_metatable(&self, id: isize) {
@ -1586,7 +1606,7 @@ impl Lua {
// Pushes a LuaRef value onto the stack, checking that it's a registered
// and not destructed UserData.
// Uses 3 stack spaces, does not call checkstack
// Uses 3 stack spaces, does not call checkstack.
pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<()> {
self.push_ref(lref);
if ffi::lua_getmetatable(self.state, -1) == 0 {
@ -1663,7 +1683,7 @@ impl Lua {
unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 6);
check_stack(self.state, 5)?;
push_gc_userdata::<Callback>(self.state, mem::transmute(func))?;
push_gc_userdata(self.state, self.clone())?;
@ -1744,8 +1764,8 @@ impl Lua {
let mut waker = noop_waker();
// Try to get an outer poll waker
ffi::lua_pushlightuserdata(state, &WAKER_REGISTRY_KEY as *const u8 as *mut c_void);
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key);
if let Some(w) = get_gc_userdata::<Waker>(state, -1).as_ref() {
waker = (*w).clone();
}
@ -1775,7 +1795,7 @@ impl Lua {
let get_poll = unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 6);
check_stack(self.state, 5)?;
push_gc_userdata::<AsyncCallback>(self.state, mem::transmute(func))?;
push_gc_userdata(self.state, self.clone())?;
@ -1786,7 +1806,7 @@ impl Lua {
let coroutine = self.globals().get::<_, Table>("coroutine")?;
let env = self.create_table()?;
let env = self.create_table_with_capacity(0, 4)?;
env.set("get_poll", get_poll)?;
env.set("yield", coroutine.get::<_, Function>("yield")?)?;
env.set(
@ -1800,7 +1820,7 @@ impl Lua {
)?;
env.set("pending", unsafe {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 5)?;
check_stack(self.state, 3)?;
push_gc_userdata(self.state, AsyncPollPending)?;
self.pop_value()
})?;
@ -1829,12 +1849,10 @@ impl Lua {
T: 'static + UserData,
{
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 4);
check_stack(self.state, 2)?;
let ud_index = self.userdata_metatable::<T>()?;
push_userdata(self.state, data)?;
ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ud_index as Integer);
self.push_userdata_metatable::<T>()?;
ffi::lua_setmetatable(self.state, -2);
Ok(AnyUserData(self.pop_ref()))
@ -1879,13 +1897,10 @@ impl Lua {
pub(crate) unsafe fn make_from_ptr(state: *mut ffi::lua_State) -> Self {
let _sg = StackGuard::new(state);
assert_stack(state, 3);
assert_stack(state, 1);
ffi::lua_rawgetp(
state,
ffi::LUA_REGISTRYINDEX,
&EXTRA_REGISTRY_KEY as *const u8 as *mut c_void,
);
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key);
let extra = mlua_expect!(
(*get_gc_userdata::<Weak<Mutex<ExtraData>>>(state, -1)).upgrade(),
"extra is destroyed"
@ -1929,7 +1944,7 @@ pub enum ChunkMode {
impl<'lua, 'a> Chunk<'lua, 'a> {
/// Sets the name of this chunk, which results in more informative error traces.
pub fn set_name<S: ?Sized + AsRef<[u8]>>(mut self, name: &S) -> Result<Chunk<'lua, 'a>> {
pub fn set_name<S: AsRef<[u8]> + ?Sized>(mut self, name: &S) -> Result<Chunk<'lua, 'a>> {
let name =
CString::new(name.as_ref().to_vec()).map_err(|e| Error::ToLuaConversionError {
from: "&str",

View File

@ -16,7 +16,8 @@ use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
};
use crate::util::{
assert_stack, get_userdata, init_userdata_metatable, push_userdata, take_userdata, StackGuard,
assert_stack, check_stack, get_userdata, init_userdata_metatable, push_userdata, take_userdata,
StackGuard,
};
use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
@ -188,6 +189,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let state = ud.lua.state;
let _sg = StackGuard::new(state);
assert_stack(state, 2);
ud.lua.push_ref(&ud);
// We know the destructor has not run yet because we hold a reference to the userdata.
@ -260,7 +262,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
if let Some(Value::UserData(ud)) = value {
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 3);
check_stack(lua.state, 3)?;
lua.push_userdata_ref(&ud.0)?;
if get_userdata(lua.state, -1) == data_ptr {
return Ok(());
@ -320,7 +322,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
unsafe {
let lua = self.lua;
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 13);
check_stack(lua.state, 13)?;
push_userdata(lua.state, data.clone())?;
let data_ptr = ffi::lua_touserdata(lua.state, -1);
@ -401,6 +403,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let state = ud.lua.state;
let _sg = StackGuard::new(state);
assert_stack(state, 2);
ud.lua.push_ref(&ud);
// We know the destructor has not run yet because we hold a reference to the userdata.
@ -450,6 +453,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let state = f.lua.state;
let _sg = StackGuard::new(state);
assert_stack(state, 3);
f.lua.push_ref(&f);
// We know the destructor has not run yet because we hold a reference to the callback.
@ -487,7 +491,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let destructor: DestructorCallback = Box::new(move |f| {
let state = f.lua.state;
let _sg = StackGuard::new(state);
assert_stack(state, 4);
assert_stack(state, 5);
f.lua.push_ref(&f);
// We know the destructor has not run yet because we hold a reference to the callback.

View File

@ -10,7 +10,7 @@ use crate::ffi;
use crate::lua::Lua;
use crate::table::Table;
use crate::types::LightUserData;
use crate::util::{assert_stack, StackGuard};
use crate::util::{assert_stack, check_stack, StackGuard};
use crate::value::Value;
/// Trait for serializing/deserializing Lua values using Serde.
@ -200,22 +200,22 @@ 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<()> {
ffi::lua_pushlightuserdata(
state,
&ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
);
check_stack(state, 6)?;
ffi::safe::lua_createtable(state, 0, 1)?;
ffi::lua_pushboolean(state, 0);
ffi::safe::lua_rawsetfield(state, -2, "__metatable")?;
ffi::safe::lua_rawset(state, ffi::LUA_REGISTRYINDEX)
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)
}
pub(crate) unsafe fn push_array_metatable(state: *mut ffi::lua_State) {
let key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key);
}
static ARRAY_METATABLE_REGISTRY_KEY: u8 = 0;

View File

@ -9,7 +9,7 @@ use crate::lua::Lua;
use crate::string::String;
use crate::table::Table;
use crate::types::Integer;
use crate::util::{assert_stack, StackGuard};
use crate::util::{check_stack, StackGuard};
use crate::value::{ToLua, Value};
/// A struct for serializing Rust values into Lua values.
@ -137,7 +137,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
#[inline]
fn serialize_some<T>(self, value: &T) -> Result<Value<'lua>>
where
T: ?Sized + Serialize,
T: Serialize + ?Sized,
{
value.serialize(self)
}
@ -173,7 +173,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
#[inline]
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value<'lua>>
where
T: ?Sized + Serialize,
T: Serialize + ?Sized,
{
value.serialize(self)
}
@ -187,7 +187,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
value: &T,
) -> Result<Value<'lua>>
where
T: ?Sized + Serialize,
T: Serialize + ?Sized,
{
let table = self.lua.create_table()?;
let variant = self.lua.create_string(variant)?;
@ -279,13 +279,13 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
let value = lua.to_value_with(value, self.options)?;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 5);
check_stack(lua.state, 6)?;
lua.push_ref(&self.table.0);
lua.push_value(value)?;
@ -305,7 +305,7 @@ impl<'lua> ser::SerializeTuple for SerializeVec<'lua> {
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
T: Serialize + ?Sized,
{
ser::SerializeSeq::serialize_element(self, value)
}
@ -321,7 +321,7 @@ impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
T: Serialize + ?Sized,
{
ser::SerializeSeq::serialize_element(self, value)
}
@ -344,7 +344,7 @@ impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
let idx = self.table.raw_len() + 1;
@ -373,7 +373,7 @@ impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
where
T: ?Sized + Serialize,
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
self.key = Some(lua.to_value_with(key, self.options)?);
@ -382,7 +382,7 @@ impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
let key = mlua_expect!(
@ -404,7 +404,7 @@ impl<'lua> ser::SerializeStruct for SerializeMap<'lua> {
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
T: Serialize + ?Sized,
{
ser::SerializeMap::serialize_key(self, key)?;
ser::SerializeMap::serialize_value(self, value)
@ -428,7 +428,7 @@ impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> {
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
self.table

View File

@ -39,17 +39,17 @@ impl StdLib {
#[cfg(any(feature = "luajit", doc))]
pub const JIT: StdLib = StdLib(1 << 9);
/// (unsafe) [`ffi`](http://luajit.org/ext_ffi.html) library
/// (**unsafe**) [`ffi`](http://luajit.org/ext_ffi.html) library
///
/// Requires `feature = "luajit"`
#[cfg(any(feature = "luajit", doc))]
pub const FFI: StdLib = StdLib(1 << 30);
/// (unsafe) [`debug`](https://www.lua.org/manual/5.3/manual.html#6.10) library
/// (**unsafe**) [`debug`](https://www.lua.org/manual/5.3/manual.html#6.10) library
pub const DEBUG: StdLib = StdLib(1 << 31);
/// No libraries
pub const NONE: StdLib = StdLib(0);
/// (unsafe) All standard libraries
/// (**unsafe**) All standard libraries
pub const ALL: StdLib = StdLib(u32::MAX);
/// The safe subset of the standard libraries
pub const ALL_SAFE: StdLib = StdLib((1 << 30) - 1);

View File

@ -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, StackGuard};
use crate::util::{assert_stack, check_stack, StackGuard};
use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
#[cfg(feature = "async")]
@ -62,7 +62,7 @@ impl<'lua> Table<'lua> {
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 5);
check_stack(lua.state, 6)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
@ -101,7 +101,7 @@ impl<'lua> Table<'lua> {
let value = unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 4);
check_stack(lua.state, 5)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
@ -119,7 +119,7 @@ impl<'lua> Table<'lua> {
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 4);
check_stack(lua.state, 5)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
@ -193,7 +193,7 @@ impl<'lua> Table<'lua> {
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 5);
check_stack(lua.state, 6)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
@ -209,7 +209,7 @@ impl<'lua> Table<'lua> {
let value = unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 2);
check_stack(lua.state, 3)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
@ -232,7 +232,7 @@ impl<'lua> Table<'lua> {
let value = value.to_lua(lua)?;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 5);
check_stack(lua.state, 6)?;
lua.push_ref(&self.0);
lua.push_value(value)?;
@ -258,7 +258,7 @@ impl<'lua> Table<'lua> {
}
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 4);
check_stack(lua.state, 5)?;
lua.push_ref(&self.0);
ffi::safe::lua_rawremove(lua.state, -1, idx)
@ -277,7 +277,7 @@ impl<'lua> Table<'lua> {
let lua = self.0.lua;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 4);
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
ffi::safe::luaL_len(lua.state, -1)
@ -370,7 +370,7 @@ impl<'lua> Table<'lua> {
pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> {
TablePairs {
table: self.0,
next_key: Some(Nil),
key: Some(Nil),
_phantom: PhantomData,
}
}
@ -635,7 +635,7 @@ impl<'lua> Serialize for Table<'lua> {
/// [`Table::pairs`]: struct.Table.html#method.pairs
pub struct TablePairs<'lua, K, V> {
table: LuaRef<'lua>,
next_key: Option<Value<'lua>>,
key: Option<Value<'lua>>,
_phantom: PhantomData<(K, V)>,
}
@ -647,38 +647,34 @@ where
type Item = Result<(K, V)>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(next_key) = self.next_key.take() {
if let Some(prev_key) = self.key.take() {
let lua = self.table.lua;
let res = (|| {
let res = unsafe {
let res = (|| unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 4);
check_stack(lua.state, 5)?;
lua.push_ref(&self.table);
lua.push_value(next_key)?;
lua.push_value(prev_key)?;
if ffi::safe::lua_next(lua.state, -2)? != 0 {
ffi::lua_pushvalue(lua.state, -2);
let key = lua.pop_value();
let value = lua.pop_value();
self.next_key = Some(lua.pop_value());
Some((key, value))
let key = lua.pop_value();
Ok(Some((
key.clone(),
K::from_lua(key, lua)?,
V::from_lua(value, lua)?,
)))
} else {
None
Ok(None)
}
};
Ok(if let Some((key, value)) = res {
Some((K::from_lua(key, lua)?, V::from_lua(value, lua)?))
} else {
None
})
})();
match res {
Ok(Some((key, value))) => Some(Ok((key, value))),
Ok(Some((key, ret_key, value))) => {
self.key = Some(key);
Some(Ok((ret_key, value)))
}
Ok(None) => None,
Err(e) => Some(Err(e)),
}
@ -711,31 +707,29 @@ where
if let Some(index) = self.index.take() {
let lua = self.table.lua;
let res = unsafe {
let res = (|| unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 4);
check_stack(lua.state, 1 + if self.raw { 0 } else { 4 })?;
lua.push_ref(&self.table);
let res = if self.raw {
Ok(ffi::lua_rawgeti(lua.state, -1, index))
ffi::lua_rawgeti(lua.state, -1, index)
} else {
ffi::safe::lua_geti(lua.state, -1, index)
ffi::safe::lua_geti(lua.state, -1, index)?
};
match res {
Ok(ffi::LUA_TNIL) if index > self.len.unwrap_or(0) => None,
Ok(_) => {
let value = lua.pop_value();
self.index = Some(index + 1);
Some(Ok(value))
ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None),
_ => Ok(Some((index, lua.pop_value()))),
}
Err(err) => Some(Err(err)),
}
};
})();
match res {
Some(Ok(r)) => Some(V::from_lua(r, lua)),
Some(Err(err)) => Some(Err(err)),
None => None,
Ok(Some((index, r))) => {
self.index = Some(index + 1);
Some(V::from_lua(r, lua))
}
Ok(None) => None,
Err(err) => Some(Err(err)),
}
} else {
None

View File

@ -1,3 +1,4 @@
use std::cmp;
use std::os::raw::c_int;
use crate::error::{Error, Result};
@ -107,9 +108,10 @@ impl<'lua> Thread<'lua> {
{
let lua = self.0.lua;
let args = args.to_lua_multi(lua)?;
let nargs = args.len() as c_int;
let results = unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 2);
check_stack(lua.state, cmp::min(nargs + 1, 3))?;
lua.push_ref(&self.0);
let thread_state = ffi::lua_tothread(lua.state, -1);
@ -120,10 +122,7 @@ impl<'lua> Thread<'lua> {
return Err(Error::CoroutineInactive);
}
let nargs = args.len() as c_int;
check_stack(lua.state, nargs)?;
check_stack(thread_state, nargs + 1)?;
check_stack(thread_state, nargs)?;
for arg in args {
lua.push_value(arg)?;
}
@ -138,10 +137,9 @@ impl<'lua> Thread<'lua> {
}
let mut results = MultiValue::new();
check_stack(lua.state, nresults)?;
check_stack(lua.state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below
ffi::lua_xmove(thread_state, lua.state, nresults);
assert_stack(lua.state, 2);
for _ in 0..nresults {
results.push_front(lua.pop_value());
}
@ -314,9 +312,7 @@ fn is_poll_pending(lua: &Lua, val: &MultiValue) -> bool {
assert_stack(lua.state, 3);
lua.push_ref(&ud.0);
let is_pending = get_gc_userdata::<AsyncPollPending>(lua.state, -1)
.as_ref()
.is_some();
let is_pending = !get_gc_userdata::<AsyncPollPending>(lua.state, -1).is_null();
ffi::lua_pop(lua.state, 1);
return is_pending;
@ -334,11 +330,11 @@ impl WakerGuard {
pub fn new(state: *mut ffi::lua_State, waker: Waker) -> Result<WakerGuard> {
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 6);
check_stack(state, 5)?;
ffi::lua_pushlightuserdata(state, &WAKER_REGISTRY_KEY as *const u8 as *mut c_void);
push_gc_userdata(state, waker)?;
ffi::safe::lua_rawset(state, ffi::LUA_REGISTRYINDEX)?;
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, waker_key)?;
Ok(WakerGuard(state))
}
@ -348,14 +344,14 @@ impl WakerGuard {
#[cfg(feature = "async")]
impl Drop for WakerGuard {
fn drop(&mut self) {
unsafe {
let state = self.0;
unsafe {
let _sg = StackGuard::new(state);
assert_stack(state, 2);
assert_stack(state, 1);
ffi::lua_pushlightuserdata(state, &WAKER_REGISTRY_KEY as *const u8 as *mut c_void);
ffi::lua_pushnil(state);
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX); // TODO: make safe
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, waker_key); // TODO: make safe
}
}
}

View File

@ -19,8 +19,8 @@ use crate::ffi;
use crate::function::Function;
use crate::lua::Lua;
use crate::table::{Table, TablePairs};
use crate::types::{Integer, LuaRef, MaybeSend};
use crate::util::{assert_stack, get_destructed_userdata_metatable, get_userdata, StackGuard};
use crate::types::{LuaRef, MaybeSend};
use crate::util::{check_stack, get_destructed_userdata_metatable, get_userdata, StackGuard};
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti, Value};
/// Kinds of metamethods that can be overridden.
@ -112,7 +112,7 @@ pub enum MetaMethod {
Close,
/// A custom metamethod.
///
/// Must not be in the protected list: `__gc`, `__metatable`.
/// Must not be in the protected list: `__gc`, `__metatable`, `__mlua*`.
Custom(StdString),
}
@ -546,6 +546,7 @@ pub trait UserData: Sized {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {}
}
// Wraps UserData in a way to always implement `serde::Serialize` trait.
pub(crate) enum UserDataCell<T> {
Arc(Arc<RefCell<UserDataWrapped<T>>>),
Plain(RefCell<UserDataWrapped<T>>),
@ -596,7 +597,7 @@ impl<T> Clone for UserDataCell<T> {
fn clone(&self) -> Self {
match self {
UserDataCell::Arc(t) => UserDataCell::Arc(t.clone()),
UserDataCell::Plain(_) => mlua_panic!("cannot clone plain userdata"),
UserDataCell::Plain(_) => mlua_panic!("cannot clone non-arc userdata"),
}
}
}
@ -711,8 +712,8 @@ impl<'lua> AnyUserData<'lua> {
let lua = self.0.lua;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
let v = {
// Lua 5.2/5.1 allows to store only a table. Then we will wrap the value.
let t = lua.create_table()?;
// Lua <= 5.2 allows to store only a table. Then we will wrap the value.
let t = lua.create_table_with_capacity(1, 0)?;
t.raw_set(1, v)?;
Value::Table(t)
};
@ -720,10 +721,12 @@ impl<'lua> AnyUserData<'lua> {
let v = v.to_lua(lua)?;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 3);
check_stack(lua.state, 3)?;
lua.push_userdata_ref(&self.0)?;
lua.push_value(v)?;
ffi::lua_setuservalue(lua.state, -2);
Ok(())
}
}
@ -737,7 +740,8 @@ impl<'lua> AnyUserData<'lua> {
let lua = self.0.lua;
let res = unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 3);
check_stack(lua.state, 3)?;
lua.push_userdata_ref(&self.0)?;
ffi::lua_getuservalue(lua.state, -1);
lua.pop_value()
@ -792,13 +796,10 @@ impl<'lua> AnyUserData<'lua> {
unsafe {
let lua = self.0.lua;
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 3);
check_stack(lua.state, 3)?;
lua.push_userdata_ref(&self.0)?;
if ffi::lua_getmetatable(lua.state, -1) == 0 {
return Err(Error::UserDataTypeMismatch);
}
ffi::lua_getmetatable(lua.state, -1); // Checked that non-empty on the previous call
Ok(Table(lua.pop_ref()))
}
}
@ -829,20 +830,16 @@ impl<'lua> AnyUserData<'lua> {
T: 'static + UserData,
F: FnOnce(&'a UserDataCell<T>) -> Result<R>,
{
unsafe {
let lua = self.0.lua;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 3);
check_stack(lua.state, 3)?;
lua.push_ref(&self.0);
if ffi::lua_getmetatable(lua.state, -1) == 0 {
return Err(Error::UserDataTypeMismatch);
}
ffi::lua_rawgeti(
lua.state,
ffi::LUA_REGISTRYINDEX,
lua.userdata_metatable::<T>()? as Integer,
);
lua.push_userdata_metatable::<T>()?;
if ffi::lua_rawequal(lua.state, -1, -2) == 0 {
// Maybe UserData destructed?
@ -956,7 +953,7 @@ impl<'lua> Serialize for AnyUserData<'lua> {
let res = (|| unsafe {
let lua = self.0.lua;
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 3);
check_stack(lua.state, 3)?;
lua.push_userdata_ref(&self.0)?;
let ud = &*get_userdata::<UserDataCell<()>>(lua.state, -1);

View File

@ -40,6 +40,7 @@ pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<(
pub struct StackGuard {
state: *mut ffi::lua_State,
top: c_int,
extra: c_int,
}
impl StackGuard {
@ -50,6 +51,16 @@ impl StackGuard {
StackGuard {
state,
top: ffi::lua_gettop(state),
extra: 0,
}
}
// Similar to `new`, but checks and keeps `extra` elements from top of the stack on Drop.
pub unsafe fn new_extra(state: *mut ffi::lua_State, extra: c_int) -> StackGuard {
StackGuard {
state,
top: ffi::lua_gettop(state),
extra,
}
}
}
@ -58,11 +69,14 @@ impl Drop for StackGuard {
fn drop(&mut self) {
unsafe {
let top = ffi::lua_gettop(self.state);
if top < self.top {
if top < self.top + self.extra {
mlua_panic!("{} too many stack values popped", self.top - top)
}
if top > self.top {
ffi::lua_settop(self.state, self.top);
if top > self.top + self.extra {
if self.extra > 0 {
ffi::lua_rotate(self.state, self.top + 1, self.extra);
}
ffi::lua_settop(self.state, self.top + self.extra);
}
}
}
@ -147,7 +161,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
}
}
// Internally uses 3 stack spaces, does not call checkstack
// Internally uses 3 stack spaces, does not call checkstack.
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
let ud = ffi::safe::lua_newuserdata(state, mem::size_of::<T>())? as *mut T;
ptr::write(ud, t);
@ -171,14 +185,13 @@ pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
// after the first call to __gc.
get_destructed_userdata_metatable(state);
ffi::lua_setmetatable(state, -2);
let ud = ffi::lua_touserdata(state, -1) as *mut T;
mlua_debug_assert!(!ud.is_null(), "userdata pointer is null");
let ud = get_userdata(state, -1);
ffi::lua_pop(state, 1);
ptr::read(ud)
}
// Pushes the userdata and attaches a metatable with __gc method.
// Internally uses 4 stack spaces, does not call checkstack.
// Internally uses 3 stack spaces, does not call checkstack.
pub unsafe fn push_gc_userdata<T: Any>(state: *mut ffi::lua_State, t: T) -> Result<()> {
push_userdata(state, t)?;
get_gc_metatable_for::<T>(state);
@ -193,9 +206,9 @@ pub unsafe fn get_gc_userdata<T: Any>(state: *mut ffi::lua_State, index: c_int)
return ptr::null_mut();
}
get_gc_metatable_for::<T>(state);
let res = ffi::lua_rawequal(state, -1, -2) != 0;
let res = ffi::lua_rawequal(state, -1, -2);
ffi::lua_pop(state, 2);
if !res {
if res == 0 {
return ptr::null_mut();
}
ud
@ -208,7 +221,7 @@ pub unsafe fn get_gc_userdata<T: Any>(state: *mut ffi::lua_State, index: c_int)
// (capturing previous one) to lookup in `field_getters` first, then `methods` and falling back to the
// captured `__index` if no matches found.
// The same is also applicable for `__newindex` metamethod and `field_setters` table.
// Internally uses 8 stack spaces and does not call checkstack.
// Internally uses 9 stack spaces and does not call checkstack.
pub unsafe fn init_userdata_metatable<T>(
state: *mut ffi::lua_State,
metatable: c_int,
@ -277,15 +290,15 @@ pub unsafe extern "C" fn userdata_destructor<T>(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). 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.
// 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).
//
// 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 0.
// as normal, but cannot assume that the arguments available start at 1.
pub unsafe fn callback_error<F>(state: *mut ffi::lua_State, f: F) -> c_int
where
F: FnOnce(c_int) -> Result<c_int>,
@ -315,6 +328,9 @@ where
}
}
// A part of the C shim (error_traceback).
// Receives absolute index of error in the stack, a pointer to pre-allocated WrappedError memory,
// and optional boolean flag if a traceback value is on top of the stack.
#[no_mangle]
pub unsafe extern "C" fn wrapped_error_traceback(
state: *mut ffi::lua_State,
@ -348,6 +364,7 @@ pub unsafe extern "C" fn wrapped_error_traceback(
ffi::lua_setmetatable(state, -2);
}
// Returns Lua main thread for Lua >= 5.2 or checks that the passed thread is main for Lua 5.1.
// Does not call lua_checkstack, uses 1 stack space.
pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua_State> {
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
@ -370,10 +387,14 @@ 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 two stack spaces and does not call
// lua_checkstack.
// Pushes a WrappedError to the top of the stack.
// Uses 2 stack spaces and does not call checkstack.
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> {
push_gc_userdata::<WrappedError>(state, WrappedError(err))
let error_ud = ffi::safe::lua_newwrappederror(state)? as *mut WrappedError;
ptr::write(error_ud, WrappedError(err));
get_gc_metatable_for::<WrappedError>(state);
ffi::lua_setmetatable(state, -2);
Ok(())
}
// Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it,
@ -387,13 +408,15 @@ pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *co
&(*ud).0
}
// Initialize the internal (with __gc) metatable for a type T
// Initialize the internal (with __gc method) metatable for a type T.
// Uses 6 stack spaces and calls checkstack.
pub unsafe fn init_gc_metatable_for<T: Any>(
state: *mut ffi::lua_State,
customize_fn: Option<fn(*mut ffi::lua_State) -> Result<()>>,
) -> Result<*const u8> {
let type_id = TypeId::of::<T>();
check_stack(state, 6)?;
let type_id = TypeId::of::<T>();
let ref_addr = {
let mut mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache");
mlua_assert!(
@ -433,7 +456,7 @@ pub unsafe fn get_gc_metatable_for<T: Any>(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)> {
assert_stack(state, 8);
check_stack(state, 7)?;
// Create error and panic metatables
@ -442,11 +465,8 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const
check_stack(state, 3)?;
let err_buf = if let Some(error) = get_wrapped_error(state, -1).as_ref() {
ffi::lua_pushlightuserdata(
state,
&ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void,
);
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
ffi::lua_pop(state, 2);
@ -507,22 +527,16 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const
unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
callback_error(state, |_| {
check_stack(state, 3)?;
let ud = ffi::safe::lua_newuserdata(state, mem::size_of::<WrappedError>())?
as *mut WrappedError;
ptr::write(ud, WrappedError(Error::CallbackDestructed));
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::<WrappedError>(state);
ffi::lua_setmetatable(state, -2);
Ok(-1) // to trigger lua_error
})
}
ffi::lua_pushlightuserdata(
state,
&DESTRUCTED_USERDATA_METATABLE as *const u8 as *mut c_void,
);
ffi::safe::lua_createtable(state, 0, 26)?;
ffi::safe::lua_pushrclosure(state, destructed_error, 0)?;
for &method in &[
"__add",
@ -567,22 +581,14 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const
}
ffi::lua_pop(state, 1);
ffi::safe::lua_rawset(state, ffi::LUA_REGISTRYINDEX)?;
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)?;
// Create error print buffer
ffi::lua_pushlightuserdata(state, &ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void);
let ud = ffi::safe::lua_newuserdata(state, mem::size_of::<String>())? as *mut String;
ptr::write(ud, String::new());
ffi::safe::lua_createtable(state, 0, 1)?;
ffi::safe::lua_pushrclosure(state, userdata_destructor::<String>, 0)?;
ffi::safe::lua_rawsetfield(state, -2, "__gc")?;
ffi::lua_setmetatable(state, -2);
ffi::safe::lua_rawset(state, ffi::LUA_REGISTRYINDEX)?;
init_gc_metatable_for::<String>(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)?;
Ok((wrapped_error_key, wrapped_panic_key))
}
@ -625,11 +631,8 @@ unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> String {
}
pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
ffi::lua_pushlightuserdata(
state,
&DESTRUCTED_USERDATA_METATABLE as *const u8 as *mut c_void,
);
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
let key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
}
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;

View File

@ -18,8 +18,7 @@ use crate::userdata::AnyUserData;
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData`
/// variants contain handle types into the internal Lua state. It is a logic error to mix handle
/// types between separate `Lua` instances, or between a parent `Lua` instance and one received as a
/// parameter in a Rust callback, and doing so will result in a panic.
/// types between separate `Lua` instances, and doing so will result in a panic.
#[derive(Debug, Clone)]
pub enum Value<'lua> {
/// The Lua value `nil`.