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_tolstring_s(L: *mut lua_State) -> c_int;
fn lua_newthread_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_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_pushcclosure_s(L: *mut lua_State) -> c_int;
fn lua_pushrclosure_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 luaL_requiref_s(L: *mut lua_State) -> c_int;
fn error_traceback_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_createtable_s(L: *mut lua_State) -> c_int;
fn lua_gettable_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; 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)) Ok(super::lua_touserdata(state, -1))
} }
// Uses 4 stack spaces // Uses 2 stack spaces
pub unsafe fn lua_pushcclosure(state: *mut lua_State, f: lua_CFunction, n: c_int) -> Result<()> { pub unsafe fn lua_newwrappederror(state: *mut lua_State) -> Result<*mut c_void> {
super::lua_pushlightuserdata(state, f as *mut c_void); protect_lua(state, 0, lua_newwrappederror_s)?;
super::lua_pushinteger(state, n as lua_Integer); Ok(super::lua_touserdata(state, -1))
protect_lua(state, n + 2, lua_pushcclosure_s)
} }
// 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<()> { 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); super::lua_pushlightuserdata(state, f as *mut c_void);
if n > 0 { if n > 0 {
super::lua_rotate(state, -n - 1, 1); super::lua_rotate(state, -n - 1, 1);
} }
super::lua_pushinteger(state, n as lua_Integer + 1); protect_lua(state, n + 1, lua_pushrclosure_s)
protect_lua(state, n + 2, lua_pushrclosure_s)
} }
// Uses 5 stack spaces // Uses 5 stack spaces
@ -163,6 +169,11 @@ pub unsafe fn error_traceback2(state: *mut lua_State, state2: *mut lua_State) ->
// Table functions // 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 // Uses 4 stack spaces
pub unsafe fn lua_createtable(state: *mut lua_State, narr: c_int, nrec: c_int) -> Result<()> { pub unsafe fn lua_createtable(state: *mut lua_State, narr: c_int, nrec: c_int) -> Result<()> {
super::lua_pushinteger(state, narr as lua_Integer); super::lua_pushinteger(state, narr as lua_Integer);

View File

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

View File

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

View File

@ -9,8 +9,8 @@ use crate::util::callback_error;
/// Contains information about currently executing Lua code. /// Contains information about currently executing Lua code.
/// ///
/// The `Debug` structure is provided as a parameter to the hook function set with /// The `Debug` structure is provided as a parameter to the hook function set with
/// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the /// [`Lua::set_hook`]. You may call the methods on this structure to retrieve information about the
/// Lua code executing at the time that the hook function was called. Further information can be /// Lua code executing at the time that the hook function was called. Further information can be
/// found in the [Lua 5.3 documentaton][lua_doc]. /// found in the [Lua 5.3 documentaton][lua_doc].
/// ///
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#lua_Debug /// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#lua_Debug
@ -130,7 +130,7 @@ pub struct HookTriggers {
pub on_returns: bool, pub on_returns: bool,
/// Before executing a new line, or returning from a function call. /// Before executing a new line, or returning from a function call.
pub every_line: bool, pub every_line: bool,
/// After a certain number of VM instructions have been executed. When set to `Some(count)`, /// After a certain number of VM instructions have been executed. When set to `Some(count)`,
/// `count` is the number of VM instructions to execute before calling the hook. /// `count` is the number of VM instructions to execute before calling the hook.
/// ///
/// # Performance /// # Performance

View File

@ -84,9 +84,9 @@ struct MemoryInfo {
/// In Lua 5.4 GC can work in two modes: incremental and generational. /// In Lua 5.4 GC can work in two modes: incremental and generational.
/// Previous Lua versions support only incremental GC. /// 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 { pub enum GCMode {
Incremental, Incremental,
/// Requires `feature = "lua54"` /// Requires `feature = "lua54"`
@ -128,11 +128,15 @@ impl Drop for Lua {
} }
impl 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 /// # Safety
/// The created Lua state would have _some_ safety guarantees and would not allow to load unsafe /// The created Lua state would have _some_ safety guarantees and would not allow to load unsafe
/// standard libraries or C modules. /// 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)] #[allow(clippy::new_without_default)]
pub fn new() -> Lua { pub fn new() -> Lua {
mlua_expect!( mlua_expect!(
@ -157,6 +161,8 @@ impl Lua {
/// The created Lua state would have _some_ safety guarantees and would not allow to load unsafe /// The created Lua state would have _some_ safety guarantees and would not allow to load unsafe
/// standard libraries or C modules. /// standard libraries or C modules.
/// ///
/// See [`StdLib`] documentation for a list of unsafe modules that cannot be loaded.
///
/// [`StdLib`]: struct.StdLib.html /// [`StdLib`]: struct.StdLib.html
pub fn new_with(libs: StdLib) -> Result<Lua> { pub fn new_with(libs: StdLib) -> Result<Lua> {
if libs.contains(StdLib::DEBUG) { if libs.contains(StdLib::DEBUG) {
@ -188,7 +194,7 @@ impl Lua {
/// Use the [`StdLib`] flags to specifiy the libraries you want to load. /// Use the [`StdLib`] flags to specifiy the libraries you want to load.
/// ///
/// # Safety /// # 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 /// [`StdLib`]: struct.StdLib.html
pub unsafe fn unsafe_new_with(libs: StdLib) -> Lua { 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 = maybe_main_state.unwrap_or(state);
let main_state_top = ffi::lua_gettop(main_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| { (|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)?; 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 // Create the internal metatables and place them in the registry
// to prevent them from being garbage collected. // to prevent them from being garbage collected.
@ -317,7 +331,7 @@ impl Lua {
let ref_thread = ffi::safe::lua_newthread(state)?; let ref_thread = ffi::safe::lua_newthread(state)?;
ffi::safe::luaL_ref(state, ffi::LUA_REGISTRYINDEX)?; ffi::safe::luaL_ref(state, ffi::LUA_REGISTRYINDEX)?;
Ok::<_, Error>((ref_thread, wrapped_error_key, wrapped_panic_key)) Ok::<_, Error>(ref_thread)
})(main_state), })(main_state),
"Error during Lua construction", "Error during Lua construction",
); );
@ -342,12 +356,9 @@ impl Lua {
push_gc_userdata(main_state, Arc::downgrade(&extra)), push_gc_userdata(main_state, Arc::downgrade(&extra)),
"Error while storing extra data", "Error while storing extra data",
); );
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
mlua_expect!( mlua_expect!(
ffi::safe::lua_rawsetp( ffi::safe::lua_rawsetp(main_state, ffi::LUA_REGISTRYINDEX, extra_key,),
main_state,
ffi::LUA_REGISTRYINDEX,
&EXTRA_REGISTRY_KEY as *const u8 as *const c_void
),
"Error while storing extra data" "Error while storing extra data"
); );
@ -357,11 +368,6 @@ impl Lua {
); );
assert_stack(main_state, ffi::LUA_MINSTACK); 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 { Lua {
state, state,
main_state: maybe_main_state, main_state: maybe_main_state,
@ -448,7 +454,7 @@ impl Lua {
/// parameter, see [`HookTriggers`] for more details. /// parameter, see [`HookTriggers`] for more details.
/// ///
/// The provided hook function can error, and this error will be propagated through the Lua code /// The provided hook function can error, and this error will be propagated through the Lua code
/// that was executing at the time the hook was triggered. This can be used to implement a /// that was executing at the time the hook was triggered. This can be used to implement a
/// limited form of execution limits by setting [`HookTriggers.every_nth_instruction`] and /// limited form of execution limits by setting [`HookTriggers.every_nth_instruction`] and
/// erroring once an instruction limit has been reached. /// erroring once an instruction limit has been reached.
/// ///
@ -571,7 +577,7 @@ impl Lua {
/// Perform a full garbage-collection cycle. /// Perform a full garbage-collection cycle.
/// ///
/// It may be necessary to call this function twice to collect all currently unreachable /// It may be necessary to call this function twice to collect all currently unreachable
/// objects. Once to finish the current gc cycle, and once to start and finish the next cycle. /// objects. Once to finish the current gc cycle, and once to start and finish the next cycle.
pub fn gc_collect(&self) -> Result<()> { pub fn gc_collect(&self) -> Result<()> {
let state = self.main_state.unwrap_or(self.state); let state = self.main_state.unwrap_or(self.state);
unsafe { ffi::safe::lua_gc(state, ffi::LUA_GCCOLLECT, 0).map(|_| ()) } unsafe { ffi::safe::lua_gc(state, ffi::LUA_GCCOLLECT, 0).map(|_| ()) }
@ -586,7 +592,7 @@ impl Lua {
/// Steps the garbage collector as though memory had been allocated. /// Steps the garbage collector as though memory had been allocated.
/// ///
/// if `kbytes` is 0, then this is the same as calling `gc_step`. Returns true if this step has /// if `kbytes` is 0, then this is the same as calling `gc_step`. Returns true if this step has
/// finished a collection cycle. /// finished a collection cycle.
pub fn gc_step_kbytes(&self, kbytes: c_int) -> Result<bool> { pub fn gc_step_kbytes(&self, kbytes: c_int) -> Result<bool> {
let state = self.main_state.unwrap_or(self.state); let state = self.main_state.unwrap_or(self.state);
@ -595,7 +601,7 @@ impl Lua {
/// Sets the 'pause' value of the collector. /// Sets the 'pause' value of the collector.
/// ///
/// Returns the previous value of 'pause'. More information can be found in the [Lua 5.3 /// Returns the previous value of 'pause'. More information can be found in the [Lua 5.3
/// documentation][lua_doc]. /// documentation][lua_doc].
/// ///
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#2.5 /// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#2.5
@ -606,7 +612,7 @@ impl Lua {
/// Sets the 'step multiplier' value of the collector. /// Sets the 'step multiplier' value of the collector.
/// ///
/// Returns the previous value of the 'step multiplier'. More information can be found in the /// Returns the previous value of the 'step multiplier'. More information can be found in the
/// Lua 5.x [documentation][lua_doc]. /// Lua 5.x [documentation][lua_doc].
/// ///
/// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#2.5 /// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#2.5
@ -642,15 +648,8 @@ impl Lua {
} }
#[cfg(feature = "lua54")] #[cfg(feature = "lua54")]
let prev_mode = unsafe { let prev_mode =
ffi::lua_gc( unsafe { ffi::lua_gc(state, ffi::LUA_GCINC, pause, step_multiplier, step_size) };
state,
ffi::LUA_GCSETPAUSE,
pause,
step_multiplier,
step_size,
)
};
#[cfg(feature = "lua54")] #[cfg(feature = "lua54")]
match prev_mode { match prev_mode {
ffi::LUA_GCINC => GCMode::Incremental, ffi::LUA_GCINC => GCMode::Incremental,
@ -682,7 +681,7 @@ impl Lua {
/// Returns Lua source code as a `Chunk` builder type. /// Returns Lua source code as a `Chunk` builder type.
/// ///
/// In order to actually compile or run the resulting code, you must call [`Chunk::exec`] or /// In order to actually compile or run the resulting code, you must call [`Chunk::exec`] or
/// similar on the returned builder. Code is not even parsed until one of these methods is /// similar on the returned builder. Code is not even parsed until one of these methods is
/// called. /// called.
/// ///
/// If this `Lua` was created with `unsafe_new`, `load` will automatically detect and load /// If this `Lua` was created with `unsafe_new`, `load` will automatically detect and load
@ -691,7 +690,7 @@ impl Lua {
/// [`Chunk::exec`]: struct.Chunk.html#method.exec /// [`Chunk::exec`]: struct.Chunk.html#method.exec
pub fn load<'lua, 'a, S>(&'lua self, source: &'a S) -> Chunk<'lua, 'a> pub fn load<'lua, 'a, S>(&'lua self, source: &'a S) -> Chunk<'lua, 'a>
where where
S: ?Sized + AsRef<[u8]>, S: AsRef<[u8]> + ?Sized,
{ {
Chunk { Chunk {
lua: self, lua: self,
@ -711,7 +710,7 @@ impl Lua {
) -> Result<Function<'lua>> { ) -> Result<Function<'lua>> {
unsafe { unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 1); check_stack(self.state, 1)?;
let mode_str = match mode { let mode_str = match mode {
Some(ChunkMode::Binary) if self.safe => { Some(ChunkMode::Binary) if self.safe => {
@ -751,16 +750,16 @@ impl Lua {
} }
} }
/// Create and return an interned Lua string. Lua strings can be arbitrary [u8] data including /// Create and return an interned Lua string. Lua strings can be arbitrary [u8] data including
/// embedded nulls, so in addition to `&str` and `&String`, you can also pass plain `&[u8]` /// embedded nulls, so in addition to `&str` and `&String`, you can also pass plain `&[u8]`
/// here. /// here.
pub fn create_string<S>(&self, s: &S) -> Result<String> pub fn create_string<S>(&self, s: &S) -> Result<String>
where where
S: ?Sized + AsRef<[u8]>, S: AsRef<[u8]> + ?Sized,
{ {
unsafe { unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 4); check_stack(self.state, 3)?;
ffi::safe::lua_pushstring(self.state, s)?; ffi::safe::lua_pushstring(self.state, s)?;
Ok(String(self.pop_ref())) Ok(String(self.pop_ref()))
} }
@ -768,7 +767,12 @@ impl Lua {
/// Creates and returns a new empty table. /// Creates and returns a new empty table.
pub fn create_table(&self) -> Result<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. /// 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> { pub fn create_table_with_capacity(&self, narr: c_int, nrec: c_int) -> Result<Table> {
unsafe { unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 4); check_stack(self.state, 4)?;
ffi::safe::lua_createtable(self.state, narr, nrec)?; ffi::safe::lua_createtable(self.state, narr, nrec)?;
Ok(Table(self.pop_ref())) Ok(Table(self.pop_ref()))
} }
} }
/// Creates a table and fills it with values from an iterator. /// 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 where
K: ToLua<'lua>, K: ToLua<'lua>,
V: ToLua<'lua>, V: ToLua<'lua>,
@ -793,12 +797,12 @@ impl Lua {
{ {
unsafe { unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
// `Lua` instance assumes that on any callback, the Lua stack has at least LUA_MINSTACK check_stack(self.state, 6)?;
// slots available to avoid panics.
check_stack(self.state, 5 + ffi::LUA_MINSTACK)?;
ffi::safe::lua_createtable(self.state, 0, 0)?; let iter = iter.into_iter();
for (k, v) in cont { 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(k.to_lua(self)?)?;
self.push_value(v.to_lua(self)?)?; self.push_value(v.to_lua(self)?)?;
ffi::safe::lua_rawset(self.state, -3)?; 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. /// 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 where
T: ToLua<'lua>, T: ToLua<'lua>,
I: IntoIterator<Item = T>, 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. /// Wraps a Rust function or closure, creating a callable Lua function handle to it.
@ -876,7 +893,7 @@ impl Lua {
/// Wraps a Rust mutable closure, creating a callable Lua function handle to it. /// Wraps a Rust mutable closure, creating a callable Lua function handle to it.
/// ///
/// This is a version of [`create_function`] that accepts a FnMut argument. Refer to /// This is a version of [`create_function`] that accepts a FnMut argument. Refer to
/// [`create_function`] for more information about the implementation. /// [`create_function`] for more information about the implementation.
/// ///
/// [`create_function`]: #method.create_function /// [`create_function`]: #method.create_function
@ -966,7 +983,7 @@ impl Lua {
pub fn create_thread<'lua>(&'lua self, func: Function<'lua>) -> Result<Thread<'lua>> { pub fn create_thread<'lua>(&'lua self, func: Function<'lua>) -> Result<Thread<'lua>> {
unsafe { unsafe {
let _sg = StackGuard::new(self.state); 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)?; let thread_state = ffi::safe::lua_newthread(self.state)?;
self.push_ref(&func.0); self.push_ref(&func.0);
@ -1000,7 +1017,7 @@ impl Lua {
pub fn globals(&self) -> Table { pub fn globals(&self) -> Table {
unsafe { unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2); assert_stack(self.state, 1);
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS); ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", feature = "luajit"))]
@ -1009,10 +1026,12 @@ impl Lua {
} }
} }
/// Returns a handle to the active `Thread`. For calls to `Lua` this will be the main Lua thread, /// Returns a handle to the active `Thread`. For calls to `Lua` this will be the main Lua thread,
/// for parameters given to a callback, this will be whatever Lua thread called the callback. /// for parameters given to a callback, this will be whatever Lua thread called the callback.
pub fn current_thread(&self) -> Thread { pub fn current_thread(&self) -> Thread {
unsafe { unsafe {
let _sg = StackGuard::new(self.state);
assert_stack(self.state, 1);
ffi::lua_pushthread(self.state); ffi::lua_pushthread(self.state);
Thread(self.pop_ref()) Thread(self.pop_ref())
} }
@ -1023,19 +1042,19 @@ impl Lua {
/// ///
/// The lifetime of any function or userdata created through `Scope` lasts only until the /// The lifetime of any function or userdata created through `Scope` lasts only until the
/// completion of this method call, on completion all such created values are automatically /// completion of this method call, on completion all such created values are automatically
/// dropped and Lua references to them are invalidated. If a script accesses a value created /// dropped and Lua references to them are invalidated. If a script accesses a value created
/// through `Scope` outside of this method, a Lua error will result. Since we can ensure the /// through `Scope` outside of this method, a Lua error will result. Since we can ensure the
/// lifetime of values created through `Scope`, and we know that `Lua` cannot be sent to another /// lifetime of values created through `Scope`, and we know that `Lua` cannot be sent to another
/// thread while `Scope` is live, it is safe to allow !Send datatypes and whose lifetimes only /// thread while `Scope` is live, it is safe to allow !Send datatypes and whose lifetimes only
/// outlive the scope lifetime. /// outlive the scope lifetime.
/// ///
/// Inside the scope callback, all handles created through Scope will share the same unique 'lua /// Inside the scope callback, all handles created through Scope will share the same unique 'lua
/// lifetime of the parent `Lua`. This allows scoped and non-scoped values to be mixed in /// lifetime of the parent `Lua`. This allows scoped and non-scoped values to be mixed in
/// API calls, which is very useful (e.g. passing a scoped userdata to a non-scoped function). /// API calls, which is very useful (e.g. passing a scoped userdata to a non-scoped function).
/// However, this also enables handles to scoped values to be trivially leaked from the given /// However, this also enables handles to scoped values to be trivially leaked from the given
/// callback. This is not dangerous, though! After the callback returns, all scoped values are /// callback. This is not dangerous, though! After the callback returns, all scoped values are
/// invalidated, which means that though references may exist, the Rust types backing them have /// invalidated, which means that though references may exist, the Rust types backing them have
/// dropped. `Function` types will error when called, and `AnyUserData` will be typeless. It /// dropped. `Function` types will error when called, and `AnyUserData` will be typeless. It
/// would be impossible to prevent handles to scoped values from escaping anyway, since you /// would be impossible to prevent handles to scoped values from escaping anyway, since you
/// would always be able to smuggle them through Lua state. /// would always be able to smuggle them through Lua state.
pub fn scope<'lua, 'scope, R, F>(&'lua self, f: F) -> Result<R> pub fn scope<'lua, 'scope, R, F>(&'lua self, f: F) -> Result<R>
@ -1078,7 +1097,7 @@ impl Lua {
Value::String(s) => Some(s), Value::String(s) => Some(s),
v => unsafe { v => unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 4); check_stack(self.state, 5)?;
self.push_value(v)?; self.push_value(v)?;
if !ffi::safe::lua_tolstring(self.state, -1, ptr::null_mut())?.is_null() { if !ffi::safe::lua_tolstring(self.state, -1, ptr::null_mut())?.is_null() {
@ -1101,7 +1120,7 @@ impl Lua {
Value::Integer(i) => Some(i), Value::Integer(i) => Some(i),
v => unsafe { v => unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2); check_stack(self.state, 2)?;
self.push_value(v)?; self.push_value(v)?;
let mut isint = 0; let mut isint = 0;
@ -1125,7 +1144,7 @@ impl Lua {
Value::Number(n) => Some(n), Value::Number(n) => Some(n),
v => unsafe { v => unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2); check_stack(self.state, 2)?;
self.push_value(v)?; self.push_value(v)?;
let mut isnum = 0; let mut isnum = 0;
@ -1168,13 +1187,13 @@ impl Lua {
/// state. /// state.
pub fn set_named_registry_value<'lua, S, T>(&'lua self, name: &S, t: T) -> Result<()> pub fn set_named_registry_value<'lua, S, T>(&'lua self, name: &S, t: T) -> Result<()>
where where
S: ?Sized + AsRef<[u8]>, S: AsRef<[u8]> + ?Sized,
T: ToLua<'lua>, T: ToLua<'lua>,
{ {
let t = t.to_lua(self)?; let t = t.to_lua(self)?;
unsafe { unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 5); check_stack(self.state, 5)?;
self.push_value(t)?; self.push_value(t)?;
ffi::safe::lua_rawsetfield(self.state, ffi::LUA_REGISTRYINDEX, name) 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 /// [`set_named_registry_value`]: #method.set_named_registry_value
pub fn named_registry_value<'lua, S, T>(&'lua self, name: &S) -> Result<T> pub fn named_registry_value<'lua, S, T>(&'lua self, name: &S) -> Result<T>
where where
S: ?Sized + AsRef<[u8]>, S: AsRef<[u8]> + ?Sized,
T: FromLua<'lua>, T: FromLua<'lua>,
{ {
let value = unsafe { let value = unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 4); check_stack(self.state, 3)?;
ffi::safe::lua_pushstring(self.state, name)?; ffi::safe::lua_pushstring(self.state, name)?;
ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX); ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX);
@ -1211,7 +1230,7 @@ impl Lua {
/// [`set_named_registry_value`]: #method.set_named_registry_value /// [`set_named_registry_value`]: #method.set_named_registry_value
pub fn unset_named_registry_value<S>(&self, name: &S) -> Result<()> pub fn unset_named_registry_value<S>(&self, name: &S) -> Result<()>
where where
S: ?Sized + AsRef<[u8]>, S: AsRef<[u8]> + ?Sized,
{ {
self.set_named_registry_value(name, Nil) self.set_named_registry_value(name, Nil)
} }
@ -1229,7 +1248,7 @@ impl Lua {
let t = t.to_lua(self)?; let t = t.to_lua(self)?;
unsafe { unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2); check_stack(self.state, 4)?;
self.push_value(t)?; self.push_value(t)?;
let registry_id = ffi::safe::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)?; 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 /// [`create_registry_value`]: #method.create_registry_value
pub fn registry_value<'lua, T: FromLua<'lua>>(&'lua self, key: &RegistryKey) -> Result<T> { pub fn registry_value<'lua, T: FromLua<'lua>>(&'lua self, key: &RegistryKey) -> Result<T> {
let value = unsafe { if !self.owns_registry_value(key) {
if !self.owns_registry_value(key) { return Err(Error::MismatchedRegistryKey);
return Err(Error::MismatchedRegistryKey); }
}
let value = unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
assert_stack(self.state, 2); check_stack(self.state, 1)?;
ffi::lua_rawgeti( ffi::lua_rawgeti(
self.state, self.state,
@ -1278,14 +1297,13 @@ impl Lua {
/// [`create_registry_value`]: #method.create_registry_value /// [`create_registry_value`]: #method.create_registry_value
/// [`expire_registry_values`]: #method.expire_registry_values /// [`expire_registry_values`]: #method.expire_registry_values
pub fn remove_registry_value(&self, key: RegistryKey) -> Result<()> { pub fn remove_registry_value(&self, key: RegistryKey) -> Result<()> {
unsafe { if !self.owns_registry_value(&key) {
if !self.owns_registry_value(&key) { return Err(Error::MismatchedRegistryKey);
return Err(Error::MismatchedRegistryKey);
}
ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, key.take());
Ok(())
} }
unsafe {
ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, key.take());
}
Ok(())
} }
/// Returns true if the given `RegistryKey` was created by a `Lua` which shares the underlying /// Returns true if the given `RegistryKey` was created by a `Lua` which shares the underlying
@ -1443,13 +1461,13 @@ impl Lua {
ffi::lua_xmove(extra.ref_thread, self.state, 1); ffi::lua_xmove(extra.ref_thread, self.state, 1);
} }
// Pops the topmost element of the stack and stores a reference to it. This pins the object, // Pops the topmost element of the stack and stores a reference to it. This pins the object,
// preventing garbage collection until the returned `LuaRef` is dropped. // preventing garbage collection until the returned `LuaRef` is dropped.
// //
// References are stored in the stack of a specially created auxiliary thread that exists only // References are stored in the stack of a specially created auxiliary thread that exists only
// to store reference values. This is much faster than storing these in the registry, and also // to store reference values. This is much faster than storing these in the registry, and also
// much more flexible and requires less bookkeeping than storing them directly in the currently // much more flexible and requires less bookkeeping than storing them directly in the currently
// used stack. The implementation is somewhat biased towards the use case of a relatively small // used stack. The implementation is somewhat biased towards the use case of a relatively small
// number of short term references being created, and `RegistryKey` being used for long term // number of short term references being created, and `RegistryKey` being used for long term
// references. // references.
pub(crate) unsafe fn pop_ref(&self) -> LuaRef { pub(crate) unsafe fn pop_ref(&self) -> LuaRef {
@ -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>(); 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 .registered_userdata
.get(&type_id) .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); let _sg = StackGuard::new_extra(self.state, 1);
assert_stack(self.state, 10); check_stack(self.state, 13)?;
let mut fields = StaticUserDataFields::default(); let mut fields = StaticUserDataFields::default();
let mut methods = StaticUserDataMethods::default(); let mut methods = StaticUserDataMethods::default();
@ -1565,13 +1584,14 @@ impl Lua {
ffi::lua_pop(self.state, extra_tables_count); ffi::lua_pop(self.state, extra_tables_count);
let ptr = ffi::lua_topointer(self.state, -1); 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 id = ffi::safe::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)?;
let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned"); let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
extra.registered_userdata.insert(type_id, id); extra.registered_userdata.insert(type_id, id);
extra.registered_userdata_mt.insert(ptr as isize); extra.registered_userdata_mt.insert(ptr as isize);
Ok(id) Ok(())
} }
pub(crate) fn register_userdata_metatable(&self, id: isize) { 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 // Pushes a LuaRef value onto the stack, checking that it's a registered
// and not destructed UserData. // 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<()> { pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<()> {
self.push_ref(lref); self.push_ref(lref);
if ffi::lua_getmetatable(self.state, -1) == 0 { if ffi::lua_getmetatable(self.state, -1) == 0 {
@ -1609,8 +1629,8 @@ impl Lua {
Err(Error::UserDataTypeMismatch) Err(Error::UserDataTypeMismatch)
} }
// Creates a Function out of a Callback containing a 'static Fn. This is safe ONLY because the // Creates a Function out of a Callback containing a 'static Fn. This is safe ONLY because the
// Fn is 'static, otherwise it could capture 'callback arguments improperly. Without ATCs, we // Fn is 'static, otherwise it could capture 'callback arguments improperly. Without ATCs, we
// cannot easily deal with the "correct" callback type of: // cannot easily deal with the "correct" callback type of:
// //
// Box<for<'lua> Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>)> // Box<for<'lua> Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>)>
@ -1663,7 +1683,7 @@ impl Lua {
unsafe { unsafe {
let _sg = StackGuard::new(self.state); 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::<Callback>(self.state, mem::transmute(func))?;
push_gc_userdata(self.state, self.clone())?; push_gc_userdata(self.state, self.clone())?;
@ -1744,8 +1764,8 @@ impl Lua {
let mut waker = noop_waker(); let mut waker = noop_waker();
// Try to get an outer poll waker // Try to get an outer poll waker
ffi::lua_pushlightuserdata(state, &WAKER_REGISTRY_KEY as *const u8 as *mut c_void); let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX); ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key);
if let Some(w) = get_gc_userdata::<Waker>(state, -1).as_ref() { if let Some(w) = get_gc_userdata::<Waker>(state, -1).as_ref() {
waker = (*w).clone(); waker = (*w).clone();
} }
@ -1775,7 +1795,7 @@ impl Lua {
let get_poll = unsafe { let get_poll = unsafe {
let _sg = StackGuard::new(self.state); 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::<AsyncCallback>(self.state, mem::transmute(func))?;
push_gc_userdata(self.state, self.clone())?; push_gc_userdata(self.state, self.clone())?;
@ -1786,7 +1806,7 @@ impl Lua {
let coroutine = self.globals().get::<_, Table>("coroutine")?; 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("get_poll", get_poll)?;
env.set("yield", coroutine.get::<_, Function>("yield")?)?; env.set("yield", coroutine.get::<_, Function>("yield")?)?;
env.set( env.set(
@ -1800,7 +1820,7 @@ impl Lua {
)?; )?;
env.set("pending", unsafe { env.set("pending", unsafe {
let _sg = StackGuard::new(self.state); let _sg = StackGuard::new(self.state);
check_stack(self.state, 5)?; check_stack(self.state, 3)?;
push_gc_userdata(self.state, AsyncPollPending)?; push_gc_userdata(self.state, AsyncPollPending)?;
self.pop_value() self.pop_value()
})?; })?;
@ -1829,12 +1849,10 @@ impl Lua {
T: 'static + UserData, T: 'static + UserData,
{ {
let _sg = StackGuard::new(self.state); 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)?; push_userdata(self.state, data)?;
self.push_userdata_metatable::<T>()?;
ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ud_index as Integer);
ffi::lua_setmetatable(self.state, -2); ffi::lua_setmetatable(self.state, -2);
Ok(AnyUserData(self.pop_ref())) Ok(AnyUserData(self.pop_ref()))
@ -1879,13 +1897,10 @@ impl Lua {
pub(crate) unsafe fn make_from_ptr(state: *mut ffi::lua_State) -> Self { pub(crate) unsafe fn make_from_ptr(state: *mut ffi::lua_State) -> Self {
let _sg = StackGuard::new(state); let _sg = StackGuard::new(state);
assert_stack(state, 3); assert_stack(state, 1);
ffi::lua_rawgetp( let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
state, ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key);
ffi::LUA_REGISTRYINDEX,
&EXTRA_REGISTRY_KEY as *const u8 as *mut c_void,
);
let extra = mlua_expect!( let extra = mlua_expect!(
(*get_gc_userdata::<Weak<Mutex<ExtraData>>>(state, -1)).upgrade(), (*get_gc_userdata::<Weak<Mutex<ExtraData>>>(state, -1)).upgrade(),
"extra is destroyed" "extra is destroyed"
@ -1929,7 +1944,7 @@ pub enum ChunkMode {
impl<'lua, 'a> Chunk<'lua, 'a> { impl<'lua, 'a> Chunk<'lua, 'a> {
/// Sets the name of this chunk, which results in more informative error traces. /// 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 = let name =
CString::new(name.as_ref().to_vec()).map_err(|e| Error::ToLuaConversionError { CString::new(name.as_ref().to_vec()).map_err(|e| Error::ToLuaConversionError {
from: "&str", from: "&str",
@ -1943,7 +1958,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// Sets the first upvalue (`_ENV`) of the loaded chunk to the given value. /// Sets the first upvalue (`_ENV`) of the loaded chunk to the given value.
/// ///
/// Lua main chunks always have exactly one upvalue, and this upvalue is used as the `_ENV` /// Lua main chunks always have exactly one upvalue, and this upvalue is used as the `_ENV`
/// variable inside the chunk. By default this value is set to the global environment. /// variable inside the chunk. By default this value is set to the global environment.
/// ///
/// Calling this method changes the `_ENV` upvalue to the value provided, and variables inside /// Calling this method changes the `_ENV` upvalue to the value provided, and variables inside
/// the chunk will refer to the given environment rather than the global one. /// the chunk will refer to the given environment rather than the global one.
@ -1994,12 +2009,12 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
/// Evaluate the chunk as either an expression or block. /// Evaluate the chunk as either an expression or block.
/// ///
/// If the chunk can be parsed as an expression, this loads and executes the chunk and returns /// If the chunk can be parsed as an expression, this loads and executes the chunk and returns
/// the value that it evaluates to. Otherwise, the chunk is interpreted as a block as normal, /// the value that it evaluates to. Otherwise, the chunk is interpreted as a block as normal,
/// and this is equivalent to calling `exec`. /// and this is equivalent to calling `exec`.
pub fn eval<R: FromLuaMulti<'lua>>(self) -> Result<R> { pub fn eval<R: FromLuaMulti<'lua>>(self) -> Result<R> {
// Bytecode is always interpreted as a statement. // Bytecode is always interpreted as a statement.
// For source code, first try interpreting the lua as an expression by adding // For source code, first try interpreting the lua as an expression by adding
// "return", then as a statement. This is the same thing the // "return", then as a statement. This is the same thing the
// actual lua repl does. // actual lua repl does.
if self.source.starts_with(ffi::LUA_SIGNATURE) { if self.source.starts_with(ffi::LUA_SIGNATURE) {
self.call(()) self.call(())

View File

@ -16,7 +16,8 @@ use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods, AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
}; };
use crate::util::{ 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}; use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
@ -53,7 +54,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
/// Wraps a Rust function or closure, creating a callable Lua function handle to it. /// Wraps a Rust function or closure, creating a callable Lua function handle to it.
/// ///
/// This is a version of [`Lua::create_function`] that creates a callback which expires on /// This is a version of [`Lua::create_function`] that creates a callback which expires on
/// scope drop. See [`Lua::scope`] for more details. /// scope drop. See [`Lua::scope`] for more details.
/// ///
/// [`Lua::create_function`]: struct.Lua.html#method.create_function /// [`Lua::create_function`]: struct.Lua.html#method.create_function
/// [`Lua::scope`]: struct.Lua.html#method.scope /// [`Lua::scope`]: struct.Lua.html#method.scope
@ -65,7 +66,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
{ {
// Safe, because 'scope must outlive 'callback (due to Self containing 'scope), however the // Safe, because 'scope must outlive 'callback (due to Self containing 'scope), however the
// callback itself must be 'scope lifetime, so the function should not be able to capture // callback itself must be 'scope lifetime, so the function should not be able to capture
// anything of 'callback lifetime. 'scope can't be shortened due to being invariant, and // anything of 'callback lifetime. 'scope can't be shortened due to being invariant, and
// the 'callback lifetime here can't be enlarged due to coming from a universal // the 'callback lifetime here can't be enlarged due to coming from a universal
// quantification in Lua::scope. // quantification in Lua::scope.
// //
@ -82,7 +83,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
/// Wraps a Rust mutable closure, creating a callable Lua function handle to it. /// Wraps a Rust mutable closure, creating a callable Lua function handle to it.
/// ///
/// This is a version of [`Lua::create_function_mut`] that creates a callback which expires /// This is a version of [`Lua::create_function_mut`] that creates a callback which expires
/// on scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details. /// on scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details.
/// ///
/// [`Lua::create_function_mut`]: struct.Lua.html#method.create_function_mut /// [`Lua::create_function_mut`]: struct.Lua.html#method.create_function_mut
/// [`Lua::scope`]: struct.Lua.html#method.scope /// [`Lua::scope`]: struct.Lua.html#method.scope
@ -107,7 +108,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it. /// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
/// ///
/// This is a version of [`Lua::create_async_function`] that creates a callback which expires on /// This is a version of [`Lua::create_async_function`] that creates a callback which expires on
/// scope drop. See [`Lua::scope`] and [`Lua::async_scope`] for more details. /// scope drop. See [`Lua::scope`] and [`Lua::async_scope`] for more details.
/// ///
/// Requires `feature = "async"` /// Requires `feature = "async"`
/// ///
@ -188,6 +189,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let state = ud.lua.state; let state = ud.lua.state;
let _sg = StackGuard::new(state); let _sg = StackGuard::new(state);
assert_stack(state, 2); assert_stack(state, 2);
ud.lua.push_ref(&ud); ud.lua.push_ref(&ud);
// We know the destructor has not run yet because we hold a reference to the userdata. // We know the destructor has not run yet because we hold a reference to the userdata.
@ -221,10 +223,10 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
/// ///
/// The main limitation that comes from using non-'static userdata is that the produced userdata /// The main limitation that comes from using non-'static userdata is that the produced userdata
/// will no longer have a `TypeId` associated with it, becuase `TypeId` can only work for /// will no longer have a `TypeId` associated with it, becuase `TypeId` can only work for
/// 'static types. This means that it is impossible, once the userdata is created, to get a /// 'static types. This means that it is impossible, once the userdata is created, to get a
/// reference to it back *out* of an `AnyUserData` handle. This also implies that the /// reference to it back *out* of an `AnyUserData` handle. This also implies that the
/// "function" type methods that can be added via [`UserDataMethods`] (the ones that accept /// "function" type methods that can be added via [`UserDataMethods`] (the ones that accept
/// `AnyUserData` as a first parameter) are vastly less useful. Also, there is no way to re-use /// `AnyUserData` as a first parameter) are vastly less useful. Also, there is no way to re-use
/// a single metatable for multiple non-'static types, so there is a higher cost associated with /// a single metatable for multiple non-'static types, so there is a higher cost associated with
/// creating the userdata metatable each time a new userdata is created. /// creating the userdata metatable each time a new userdata is created.
/// ///
@ -240,7 +242,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
// 'callback outliving 'scope is a lie to make the types work out, required due to the // 'callback outliving 'scope is a lie to make the types work out, required due to the
// inability to work with the more correct callback type that is universally quantified over // inability to work with the more correct callback type that is universally quantified over
// 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua // 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua
// lifetime, so none of the static methods UserData types can add can possibly capture // lifetime, so none of the static methods UserData types can add can possibly capture
// parameters. // parameters.
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>( fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
@ -251,7 +253,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
) -> Result<Function<'lua>> { ) -> Result<Function<'lua>> {
// On methods that actually receive the userdata, we fake a type check on the passed in // On methods that actually receive the userdata, we fake a type check on the passed in
// userdata, where we pretend there is a unique type per call to // userdata, where we pretend there is a unique type per call to
// `Scope::create_nonstatic_userdata`. You can grab a method from a userdata and call // `Scope::create_nonstatic_userdata`. You can grab a method from a userdata and call
// it on a mismatched userdata type, which when using normal 'static userdata will fail // it on a mismatched userdata type, which when using normal 'static userdata will fail
// with a type mismatch, but here without this check would proceed as though you had // with a type mismatch, but here without this check would proceed as though you had
// called the method on the original value (since we otherwise completely ignore the // called the method on the original value (since we otherwise completely ignore the
@ -260,7 +262,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
if let Some(Value::UserData(ud)) = value { if let Some(Value::UserData(ud)) = value {
unsafe { unsafe {
let _sg = StackGuard::new(lua.state); let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 3); check_stack(lua.state, 3)?;
lua.push_userdata_ref(&ud.0)?; lua.push_userdata_ref(&ud.0)?;
if get_userdata(lua.state, -1) == data_ptr { if get_userdata(lua.state, -1) == data_ptr {
return Ok(()); return Ok(());
@ -320,7 +322,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
unsafe { unsafe {
let lua = self.lua; let lua = self.lua;
let _sg = StackGuard::new(lua.state); let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 13); check_stack(lua.state, 13)?;
push_userdata(lua.state, data.clone())?; push_userdata(lua.state, data.clone())?;
let data_ptr = ffi::lua_touserdata(lua.state, -1); 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 state = ud.lua.state;
let _sg = StackGuard::new(state); let _sg = StackGuard::new(state);
assert_stack(state, 2); assert_stack(state, 2);
ud.lua.push_ref(&ud); ud.lua.push_ref(&ud);
// We know the destructor has not run yet because we hold a reference to the userdata. // We know the destructor has not run yet because we hold a reference to the userdata.
@ -437,7 +440,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
// Unsafe, because the callback can improperly capture any value with 'callback scope, such as // Unsafe, because the callback can improperly capture any value with 'callback scope, such as
// improperly capturing an argument. Since the 'callback lifetime is chosen by the user and the // improperly capturing an argument. Since the 'callback lifetime is chosen by the user and the
// lifetime of the callback itself is 'scope (non-'static), the borrow checker will happily pick // lifetime of the callback itself is 'scope (non-'static), the borrow checker will happily pick
// a 'callback that outlives 'scope to allow this. In order for this to be safe, the callback // a 'callback that outlives 'scope to allow this. In order for this to be safe, the callback
// must NOT capture any parameters. // must NOT capture any parameters.
unsafe fn create_callback<'callback>( unsafe fn create_callback<'callback>(
&self, &self,
@ -450,6 +453,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let state = f.lua.state; let state = f.lua.state;
let _sg = StackGuard::new(state); let _sg = StackGuard::new(state);
assert_stack(state, 3); assert_stack(state, 3);
f.lua.push_ref(&f); f.lua.push_ref(&f);
// We know the destructor has not run yet because we hold a reference to the callback. // 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 destructor: DestructorCallback = Box::new(move |f| {
let state = f.lua.state; let state = f.lua.state;
let _sg = StackGuard::new(state); let _sg = StackGuard::new(state);
assert_stack(state, 4); assert_stack(state, 5);
f.lua.push_ref(&f); f.lua.push_ref(&f);
// We know the destructor has not run yet because we hold a reference to the callback. // We know the destructor has not run yet because we hold a reference to the callback.
@ -545,7 +550,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
impl<'lua, 'scope> Drop for Scope<'lua, 'scope> { impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
fn drop(&mut self) { fn drop(&mut self) {
// We separate the action of invalidating the userdata in Lua and actually dropping the // We separate the action of invalidating the userdata in Lua and actually dropping the
// userdata type into two phases. This is so that, in the event a userdata drop panics, we // userdata type into two phases. This is so that, in the event a userdata drop panics, we
// can be sure that all of the userdata in Lua is actually invalidated. // can be sure that all of the userdata in Lua is actually invalidated.
// All destructors are non-panicking, so this is fine // All destructors are non-panicking, so this is fine

View File

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

View File

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

View File

@ -39,17 +39,17 @@ impl StdLib {
#[cfg(any(feature = "luajit", doc))] #[cfg(any(feature = "luajit", doc))]
pub const JIT: StdLib = StdLib(1 << 9); 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"` /// Requires `feature = "luajit"`
#[cfg(any(feature = "luajit", doc))] #[cfg(any(feature = "luajit", doc))]
pub const FFI: StdLib = StdLib(1 << 30); 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); pub const DEBUG: StdLib = StdLib(1 << 31);
/// No libraries /// No libraries
pub const NONE: StdLib = StdLib(0); pub const NONE: StdLib = StdLib(0);
/// (unsafe) All standard libraries /// (**unsafe**) All standard libraries
pub const ALL: StdLib = StdLib(u32::MAX); pub const ALL: StdLib = StdLib(u32::MAX);
/// The safe subset of the standard libraries /// The safe subset of the standard libraries
pub const ALL_SAFE: StdLib = StdLib((1 << 30) - 1); 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::ffi;
use crate::function::Function; use crate::function::Function;
use crate::types::{Integer, LuaRef}; 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}; use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
#[cfg(feature = "async")] #[cfg(feature = "async")]
@ -62,7 +62,7 @@ impl<'lua> Table<'lua> {
unsafe { unsafe {
let _sg = StackGuard::new(lua.state); let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 5); check_stack(lua.state, 6)?;
lua.push_ref(&self.0); lua.push_ref(&self.0);
lua.push_value(key)?; lua.push_value(key)?;
@ -101,7 +101,7 @@ impl<'lua> Table<'lua> {
let value = unsafe { let value = unsafe {
let _sg = StackGuard::new(lua.state); let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 4); check_stack(lua.state, 5)?;
lua.push_ref(&self.0); lua.push_ref(&self.0);
lua.push_value(key)?; lua.push_value(key)?;
@ -119,7 +119,7 @@ impl<'lua> Table<'lua> {
unsafe { unsafe {
let _sg = StackGuard::new(lua.state); let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 4); check_stack(lua.state, 5)?;
lua.push_ref(&self.0); lua.push_ref(&self.0);
lua.push_value(key)?; lua.push_value(key)?;
@ -193,7 +193,7 @@ impl<'lua> Table<'lua> {
unsafe { unsafe {
let _sg = StackGuard::new(lua.state); let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 5); check_stack(lua.state, 6)?;
lua.push_ref(&self.0); lua.push_ref(&self.0);
lua.push_value(key)?; lua.push_value(key)?;
@ -209,7 +209,7 @@ impl<'lua> Table<'lua> {
let value = unsafe { let value = unsafe {
let _sg = StackGuard::new(lua.state); let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 2); check_stack(lua.state, 3)?;
lua.push_ref(&self.0); lua.push_ref(&self.0);
lua.push_value(key)?; lua.push_value(key)?;
@ -232,7 +232,7 @@ impl<'lua> Table<'lua> {
let value = value.to_lua(lua)?; let value = value.to_lua(lua)?;
unsafe { unsafe {
let _sg = StackGuard::new(lua.state); let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 5); check_stack(lua.state, 6)?;
lua.push_ref(&self.0); lua.push_ref(&self.0);
lua.push_value(value)?; lua.push_value(value)?;
@ -258,7 +258,7 @@ impl<'lua> Table<'lua> {
} }
unsafe { unsafe {
let _sg = StackGuard::new(lua.state); let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 4); check_stack(lua.state, 5)?;
lua.push_ref(&self.0); lua.push_ref(&self.0);
ffi::safe::lua_rawremove(lua.state, -1, idx) ffi::safe::lua_rawremove(lua.state, -1, idx)
@ -277,7 +277,7 @@ impl<'lua> Table<'lua> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
let _sg = StackGuard::new(lua.state); let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 4); check_stack(lua.state, 4)?;
lua.push_ref(&self.0); lua.push_ref(&self.0);
ffi::safe::luaL_len(lua.state, -1) 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> { pub fn pairs<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> {
TablePairs { TablePairs {
table: self.0, table: self.0,
next_key: Some(Nil), key: Some(Nil),
_phantom: PhantomData, _phantom: PhantomData,
} }
} }
@ -635,7 +635,7 @@ impl<'lua> Serialize for Table<'lua> {
/// [`Table::pairs`]: struct.Table.html#method.pairs /// [`Table::pairs`]: struct.Table.html#method.pairs
pub struct TablePairs<'lua, K, V> { pub struct TablePairs<'lua, K, V> {
table: LuaRef<'lua>, table: LuaRef<'lua>,
next_key: Option<Value<'lua>>, key: Option<Value<'lua>>,
_phantom: PhantomData<(K, V)>, _phantom: PhantomData<(K, V)>,
} }
@ -647,38 +647,34 @@ where
type Item = Result<(K, V)>; type Item = Result<(K, V)>;
fn next(&mut self) -> Option<Self::Item> { 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 lua = self.table.lua;
let res = (|| { let res = (|| unsafe {
let res = unsafe { let _sg = StackGuard::new(lua.state);
let _sg = StackGuard::new(lua.state); check_stack(lua.state, 5)?;
assert_stack(lua.state, 4);
lua.push_ref(&self.table); lua.push_ref(&self.table);
lua.push_value(next_key)?; lua.push_value(prev_key)?;
if ffi::safe::lua_next(lua.state, -2)? != 0 { if ffi::safe::lua_next(lua.state, -2)? != 0 {
ffi::lua_pushvalue(lua.state, -2); let value = lua.pop_value();
let key = lua.pop_value(); let key = lua.pop_value();
let value = lua.pop_value(); Ok(Some((
self.next_key = Some(lua.pop_value()); key.clone(),
K::from_lua(key, lua)?,
Some((key, value)) V::from_lua(value, lua)?,
} else { )))
None
}
};
Ok(if let Some((key, value)) = res {
Some((K::from_lua(key, lua)?, V::from_lua(value, lua)?))
} else { } else {
None Ok(None)
}) }
})(); })();
match res { 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, Ok(None) => None,
Err(e) => Some(Err(e)), Err(e) => Some(Err(e)),
} }
@ -711,31 +707,29 @@ where
if let Some(index) = self.index.take() { if let Some(index) = self.index.take() {
let lua = self.table.lua; let lua = self.table.lua;
let res = unsafe { let res = (|| unsafe {
let _sg = StackGuard::new(lua.state); 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); lua.push_ref(&self.table);
let res = if self.raw { let res = if self.raw {
Ok(ffi::lua_rawgeti(lua.state, -1, index)) ffi::lua_rawgeti(lua.state, -1, index)
} else { } else {
ffi::safe::lua_geti(lua.state, -1, index) ffi::safe::lua_geti(lua.state, -1, index)?
}; };
match res { match res {
Ok(ffi::LUA_TNIL) if index > self.len.unwrap_or(0) => None, ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None),
Ok(_) => { _ => Ok(Some((index, lua.pop_value()))),
let value = lua.pop_value();
self.index = Some(index + 1);
Some(Ok(value))
}
Err(err) => Some(Err(err)),
} }
}; })();
match res { match res {
Some(Ok(r)) => Some(V::from_lua(r, lua)), Ok(Some((index, r))) => {
Some(Err(err)) => Some(Err(err)), self.index = Some(index + 1);
None => None, Some(V::from_lua(r, lua))
}
Ok(None) => None,
Err(err) => Some(Err(err)),
} }
} else { } else {
None None

View File

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

@ -43,7 +43,7 @@ impl<T> MaybeSend for T {}
/// An auto generated key into the Lua registry. /// An auto generated key into the Lua registry.
/// ///
/// This is a handle to a value stored inside the Lua registry. It is not automatically /// This is a handle to a value stored inside the Lua registry. It is not automatically
/// garbage collected on Drop, but it can be removed with [`Lua::remove_registry_value`], /// garbage collected on Drop, but it can be removed with [`Lua::remove_registry_value`],
/// and instances not manually removed can be garbage collected with [`Lua::expire_registry_values`]. /// and instances not manually removed can be garbage collected with [`Lua::expire_registry_values`].
/// ///

View File

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

View File

@ -16,10 +16,10 @@ static METATABLE_CACHE: Lazy<Mutex<HashMap<TypeId, u8>>> = Lazy::new(|| {
Mutex::new(HashMap::with_capacity(32)) Mutex::new(HashMap::with_capacity(32))
}); });
// Checks that Lua has enough free stack space for future stack operations. On failure, this will // Checks that Lua has enough free stack space for future stack operations. On failure, this will
// panic with an internal error message. // panic with an internal error message.
pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) { pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
// TODO: This should only be triggered when there is a logic error in `mlua`. In the future, // TODO: This should only be triggered when there is a logic error in `mlua`. In the future,
// when there is a way to be confident about stack safety and test it, this could be enabled // when there is a way to be confident about stack safety and test it, this could be enabled
// only when `cfg!(debug_assertions)` is true. // only when `cfg!(debug_assertions)` is true.
mlua_assert!( mlua_assert!(
@ -40,16 +40,27 @@ pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<(
pub struct StackGuard { pub struct StackGuard {
state: *mut ffi::lua_State, state: *mut ffi::lua_State,
top: c_int, top: c_int,
extra: c_int,
} }
impl StackGuard { impl StackGuard {
// Creates a StackGuard instance with wa record of the stack size, and on Drop will check the // Creates a StackGuard instance with wa record of the stack size, and on Drop will check the
// stack size and drop any extra elements. If the stack size at the end is *smaller* than at // stack size and drop any extra elements. If the stack size at the end is *smaller* than at
// the beginning, this is considered a fatal logic error and will result in a panic. // the beginning, this is considered a fatal logic error and will result in a panic.
pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard { pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard {
StackGuard { StackGuard {
state, state,
top: ffi::lua_gettop(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) { fn drop(&mut self) {
unsafe { unsafe {
let top = ffi::lua_gettop(self.state); 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) mlua_panic!("{} too many stack values popped", self.top - top)
} }
if top > self.top { if top > self.top + self.extra {
ffi::lua_settop(self.state, self.top); if self.extra > 0 {
ffi::lua_rotate(self.state, self.top + 1, self.extra);
}
ffi::lua_settop(self.state, self.top + self.extra);
} }
} }
} }
@ -135,7 +149,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
ffi::LUA_ERRERR => { ffi::LUA_ERRERR => {
// This error is raised when the error handler raises an error too many times // This error is raised when the error handler raises an error too many times
// recursively, and continuing to trigger the error handler would cause a stack // recursively, and continuing to trigger the error handler would cause a stack
// overflow. It is not very useful to differentiate between this and "ordinary" // overflow. It is not very useful to differentiate between this and "ordinary"
// runtime errors, so we handle them the same way. // runtime errors, so we handle them the same way.
Error::RuntimeError(err_string) Error::RuntimeError(err_string)
} }
@ -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<()> { 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; let ud = ffi::safe::lua_newuserdata(state, mem::size_of::<T>())? as *mut T;
ptr::write(ud, t); ptr::write(ud, t);
@ -166,19 +180,18 @@ pub unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut
// Uses 1 extra stack space and does not call checkstack. // Uses 1 extra stack space and does not call checkstack.
pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T { pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
// We set the metatable of userdata on __gc to a special table with no __gc method and with // We set the metatable of userdata on __gc to a special table with no __gc method and with
// metamethods that trigger an error on access. We do this so that it will not be double // metamethods that trigger an error on access. We do this so that it will not be double
// dropped, and also so that it cannot be used or identified as any particular userdata type // dropped, and also so that it cannot be used or identified as any particular userdata type
// after the first call to __gc. // after the first call to __gc.
get_destructed_userdata_metatable(state); get_destructed_userdata_metatable(state);
ffi::lua_setmetatable(state, -2); ffi::lua_setmetatable(state, -2);
let ud = ffi::lua_touserdata(state, -1) as *mut T; let ud = get_userdata(state, -1);
mlua_debug_assert!(!ud.is_null(), "userdata pointer is null");
ffi::lua_pop(state, 1); ffi::lua_pop(state, 1);
ptr::read(ud) ptr::read(ud)
} }
// Pushes the userdata and attaches a metatable with __gc method. // 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<()> { pub unsafe fn push_gc_userdata<T: Any>(state: *mut ffi::lua_State, t: T) -> Result<()> {
push_userdata(state, t)?; push_userdata(state, t)?;
get_gc_metatable_for::<T>(state); 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(); return ptr::null_mut();
} }
get_gc_metatable_for::<T>(state); 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); ffi::lua_pop(state, 2);
if !res { if res == 0 {
return ptr::null_mut(); return ptr::null_mut();
} }
ud 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 // (capturing previous one) to lookup in `field_getters` first, then `methods` and falling back to the
// captured `__index` if no matches found. // captured `__index` if no matches found.
// The same is also applicable for `__newindex` metamethod and `field_setters` table. // 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>( pub unsafe fn init_userdata_metatable<T>(
state: *mut ffi::lua_State, state: *mut ffi::lua_State,
metatable: c_int, 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 // 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 // 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 // longjmp) by a C shim. The error or panic is wrapped in such a way that when calling pop_error back
// the rust side, it will resume the panic. // 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 // 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. // 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 // 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 // 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 pub unsafe fn callback_error<F>(state: *mut ffi::lua_State, f: F) -> c_int
where where
F: FnOnce(c_int) -> Result<c_int>, 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] #[no_mangle]
pub unsafe extern "C" fn wrapped_error_traceback( pub unsafe extern "C" fn wrapped_error_traceback(
state: *mut ffi::lua_State, state: *mut ffi::lua_State,
@ -348,6 +364,7 @@ pub unsafe extern "C" fn wrapped_error_traceback(
ffi::lua_setmetatable(state, -2); 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. // 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> { pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua_State> {
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] #[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 // Pushes a WrappedError to the top of the stack.
// lua_checkstack. // Uses 2 stack spaces and does not call checkstack.
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> { 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, // 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 &(*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>( pub unsafe fn init_gc_metatable_for<T: Any>(
state: *mut ffi::lua_State, state: *mut ffi::lua_State,
customize_fn: Option<fn(*mut ffi::lua_State) -> Result<()>>, customize_fn: Option<fn(*mut ffi::lua_State) -> Result<()>>,
) -> Result<*const u8> { ) -> Result<*const u8> {
let type_id = TypeId::of::<T>(); check_stack(state, 6)?;
let type_id = TypeId::of::<T>();
let ref_addr = { let ref_addr = {
let mut mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache"); let mut mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache");
mlua_assert!( 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. // Initialize the error, panic, and destructed userdata metatables.
// Returns address of WrappedError and WrappedPanic metatables in Lua registry. // 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)> { 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 // 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)?; check_stack(state, 3)?;
let err_buf = if let Some(error) = get_wrapped_error(state, -1).as_ref() { let err_buf = if let Some(error) = get_wrapped_error(state, -1).as_ref() {
ffi::lua_pushlightuserdata( let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
state, ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
&ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void,
);
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
let err_buf = ffi::lua_touserdata(state, -1) as *mut String; let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
ffi::lua_pop(state, 2); 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 { unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
callback_error(state, |_| { callback_error(state, |_| {
check_stack(state, 3)?; check_stack(state, 2)?;
let ud = ffi::safe::lua_newuserdata(state, mem::size_of::<WrappedError>())? let error_ud = ffi::safe::lua_newwrappederror(state)? as *mut WrappedError;
as *mut WrappedError; ptr::write(error_ud, WrappedError(Error::CallbackDestructed));
ptr::write(ud, WrappedError(Error::CallbackDestructed));
get_gc_metatable_for::<WrappedError>(state); get_gc_metatable_for::<WrappedError>(state);
ffi::lua_setmetatable(state, -2); ffi::lua_setmetatable(state, -2);
Ok(-1) // to trigger lua_error 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_createtable(state, 0, 26)?;
ffi::safe::lua_pushrclosure(state, destructed_error, 0)?; ffi::safe::lua_pushrclosure(state, destructed_error, 0)?;
for &method in &[ for &method in &[
"__add", "__add",
@ -567,22 +581,14 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const
} }
ffi::lua_pop(state, 1); 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 // Create error print buffer
init_gc_metatable_for::<String>(state, None)?;
ffi::lua_pushlightuserdata(state, &ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void); push_gc_userdata(state, String::new())?;
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
let ud = ffi::safe::lua_newuserdata(state, mem::size_of::<String>())? as *mut String; ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key)?;
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)?;
Ok((wrapped_error_key, wrapped_panic_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) { pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
ffi::lua_pushlightuserdata( let key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
state, ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
&DESTRUCTED_USERDATA_METATABLE as *const u8 as *mut c_void,
);
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
} }
static DESTRUCTED_USERDATA_METATABLE: u8 = 0; static DESTRUCTED_USERDATA_METATABLE: u8 = 0;

View File

@ -16,10 +16,9 @@ use crate::thread::Thread;
use crate::types::{Integer, LightUserData, Number}; use crate::types::{Integer, LightUserData, Number};
use crate::userdata::AnyUserData; use crate::userdata::AnyUserData;
/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData` /// 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 /// 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 /// types between separate `Lua` instances, and doing so will result in a panic.
/// parameter in a Rust callback, and doing so will result in a panic.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Value<'lua> { pub enum Value<'lua> {
/// The Lua value `nil`. /// The Lua value `nil`.
@ -47,7 +46,7 @@ pub enum Value<'lua> {
/// Reference to a userdata object that holds a custom type which implements `UserData`. /// Reference to a userdata object that holds a custom type which implements `UserData`.
/// Special builtin userdata types will be represented as other `Value` variants. /// Special builtin userdata types will be represented as other `Value` variants.
UserData(AnyUserData<'lua>), UserData(AnyUserData<'lua>),
/// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned. /// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
Error(Error), Error(Error),
} }
pub use self::Value::Nil; pub use self::Value::Nil;