diff --git a/src/ffi/safe.rs b/src/ffi/safe.rs index 59f6383..ee70316 100644 --- a/src/ffi/safe.rs +++ b/src/ffi/safe.rs @@ -29,11 +29,13 @@ extern "C" { fn lua_tolstring_s(L: *mut lua_State) -> c_int; fn lua_newthread_s(L: *mut lua_State) -> c_int; fn lua_newuserdata_s(L: *mut lua_State) -> c_int; + fn lua_newwrappederror_s(L: *mut lua_State) -> c_int; fn lua_pushcclosure_s(L: *mut lua_State) -> c_int; fn lua_pushrclosure_s(L: *mut lua_State) -> c_int; fn luaL_requiref_s(L: *mut lua_State) -> c_int; fn error_traceback_s(L: *mut lua_State) -> c_int; + fn lua_newtable_s(L: *mut lua_State) -> c_int; fn lua_createtable_s(L: *mut lua_State) -> c_int; fn lua_gettable_s(L: *mut lua_State) -> c_int; fn lua_settable_s(L: *mut lua_State) -> c_int; @@ -118,21 +120,25 @@ pub unsafe fn lua_newuserdata(state: *mut lua_State, size: usize) -> Result<*mut Ok(super::lua_touserdata(state, -1)) } -// Uses 4 stack spaces -pub unsafe fn lua_pushcclosure(state: *mut lua_State, f: lua_CFunction, n: c_int) -> Result<()> { - super::lua_pushlightuserdata(state, f as *mut c_void); - super::lua_pushinteger(state, n as lua_Integer); - protect_lua(state, n + 2, lua_pushcclosure_s) +// Uses 2 stack spaces +pub unsafe fn lua_newwrappederror(state: *mut lua_State) -> Result<*mut c_void> { + protect_lua(state, 0, lua_newwrappederror_s)?; + Ok(super::lua_touserdata(state, -1)) } -// Uses 4 stack spaces +// Uses 3 stack spaces +pub unsafe fn lua_pushcclosure(state: *mut lua_State, f: lua_CFunction, n: c_int) -> Result<()> { + super::lua_pushlightuserdata(state, f as *mut c_void); + protect_lua(state, n + 1, lua_pushcclosure_s) +} + +// Uses 3 stack spaces pub unsafe fn lua_pushrclosure(state: *mut lua_State, f: lua_CFunction, n: c_int) -> Result<()> { super::lua_pushlightuserdata(state, f as *mut c_void); if n > 0 { super::lua_rotate(state, -n - 1, 1); } - super::lua_pushinteger(state, n as lua_Integer + 1); - protect_lua(state, n + 2, lua_pushrclosure_s) + protect_lua(state, n + 1, lua_pushrclosure_s) } // Uses 5 stack spaces @@ -163,6 +169,11 @@ pub unsafe fn error_traceback2(state: *mut lua_State, state2: *mut lua_State) -> // Table functions // +// Uses 2 stack spaces +pub unsafe fn lua_newtable(state: *mut lua_State) -> Result<()> { + protect_lua(state, 0, lua_newtable_s) +} + // Uses 4 stack spaces pub unsafe fn lua_createtable(state: *mut lua_State, narr: c_int, nrec: c_int) -> Result<()> { super::lua_pushinteger(state, narr as lua_Integer); diff --git a/src/ffi/shim/shim.c b/src/ffi/shim/shim.c index 7c16f52..f88fe2d 100644 --- a/src/ffi/shim/shim.c +++ b/src/ffi/shim/shim.c @@ -135,16 +135,21 @@ int lua_newuserdata_s(lua_State *L) { return 1; } +int lua_newwrappederror_s(lua_State *L) { + lua_newuserdata(L, MLUA_WRAPPED_ERROR_SIZE); + return 1; +} + int lua_pushcclosure_s(lua_State *L) { - lua_CFunction fn = lua_touserdata(L, -2); - lua_Integer n = lua_tointeger(L, -1); - lua_pop(L, 2); + int n = lua_gettop(L) - 1; + lua_CFunction fn = lua_touserdata(L, -1); + lua_pop(L, 1); lua_pushcclosure(L, fn, n); return 1; } int lua_pushrclosure_s(lua_State *L) { - lua_Integer n = lua_popinteger(L); + int n = lua_gettop(L); lua_pushcclosure(L, lua_call_rust, n); return 1; } @@ -162,6 +167,11 @@ int luaL_requiref_s(lua_State *L) { // Table functions // +int lua_newtable_s(lua_State *L) { + lua_createtable(L, 0, 0); + return 1; +} + int lua_createtable_s(lua_State *L) { int nrec = lua_popinteger(L); int narr = lua_popinteger(L); @@ -344,7 +354,7 @@ int meta_newindex_impl(lua_State *state) { return 0; } -// See function::bind +// See Function::bind int bind_call_impl(lua_State *state) { int nargs = lua_gettop(state); int nbinds = lua_tointeger(state, lua_upvalueindex(2)); diff --git a/src/function.rs b/src/function.rs index 150082e..3d37894 100644 --- a/src/function.rs +++ b/src/function.rs @@ -207,13 +207,9 @@ impl<'lua> Function<'lua> { assert_stack(lua.state, 1); lua.push_ref(&self.0); + let data_ptr = &mut data as *mut Vec as *mut c_void; let strip = if strip { 1 } else { 0 }; - ffi::lua_dump( - lua.state, - writer, - &mut data as *mut Vec as *mut c_void, - strip, - ); + ffi::lua_dump(lua.state, writer, data_ptr, strip); ffi::lua_pop(lua.state, 1); } diff --git a/src/hook.rs b/src/hook.rs index f56adfe..17f2361 100644 --- a/src/hook.rs +++ b/src/hook.rs @@ -9,8 +9,8 @@ use crate::util::callback_error; /// Contains information about currently executing Lua code. /// /// 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 code executing at the time that the hook function was called. Further information can be +/// [`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 /// found in the [Lua 5.3 documentaton][lua_doc]. /// /// [lua_doc]: https://www.lua.org/manual/5.3/manual.html#lua_Debug @@ -130,7 +130,7 @@ pub struct HookTriggers { pub on_returns: bool, /// Before executing a new line, or returning from a function call. 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. /// /// # Performance diff --git a/src/lua.rs b/src/lua.rs index a1b1355..ba8cfc9 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -84,9 +84,9 @@ struct MemoryInfo { /// In Lua 5.4 GC can work in two modes: incremental and generational. /// Previous Lua versions support only incremental GC. /// -/// More information can be found in the Lua 5.x [documentation][lua_doc]. +/// More information can be found in the Lua 5.x [documentation]. /// -/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#2.5 +/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5 pub enum GCMode { Incremental, /// Requires `feature = "lua54"` @@ -128,11 +128,15 @@ impl Drop for Lua { } impl Lua { - /// Creates a new Lua state and loads the safe subset of the standard libraries. + /// Creates a new Lua state and loads the **safe** subset of the standard libraries. /// /// # Safety /// The created Lua state would have _some_ safety guarantees and would not allow to load unsafe /// standard libraries or C modules. + /// + /// See [`StdLib`] documentation for a list of unsafe modules that cannot be loaded. + /// + /// [`StdLib`]: struct.StdLib.html #[allow(clippy::new_without_default)] pub fn new() -> Lua { mlua_expect!( @@ -157,6 +161,8 @@ impl Lua { /// The created Lua state would have _some_ safety guarantees and would not allow to load unsafe /// standard libraries or C modules. /// + /// See [`StdLib`] documentation for a list of unsafe modules that cannot be loaded. + /// /// [`StdLib`]: struct.StdLib.html pub fn new_with(libs: StdLib) -> Result { if libs.contains(StdLib::DEBUG) { @@ -188,7 +194,7 @@ impl Lua { /// Use the [`StdLib`] flags to specifiy the libraries you want to load. /// /// # Safety - /// The created Lua state would not have safety guarantees and would allow to load C modules. + /// The created Lua state will not have safety guarantees and allow to load C modules. /// /// [`StdLib`]: struct.StdLib.html pub unsafe fn unsafe_new_with(libs: StdLib) -> Lua { @@ -289,10 +295,18 @@ impl Lua { let main_state = maybe_main_state.unwrap_or(state); let main_state_top = ffi::lua_gettop(main_state); - let (ref_thread, wrapped_error_key, wrapped_panic_key) = mlua_expect!( + let ref_thread = mlua_expect!( (|state| { + // Before initializing the error registry, we must set Error/Panic size. + // Error/Panic keys are not needed during the registry initialization. + ffi::safe::WRAPPED_ERROR_SIZE = mem::size_of::(); + ffi::safe::WRAPPED_PANIC_SIZE = mem::size_of::(); + let (wrapped_error_key, wrapped_panic_key) = init_error_registry(state)?; + ffi::safe::WRAPPED_ERROR_KEY = wrapped_error_key as *const c_void; + ffi::safe::WRAPPED_PANIC_KEY = wrapped_panic_key as *const c_void; + // Create the internal metatables and place them in the registry // to prevent them from being garbage collected. @@ -317,7 +331,7 @@ impl Lua { let ref_thread = ffi::safe::lua_newthread(state)?; ffi::safe::luaL_ref(state, ffi::LUA_REGISTRYINDEX)?; - Ok::<_, Error>((ref_thread, wrapped_error_key, wrapped_panic_key)) + Ok::<_, Error>(ref_thread) })(main_state), "Error during Lua construction", ); @@ -342,12 +356,9 @@ impl Lua { push_gc_userdata(main_state, Arc::downgrade(&extra)), "Error while storing extra data", ); + let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void; mlua_expect!( - ffi::safe::lua_rawsetp( - main_state, - ffi::LUA_REGISTRYINDEX, - &EXTRA_REGISTRY_KEY as *const u8 as *const c_void - ), + ffi::safe::lua_rawsetp(main_state, ffi::LUA_REGISTRYINDEX, extra_key,), "Error while storing extra data" ); @@ -357,11 +368,6 @@ impl Lua { ); assert_stack(main_state, ffi::LUA_MINSTACK); - ffi::safe::WRAPPED_ERROR_SIZE = mem::size_of::(); - ffi::safe::WRAPPED_PANIC_SIZE = mem::size_of::(); - ffi::safe::WRAPPED_ERROR_KEY = wrapped_error_key as *const c_void; - ffi::safe::WRAPPED_PANIC_KEY = wrapped_panic_key as *const c_void; - Lua { state, main_state: maybe_main_state, @@ -448,7 +454,7 @@ impl Lua { /// parameter, see [`HookTriggers`] for more details. /// /// 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 /// erroring once an instruction limit has been reached. /// @@ -571,7 +577,7 @@ impl Lua { /// Perform a full garbage-collection cycle. /// /// 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<()> { let state = self.main_state.unwrap_or(self.state); 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. /// - /// 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. pub fn gc_step_kbytes(&self, kbytes: c_int) -> Result { let state = self.main_state.unwrap_or(self.state); @@ -595,7 +601,7 @@ impl Lua { /// 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]. /// /// [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. /// - /// 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_doc]: https://www.lua.org/manual/5.3/manual.html#2.5 @@ -642,15 +648,8 @@ impl Lua { } #[cfg(feature = "lua54")] - let prev_mode = unsafe { - ffi::lua_gc( - state, - ffi::LUA_GCSETPAUSE, - pause, - step_multiplier, - step_size, - ) - }; + let prev_mode = + unsafe { ffi::lua_gc(state, ffi::LUA_GCINC, pause, step_multiplier, step_size) }; #[cfg(feature = "lua54")] match prev_mode { ffi::LUA_GCINC => GCMode::Incremental, @@ -682,7 +681,7 @@ impl Lua { /// 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 - /// 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. /// /// 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 pub fn load<'lua, 'a, S>(&'lua self, source: &'a S) -> Chunk<'lua, 'a> where - S: ?Sized + AsRef<[u8]>, + S: AsRef<[u8]> + ?Sized, { Chunk { lua: self, @@ -711,7 +710,7 @@ impl Lua { ) -> Result> { unsafe { let _sg = StackGuard::new(self.state); - assert_stack(self.state, 1); + check_stack(self.state, 1)?; let mode_str = match mode { Some(ChunkMode::Binary) if self.safe => { @@ -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]` /// here. pub fn create_string(&self, s: &S) -> Result where - S: ?Sized + AsRef<[u8]>, + S: AsRef<[u8]> + ?Sized, { unsafe { let _sg = StackGuard::new(self.state); - assert_stack(self.state, 4); + check_stack(self.state, 3)?; ffi::safe::lua_pushstring(self.state, s)?; Ok(String(self.pop_ref())) } @@ -768,7 +767,12 @@ impl Lua { /// Creates and returns a new empty table. pub fn create_table(&self) -> Result { - self.create_table_with_capacity(0, 0) + unsafe { + let _sg = StackGuard::new(self.state); + check_stack(self.state, 2)?; + ffi::safe::lua_newtable(self.state)?; + Ok(Table(self.pop_ref())) + } } /// Creates and returns a new empty table, with the specified capacity. @@ -778,14 +782,14 @@ impl Lua { pub fn create_table_with_capacity(&self, narr: c_int, nrec: c_int) -> Result
{ unsafe { let _sg = StackGuard::new(self.state); - assert_stack(self.state, 4); + check_stack(self.state, 4)?; ffi::safe::lua_createtable(self.state, narr, nrec)?; Ok(Table(self.pop_ref())) } } /// Creates a table and fills it with values from an iterator. - pub fn create_table_from<'lua, K, V, I>(&'lua self, cont: I) -> Result> + pub fn create_table_from<'lua, K, V, I>(&'lua self, iter: I) -> Result> where K: ToLua<'lua>, V: ToLua<'lua>, @@ -793,12 +797,12 @@ impl Lua { { unsafe { let _sg = StackGuard::new(self.state); - // `Lua` instance assumes that on any callback, the Lua stack has at least LUA_MINSTACK - // slots available to avoid panics. - check_stack(self.state, 5 + ffi::LUA_MINSTACK)?; + check_stack(self.state, 6)?; - ffi::safe::lua_createtable(self.state, 0, 0)?; - for (k, v) in cont { + let iter = iter.into_iter(); + let lower_bound = iter.size_hint().0; + ffi::safe::lua_createtable(self.state, 0, lower_bound as c_int)?; + for (k, v) in iter { self.push_value(k.to_lua(self)?)?; self.push_value(v.to_lua(self)?)?; ffi::safe::lua_rawset(self.state, -3)?; @@ -809,12 +813,25 @@ impl Lua { } /// Creates a table from an iterator of values, using `1..` as the keys. - pub fn create_sequence_from<'lua, T, I>(&'lua self, cont: I) -> Result> + pub fn create_sequence_from<'lua, T, I>(&'lua self, iter: I) -> Result> where T: ToLua<'lua>, I: IntoIterator, { - 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. @@ -876,7 +893,7 @@ impl Lua { /// 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`]: #method.create_function @@ -966,7 +983,7 @@ impl Lua { pub fn create_thread<'lua>(&'lua self, func: Function<'lua>) -> Result> { unsafe { let _sg = StackGuard::new(self.state); - assert_stack(self.state, 2); + check_stack(self.state, 2)?; let thread_state = ffi::safe::lua_newthread(self.state)?; self.push_ref(&func.0); @@ -1000,7 +1017,7 @@ impl Lua { pub fn globals(&self) -> Table { unsafe { let _sg = StackGuard::new(self.state); - assert_stack(self.state, 2); + assert_stack(self.state, 1); #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS); #[cfg(any(feature = "lua51", feature = "luajit"))] @@ -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. pub fn current_thread(&self) -> Thread { unsafe { + let _sg = StackGuard::new(self.state); + assert_stack(self.state, 1); ffi::lua_pushthread(self.state); Thread(self.pop_ref()) } @@ -1023,19 +1042,19 @@ impl Lua { /// /// 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 - /// 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 + /// 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 /// 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 /// outlive the scope lifetime. /// /// 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). /// 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 /// 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 always be able to smuggle them through Lua state. pub fn scope<'lua, 'scope, R, F>(&'lua self, f: F) -> Result @@ -1078,7 +1097,7 @@ impl Lua { Value::String(s) => Some(s), v => unsafe { let _sg = StackGuard::new(self.state); - assert_stack(self.state, 4); + check_stack(self.state, 5)?; self.push_value(v)?; if !ffi::safe::lua_tolstring(self.state, -1, ptr::null_mut())?.is_null() { @@ -1101,7 +1120,7 @@ impl Lua { Value::Integer(i) => Some(i), v => unsafe { let _sg = StackGuard::new(self.state); - assert_stack(self.state, 2); + check_stack(self.state, 2)?; self.push_value(v)?; let mut isint = 0; @@ -1125,7 +1144,7 @@ impl Lua { Value::Number(n) => Some(n), v => unsafe { let _sg = StackGuard::new(self.state); - assert_stack(self.state, 2); + check_stack(self.state, 2)?; self.push_value(v)?; let mut isnum = 0; @@ -1168,13 +1187,13 @@ impl Lua { /// state. pub fn set_named_registry_value<'lua, S, T>(&'lua self, name: &S, t: T) -> Result<()> where - S: ?Sized + AsRef<[u8]>, + S: AsRef<[u8]> + ?Sized, T: ToLua<'lua>, { let t = t.to_lua(self)?; unsafe { let _sg = StackGuard::new(self.state); - assert_stack(self.state, 5); + check_stack(self.state, 5)?; self.push_value(t)?; ffi::safe::lua_rawsetfield(self.state, ffi::LUA_REGISTRYINDEX, name) @@ -1189,12 +1208,12 @@ impl Lua { /// [`set_named_registry_value`]: #method.set_named_registry_value pub fn named_registry_value<'lua, S, T>(&'lua self, name: &S) -> Result where - S: ?Sized + AsRef<[u8]>, + S: AsRef<[u8]> + ?Sized, T: FromLua<'lua>, { let value = unsafe { let _sg = StackGuard::new(self.state); - assert_stack(self.state, 4); + check_stack(self.state, 3)?; ffi::safe::lua_pushstring(self.state, name)?; ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX); @@ -1211,7 +1230,7 @@ impl Lua { /// [`set_named_registry_value`]: #method.set_named_registry_value pub fn unset_named_registry_value(&self, name: &S) -> Result<()> where - S: ?Sized + AsRef<[u8]>, + S: AsRef<[u8]> + ?Sized, { self.set_named_registry_value(name, Nil) } @@ -1229,7 +1248,7 @@ impl Lua { let t = t.to_lua(self)?; unsafe { let _sg = StackGuard::new(self.state); - assert_stack(self.state, 2); + check_stack(self.state, 4)?; self.push_value(t)?; let registry_id = ffi::safe::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)?; @@ -1250,13 +1269,13 @@ impl Lua { /// /// [`create_registry_value`]: #method.create_registry_value pub fn registry_value<'lua, T: FromLua<'lua>>(&'lua self, key: &RegistryKey) -> Result { - let value = unsafe { - if !self.owns_registry_value(key) { - return Err(Error::MismatchedRegistryKey); - } + if !self.owns_registry_value(key) { + return Err(Error::MismatchedRegistryKey); + } + let value = unsafe { let _sg = StackGuard::new(self.state); - assert_stack(self.state, 2); + check_stack(self.state, 1)?; ffi::lua_rawgeti( self.state, @@ -1278,14 +1297,13 @@ impl Lua { /// [`create_registry_value`]: #method.create_registry_value /// [`expire_registry_values`]: #method.expire_registry_values pub fn remove_registry_value(&self, key: RegistryKey) -> Result<()> { - unsafe { - if !self.owns_registry_value(&key) { - return Err(Error::MismatchedRegistryKey); - } - - ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, key.take()); - Ok(()) + if !self.owns_registry_value(&key) { + return Err(Error::MismatchedRegistryKey); } + 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 @@ -1443,13 +1461,13 @@ impl Lua { 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. // // 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 - // 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 // references. pub(crate) unsafe fn pop_ref(&self) -> LuaRef { @@ -1477,17 +1495,18 @@ impl Lua { } } - pub(crate) unsafe fn userdata_metatable(&self) -> Result { + pub(crate) unsafe fn push_userdata_metatable(&self) -> Result<()> { let type_id = TypeId::of::(); - if let Some(table_id) = mlua_expect!(self.extra.lock(), "extra is poisoned") + if let Some(&table_id) = mlua_expect!(self.extra.lock(), "extra is poisoned") .registered_userdata .get(&type_id) { - return Ok(*table_id); + ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, table_id as Integer); + return Ok(()); } - let _sg = StackGuard::new(self.state); - assert_stack(self.state, 10); + let _sg = StackGuard::new_extra(self.state, 1); + check_stack(self.state, 13)?; let mut fields = StaticUserDataFields::default(); let mut methods = StaticUserDataMethods::default(); @@ -1565,13 +1584,14 @@ impl Lua { ffi::lua_pop(self.state, extra_tables_count); let ptr = ffi::lua_topointer(self.state, -1); + ffi::lua_pushvalue(self.state, -1); let id = ffi::safe::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)?; let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned"); extra.registered_userdata.insert(type_id, id); extra.registered_userdata_mt.insert(ptr as isize); - Ok(id) + Ok(()) } pub(crate) fn register_userdata_metatable(&self, id: isize) { @@ -1586,7 +1606,7 @@ impl Lua { // Pushes a LuaRef value onto the stack, checking that it's a registered // and not destructed UserData. - // Uses 3 stack spaces, does not call checkstack + // Uses 3 stack spaces, does not call checkstack. pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<()> { self.push_ref(lref); if ffi::lua_getmetatable(self.state, -1) == 0 { @@ -1609,8 +1629,8 @@ impl Lua { Err(Error::UserDataTypeMismatch) } - // 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 + // 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 // cannot easily deal with the "correct" callback type of: // // Box Fn(&'lua Lua, MultiValue<'lua>) -> Result>)> @@ -1663,7 +1683,7 @@ impl Lua { unsafe { let _sg = StackGuard::new(self.state); - assert_stack(self.state, 6); + check_stack(self.state, 5)?; push_gc_userdata::(self.state, mem::transmute(func))?; push_gc_userdata(self.state, self.clone())?; @@ -1744,8 +1764,8 @@ impl Lua { let mut waker = noop_waker(); // Try to get an outer poll waker - ffi::lua_pushlightuserdata(state, &WAKER_REGISTRY_KEY as *const u8 as *mut c_void); - ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX); + let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void; + ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key); if let Some(w) = get_gc_userdata::(state, -1).as_ref() { waker = (*w).clone(); } @@ -1775,7 +1795,7 @@ impl Lua { let get_poll = unsafe { let _sg = StackGuard::new(self.state); - assert_stack(self.state, 6); + check_stack(self.state, 5)?; push_gc_userdata::(self.state, mem::transmute(func))?; push_gc_userdata(self.state, self.clone())?; @@ -1786,7 +1806,7 @@ impl Lua { let coroutine = self.globals().get::<_, Table>("coroutine")?; - let env = self.create_table()?; + let env = self.create_table_with_capacity(0, 4)?; env.set("get_poll", get_poll)?; env.set("yield", coroutine.get::<_, Function>("yield")?)?; env.set( @@ -1800,7 +1820,7 @@ impl Lua { )?; env.set("pending", unsafe { let _sg = StackGuard::new(self.state); - check_stack(self.state, 5)?; + check_stack(self.state, 3)?; push_gc_userdata(self.state, AsyncPollPending)?; self.pop_value() })?; @@ -1829,12 +1849,10 @@ impl Lua { T: 'static + UserData, { let _sg = StackGuard::new(self.state); - assert_stack(self.state, 4); + check_stack(self.state, 2)?; - let ud_index = self.userdata_metatable::()?; push_userdata(self.state, data)?; - - ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ud_index as Integer); + self.push_userdata_metatable::()?; ffi::lua_setmetatable(self.state, -2); Ok(AnyUserData(self.pop_ref())) @@ -1879,13 +1897,10 @@ impl Lua { pub(crate) unsafe fn make_from_ptr(state: *mut ffi::lua_State) -> Self { let _sg = StackGuard::new(state); - assert_stack(state, 3); + assert_stack(state, 1); - ffi::lua_rawgetp( - state, - ffi::LUA_REGISTRYINDEX, - &EXTRA_REGISTRY_KEY as *const u8 as *mut c_void, - ); + let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void; + ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key); let extra = mlua_expect!( (*get_gc_userdata::>>(state, -1)).upgrade(), "extra is destroyed" @@ -1929,7 +1944,7 @@ pub enum ChunkMode { impl<'lua, 'a> Chunk<'lua, 'a> { /// Sets the name of this chunk, which results in more informative error traces. - pub fn set_name>(mut self, name: &S) -> Result> { + pub fn set_name + ?Sized>(mut self, name: &S) -> Result> { let name = CString::new(name.as_ref().to_vec()).map_err(|e| Error::ToLuaConversionError { 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. /// /// 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 /// 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. /// /// 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`. pub fn eval>(self) -> Result { // Bytecode is always interpreted as a statement. // 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. if self.source.starts_with(ffi::LUA_SIGNATURE) { self.call(()) diff --git a/src/scope.rs b/src/scope.rs index b0de113..c7a3e48 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -16,7 +16,8 @@ use crate::userdata::{ AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods, }; use crate::util::{ - assert_stack, get_userdata, init_userdata_metatable, push_userdata, take_userdata, StackGuard, + assert_stack, check_stack, get_userdata, init_userdata_metatable, push_userdata, take_userdata, + StackGuard, }; use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value}; @@ -53,7 +54,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { /// 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 - /// 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::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 // 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 // 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. /// /// 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::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. /// /// 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"` /// @@ -188,6 +189,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { let state = ud.lua.state; let _sg = StackGuard::new(state); assert_stack(state, 2); + ud.lua.push_ref(&ud); // We know the destructor has not run yet because we hold a reference to the userdata. @@ -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 /// 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 - /// reference to it back *out* of an `AnyUserData` handle. This also implies that the + /// '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 /// "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 /// 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 // 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 // parameters. fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>( @@ -251,7 +253,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { ) -> Result> { // 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 - // `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 // 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 @@ -260,7 +262,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { if let Some(Value::UserData(ud)) = value { unsafe { let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 3); + check_stack(lua.state, 3)?; lua.push_userdata_ref(&ud.0)?; if get_userdata(lua.state, -1) == data_ptr { return Ok(()); @@ -320,7 +322,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { unsafe { let lua = self.lua; let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 13); + check_stack(lua.state, 13)?; push_userdata(lua.state, data.clone())?; let data_ptr = ffi::lua_touserdata(lua.state, -1); @@ -401,6 +403,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { let state = ud.lua.state; let _sg = StackGuard::new(state); assert_stack(state, 2); + ud.lua.push_ref(&ud); // We know the destructor has not run yet because we hold a reference to the userdata. @@ -437,7 +440,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { // 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 // 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. unsafe fn create_callback<'callback>( &self, @@ -450,6 +453,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { let state = f.lua.state; let _sg = StackGuard::new(state); assert_stack(state, 3); + f.lua.push_ref(&f); // We know the destructor has not run yet because we hold a reference to the callback. @@ -487,7 +491,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { let destructor: DestructorCallback = Box::new(move |f| { let state = f.lua.state; let _sg = StackGuard::new(state); - assert_stack(state, 4); + assert_stack(state, 5); + f.lua.push_ref(&f); // We know the destructor has not run yet because we hold a reference to the callback. @@ -545,7 +550,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { impl<'lua, 'scope> Drop for Scope<'lua, 'scope> { fn drop(&mut self) { // 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. // All destructors are non-panicking, so this is fine diff --git a/src/serde/mod.rs b/src/serde/mod.rs index c0a89ea..e90998e 100644 --- a/src/serde/mod.rs +++ b/src/serde/mod.rs @@ -10,7 +10,7 @@ use crate::ffi; use crate::lua::Lua; use crate::table::Table; use crate::types::LightUserData; -use crate::util::{assert_stack, StackGuard}; +use crate::util::{assert_stack, check_stack, StackGuard}; use crate::value::Value; /// Trait for serializing/deserializing Lua values using Serde. @@ -200,22 +200,22 @@ impl<'lua> LuaSerdeExt<'lua> for Lua { } } +// Uses 6 stack spaces and calls checkstack. pub(crate) unsafe fn init_metatables(state: *mut ffi::lua_State) -> Result<()> { - ffi::lua_pushlightuserdata( - state, - &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void, - ); + check_stack(state, 6)?; + ffi::safe::lua_createtable(state, 0, 1)?; ffi::lua_pushboolean(state, 0); ffi::safe::lua_rawsetfield(state, -2, "__metatable")?; - ffi::safe::lua_rawset(state, ffi::LUA_REGISTRYINDEX) + let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *const c_void; + ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key) } pub(crate) unsafe fn push_array_metatable(state: *mut ffi::lua_State) { - let key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void; - ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key); + let array_metatable_key = &ARRAY_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void; + ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, array_metatable_key); } static ARRAY_METATABLE_REGISTRY_KEY: u8 = 0; diff --git a/src/serde/ser.rs b/src/serde/ser.rs index d919f1d..820f42f 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -9,7 +9,7 @@ use crate::lua::Lua; use crate::string::String; use crate::table::Table; use crate::types::Integer; -use crate::util::{assert_stack, StackGuard}; +use crate::util::{check_stack, StackGuard}; use crate::value::{ToLua, Value}; /// A struct for serializing Rust values into Lua values. @@ -137,7 +137,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> { #[inline] fn serialize_some(self, value: &T) -> Result> where - T: ?Sized + Serialize, + T: Serialize + ?Sized, { value.serialize(self) } @@ -173,7 +173,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> { #[inline] fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result> where - T: ?Sized + Serialize, + T: Serialize + ?Sized, { value.serialize(self) } @@ -187,7 +187,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> { value: &T, ) -> Result> where - T: ?Sized + Serialize, + T: Serialize + ?Sized, { let table = self.lua.create_table()?; let variant = self.lua.create_string(variant)?; @@ -279,13 +279,13 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> { fn serialize_element(&mut self, value: &T) -> Result<()> where - T: ?Sized + Serialize, + T: Serialize + ?Sized, { let lua = self.table.0.lua; let value = lua.to_value_with(value, self.options)?; unsafe { let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 5); + check_stack(lua.state, 6)?; lua.push_ref(&self.table.0); lua.push_value(value)?; @@ -305,7 +305,7 @@ impl<'lua> ser::SerializeTuple for SerializeVec<'lua> { fn serialize_element(&mut self, value: &T) -> Result<()> where - T: ?Sized + Serialize, + T: Serialize + ?Sized, { ser::SerializeSeq::serialize_element(self, value) } @@ -321,7 +321,7 @@ impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> { fn serialize_field(&mut self, value: &T) -> Result<()> where - T: ?Sized + Serialize, + T: Serialize + ?Sized, { ser::SerializeSeq::serialize_element(self, value) } @@ -344,7 +344,7 @@ impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> { fn serialize_field(&mut self, value: &T) -> Result<()> where - T: ?Sized + Serialize, + T: Serialize + ?Sized, { let lua = self.table.0.lua; let idx = self.table.raw_len() + 1; @@ -373,7 +373,7 @@ impl<'lua> ser::SerializeMap for SerializeMap<'lua> { fn serialize_key(&mut self, key: &T) -> Result<()> where - T: ?Sized + Serialize, + T: Serialize + ?Sized, { let lua = self.table.0.lua; self.key = Some(lua.to_value_with(key, self.options)?); @@ -382,7 +382,7 @@ impl<'lua> ser::SerializeMap for SerializeMap<'lua> { fn serialize_value(&mut self, value: &T) -> Result<()> where - T: ?Sized + Serialize, + T: Serialize + ?Sized, { let lua = self.table.0.lua; let key = mlua_expect!( @@ -404,7 +404,7 @@ impl<'lua> ser::SerializeStruct for SerializeMap<'lua> { fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> where - T: ?Sized + Serialize, + T: Serialize + ?Sized, { ser::SerializeMap::serialize_key(self, key)?; ser::SerializeMap::serialize_value(self, value) @@ -428,7 +428,7 @@ impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> { fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> where - T: ?Sized + Serialize, + T: Serialize + ?Sized, { let lua = self.table.0.lua; self.table diff --git a/src/stdlib.rs b/src/stdlib.rs index 9b20f4c..60c13c7 100644 --- a/src/stdlib.rs +++ b/src/stdlib.rs @@ -39,17 +39,17 @@ impl StdLib { #[cfg(any(feature = "luajit", doc))] pub const JIT: StdLib = StdLib(1 << 9); - /// (unsafe) [`ffi`](http://luajit.org/ext_ffi.html) library + /// (**unsafe**) [`ffi`](http://luajit.org/ext_ffi.html) library /// /// Requires `feature = "luajit"` #[cfg(any(feature = "luajit", doc))] pub const FFI: StdLib = StdLib(1 << 30); - /// (unsafe) [`debug`](https://www.lua.org/manual/5.3/manual.html#6.10) library + /// (**unsafe**) [`debug`](https://www.lua.org/manual/5.3/manual.html#6.10) library pub const DEBUG: StdLib = StdLib(1 << 31); /// No libraries pub const NONE: StdLib = StdLib(0); - /// (unsafe) All standard libraries + /// (**unsafe**) All standard libraries pub const ALL: StdLib = StdLib(u32::MAX); /// The safe subset of the standard libraries pub const ALL_SAFE: StdLib = StdLib((1 << 30) - 1); diff --git a/src/table.rs b/src/table.rs index 1e7a5fc..a8e2385 100644 --- a/src/table.rs +++ b/src/table.rs @@ -10,7 +10,7 @@ use crate::error::{Error, Result}; use crate::ffi; use crate::function::Function; use crate::types::{Integer, LuaRef}; -use crate::util::{assert_stack, StackGuard}; +use crate::util::{assert_stack, check_stack, StackGuard}; use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value}; #[cfg(feature = "async")] @@ -62,7 +62,7 @@ impl<'lua> Table<'lua> { unsafe { let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 5); + check_stack(lua.state, 6)?; lua.push_ref(&self.0); lua.push_value(key)?; @@ -101,7 +101,7 @@ impl<'lua> Table<'lua> { let value = unsafe { let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 4); + check_stack(lua.state, 5)?; lua.push_ref(&self.0); lua.push_value(key)?; @@ -119,7 +119,7 @@ impl<'lua> Table<'lua> { unsafe { let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 4); + check_stack(lua.state, 5)?; lua.push_ref(&self.0); lua.push_value(key)?; @@ -193,7 +193,7 @@ impl<'lua> Table<'lua> { unsafe { let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 5); + check_stack(lua.state, 6)?; lua.push_ref(&self.0); lua.push_value(key)?; @@ -209,7 +209,7 @@ impl<'lua> Table<'lua> { let value = unsafe { let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 2); + check_stack(lua.state, 3)?; lua.push_ref(&self.0); lua.push_value(key)?; @@ -232,7 +232,7 @@ impl<'lua> Table<'lua> { let value = value.to_lua(lua)?; unsafe { let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 5); + check_stack(lua.state, 6)?; lua.push_ref(&self.0); lua.push_value(value)?; @@ -258,7 +258,7 @@ impl<'lua> Table<'lua> { } unsafe { let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 4); + check_stack(lua.state, 5)?; lua.push_ref(&self.0); ffi::safe::lua_rawremove(lua.state, -1, idx) @@ -277,7 +277,7 @@ impl<'lua> Table<'lua> { let lua = self.0.lua; unsafe { let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 4); + check_stack(lua.state, 4)?; lua.push_ref(&self.0); ffi::safe::luaL_len(lua.state, -1) @@ -370,7 +370,7 @@ impl<'lua> Table<'lua> { pub fn pairs, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> { TablePairs { table: self.0, - next_key: Some(Nil), + key: Some(Nil), _phantom: PhantomData, } } @@ -635,7 +635,7 @@ impl<'lua> Serialize for Table<'lua> { /// [`Table::pairs`]: struct.Table.html#method.pairs pub struct TablePairs<'lua, K, V> { table: LuaRef<'lua>, - next_key: Option>, + key: Option>, _phantom: PhantomData<(K, V)>, } @@ -647,38 +647,34 @@ where type Item = Result<(K, V)>; fn next(&mut self) -> Option { - if let Some(next_key) = self.next_key.take() { + if let Some(prev_key) = self.key.take() { let lua = self.table.lua; - let res = (|| { - let res = unsafe { - let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 4); + let res = (|| unsafe { + let _sg = StackGuard::new(lua.state); + check_stack(lua.state, 5)?; - lua.push_ref(&self.table); - lua.push_value(next_key)?; + lua.push_ref(&self.table); + lua.push_value(prev_key)?; - if ffi::safe::lua_next(lua.state, -2)? != 0 { - ffi::lua_pushvalue(lua.state, -2); - let key = lua.pop_value(); - let value = lua.pop_value(); - self.next_key = Some(lua.pop_value()); - - Some((key, value)) - } else { - None - } - }; - - Ok(if let Some((key, value)) = res { - Some((K::from_lua(key, lua)?, V::from_lua(value, lua)?)) + if ffi::safe::lua_next(lua.state, -2)? != 0 { + let value = lua.pop_value(); + let key = lua.pop_value(); + Ok(Some(( + key.clone(), + K::from_lua(key, lua)?, + V::from_lua(value, lua)?, + ))) } else { - None - }) + Ok(None) + } })(); match res { - Ok(Some((key, value))) => Some(Ok((key, value))), + Ok(Some((key, ret_key, value))) => { + self.key = Some(key); + Some(Ok((ret_key, value))) + } Ok(None) => None, Err(e) => Some(Err(e)), } @@ -711,31 +707,29 @@ where if let Some(index) = self.index.take() { let lua = self.table.lua; - let res = unsafe { + let res = (|| unsafe { let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 4); + check_stack(lua.state, 1 + if self.raw { 0 } else { 4 })?; lua.push_ref(&self.table); let res = if self.raw { - Ok(ffi::lua_rawgeti(lua.state, -1, index)) + ffi::lua_rawgeti(lua.state, -1, index) } else { - ffi::safe::lua_geti(lua.state, -1, index) + ffi::safe::lua_geti(lua.state, -1, index)? }; match res { - Ok(ffi::LUA_TNIL) if index > self.len.unwrap_or(0) => None, - Ok(_) => { - let value = lua.pop_value(); - self.index = Some(index + 1); - Some(Ok(value)) - } - Err(err) => Some(Err(err)), + ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None), + _ => Ok(Some((index, lua.pop_value()))), } - }; + })(); match res { - Some(Ok(r)) => Some(V::from_lua(r, lua)), - Some(Err(err)) => Some(Err(err)), - None => None, + Ok(Some((index, r))) => { + self.index = Some(index + 1); + Some(V::from_lua(r, lua)) + } + Ok(None) => None, + Err(err) => Some(Err(err)), } } else { None diff --git a/src/thread.rs b/src/thread.rs index edd217e..bfa748b 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -1,3 +1,4 @@ +use std::cmp; use std::os::raw::c_int; use crate::error::{Error, Result}; @@ -107,9 +108,10 @@ impl<'lua> Thread<'lua> { { let lua = self.0.lua; let args = args.to_lua_multi(lua)?; + let nargs = args.len() as c_int; let results = unsafe { let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 2); + check_stack(lua.state, cmp::min(nargs + 1, 3))?; lua.push_ref(&self.0); let thread_state = ffi::lua_tothread(lua.state, -1); @@ -120,10 +122,7 @@ impl<'lua> Thread<'lua> { return Err(Error::CoroutineInactive); } - let nargs = args.len() as c_int; - check_stack(lua.state, nargs)?; - check_stack(thread_state, nargs + 1)?; - + check_stack(thread_state, nargs)?; for arg in args { lua.push_value(arg)?; } @@ -138,10 +137,9 @@ impl<'lua> Thread<'lua> { } let mut results = MultiValue::new(); - check_stack(lua.state, nresults)?; + check_stack(lua.state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below ffi::lua_xmove(thread_state, lua.state, nresults); - assert_stack(lua.state, 2); for _ in 0..nresults { results.push_front(lua.pop_value()); } @@ -314,9 +312,7 @@ fn is_poll_pending(lua: &Lua, val: &MultiValue) -> bool { assert_stack(lua.state, 3); lua.push_ref(&ud.0); - let is_pending = get_gc_userdata::(lua.state, -1) - .as_ref() - .is_some(); + let is_pending = !get_gc_userdata::(lua.state, -1).is_null(); ffi::lua_pop(lua.state, 1); return is_pending; @@ -334,11 +330,11 @@ impl WakerGuard { pub fn new(state: *mut ffi::lua_State, waker: Waker) -> Result { unsafe { let _sg = StackGuard::new(state); - assert_stack(state, 6); + check_stack(state, 5)?; - ffi::lua_pushlightuserdata(state, &WAKER_REGISTRY_KEY as *const u8 as *mut c_void); push_gc_userdata(state, waker)?; - ffi::safe::lua_rawset(state, ffi::LUA_REGISTRYINDEX)?; + let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void; + ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, waker_key)?; Ok(WakerGuard(state)) } @@ -348,14 +344,14 @@ impl WakerGuard { #[cfg(feature = "async")] impl Drop for WakerGuard { fn drop(&mut self) { + let state = self.0; unsafe { - let state = self.0; let _sg = StackGuard::new(state); - assert_stack(state, 2); + assert_stack(state, 1); - ffi::lua_pushlightuserdata(state, &WAKER_REGISTRY_KEY as *const u8 as *mut c_void); ffi::lua_pushnil(state); - ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX); // TODO: make safe + let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void; + ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, waker_key); // TODO: make safe } } } diff --git a/src/types.rs b/src/types.rs index 70d1139..abff804 100644 --- a/src/types.rs +++ b/src/types.rs @@ -43,7 +43,7 @@ impl MaybeSend for T {} /// 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`], /// and instances not manually removed can be garbage collected with [`Lua::expire_registry_values`]. /// diff --git a/src/userdata.rs b/src/userdata.rs index f097130..b4d687d 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -19,8 +19,8 @@ use crate::ffi; use crate::function::Function; use crate::lua::Lua; use crate::table::{Table, TablePairs}; -use crate::types::{Integer, LuaRef, MaybeSend}; -use crate::util::{assert_stack, get_destructed_userdata_metatable, get_userdata, StackGuard}; +use crate::types::{LuaRef, MaybeSend}; +use crate::util::{check_stack, get_destructed_userdata_metatable, get_userdata, StackGuard}; use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti, Value}; /// Kinds of metamethods that can be overridden. @@ -112,7 +112,7 @@ pub enum MetaMethod { Close, /// A custom metamethod. /// - /// Must not be in the protected list: `__gc`, `__metatable`. + /// Must not be in the protected list: `__gc`, `__metatable`, `__mlua*`. Custom(StdString), } @@ -546,6 +546,7 @@ pub trait UserData: Sized { fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(_methods: &mut M) {} } +// Wraps UserData in a way to always implement `serde::Serialize` trait. pub(crate) enum UserDataCell { Arc(Arc>>), Plain(RefCell>), @@ -596,7 +597,7 @@ impl Clone for UserDataCell { fn clone(&self) -> Self { match self { UserDataCell::Arc(t) => UserDataCell::Arc(t.clone()), - UserDataCell::Plain(_) => mlua_panic!("cannot clone plain userdata"), + UserDataCell::Plain(_) => mlua_panic!("cannot clone non-arc userdata"), } } } @@ -711,8 +712,8 @@ impl<'lua> AnyUserData<'lua> { let lua = self.0.lua; #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] let v = { - // Lua 5.2/5.1 allows to store only a table. Then we will wrap the value. - let t = lua.create_table()?; + // Lua <= 5.2 allows to store only a table. Then we will wrap the value. + let t = lua.create_table_with_capacity(1, 0)?; t.raw_set(1, v)?; Value::Table(t) }; @@ -720,10 +721,12 @@ impl<'lua> AnyUserData<'lua> { let v = v.to_lua(lua)?; unsafe { let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 3); + check_stack(lua.state, 3)?; + lua.push_userdata_ref(&self.0)?; lua.push_value(v)?; ffi::lua_setuservalue(lua.state, -2); + Ok(()) } } @@ -737,7 +740,8 @@ impl<'lua> AnyUserData<'lua> { let lua = self.0.lua; let res = unsafe { let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 3); + check_stack(lua.state, 3)?; + lua.push_userdata_ref(&self.0)?; ffi::lua_getuservalue(lua.state, -1); lua.pop_value() @@ -792,13 +796,10 @@ impl<'lua> AnyUserData<'lua> { unsafe { let lua = self.0.lua; let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 3); + check_stack(lua.state, 3)?; lua.push_userdata_ref(&self.0)?; - if ffi::lua_getmetatable(lua.state, -1) == 0 { - return Err(Error::UserDataTypeMismatch); - } - + ffi::lua_getmetatable(lua.state, -1); // Checked that non-empty on the previous call Ok(Table(lua.pop_ref())) } } @@ -829,20 +830,16 @@ impl<'lua> AnyUserData<'lua> { T: 'static + UserData, F: FnOnce(&'a UserDataCell) -> Result, { + let lua = self.0.lua; unsafe { - let lua = self.0.lua; let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 3); + check_stack(lua.state, 3)?; lua.push_ref(&self.0); if ffi::lua_getmetatable(lua.state, -1) == 0 { return Err(Error::UserDataTypeMismatch); } - ffi::lua_rawgeti( - lua.state, - ffi::LUA_REGISTRYINDEX, - lua.userdata_metatable::()? as Integer, - ); + lua.push_userdata_metatable::()?; if ffi::lua_rawequal(lua.state, -1, -2) == 0 { // Maybe UserData destructed? @@ -956,7 +953,7 @@ impl<'lua> Serialize for AnyUserData<'lua> { let res = (|| unsafe { let lua = self.0.lua; let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 3); + check_stack(lua.state, 3)?; lua.push_userdata_ref(&self.0)?; let ud = &*get_userdata::>(lua.state, -1); diff --git a/src/util.rs b/src/util.rs index 0d88b38..df41d2c 100644 --- a/src/util.rs +++ b/src/util.rs @@ -16,10 +16,10 @@ static METATABLE_CACHE: Lazy>> = Lazy::new(|| { 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. 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 // only when `cfg!(debug_assertions)` is true. mlua_assert!( @@ -40,16 +40,27 @@ pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<( pub struct StackGuard { state: *mut ffi::lua_State, top: c_int, + extra: c_int, } impl StackGuard { // 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. pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard { StackGuard { state, top: ffi::lua_gettop(state), + extra: 0, + } + } + + // Similar to `new`, but checks and keeps `extra` elements from top of the stack on Drop. + pub unsafe fn new_extra(state: *mut ffi::lua_State, extra: c_int) -> StackGuard { + StackGuard { + state, + top: ffi::lua_gettop(state), + extra, } } } @@ -58,11 +69,14 @@ impl Drop for StackGuard { fn drop(&mut self) { unsafe { let top = ffi::lua_gettop(self.state); - if top < self.top { + if top < self.top + self.extra { mlua_panic!("{} too many stack values popped", self.top - top) } - if top > self.top { - ffi::lua_settop(self.state, self.top); + if top > self.top + self.extra { + if self.extra > 0 { + ffi::lua_rotate(self.state, self.top + 1, self.extra); + } + ffi::lua_settop(self.state, self.top + self.extra); } } } @@ -135,7 +149,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error { ffi::LUA_ERRERR => { // 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 - // 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. 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(state: *mut ffi::lua_State, t: T) -> Result<()> { let ud = ffi::safe::lua_newuserdata(state, mem::size_of::())? as *mut T; ptr::write(ud, t); @@ -166,19 +180,18 @@ pub unsafe fn get_userdata(state: *mut ffi::lua_State, index: c_int) -> *mut // Uses 1 extra stack space and does not call checkstack. pub unsafe fn take_userdata(state: *mut ffi::lua_State) -> T { // 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 // after the first call to __gc. get_destructed_userdata_metatable(state); ffi::lua_setmetatable(state, -2); - let ud = ffi::lua_touserdata(state, -1) as *mut T; - mlua_debug_assert!(!ud.is_null(), "userdata pointer is null"); + let ud = get_userdata(state, -1); ffi::lua_pop(state, 1); ptr::read(ud) } // Pushes the userdata and attaches a metatable with __gc method. -// Internally uses 4 stack spaces, does not call checkstack. +// Internally uses 3 stack spaces, does not call checkstack. pub unsafe fn push_gc_userdata(state: *mut ffi::lua_State, t: T) -> Result<()> { push_userdata(state, t)?; get_gc_metatable_for::(state); @@ -193,9 +206,9 @@ pub unsafe fn get_gc_userdata(state: *mut ffi::lua_State, index: c_int) return ptr::null_mut(); } get_gc_metatable_for::(state); - let res = ffi::lua_rawequal(state, -1, -2) != 0; + let res = ffi::lua_rawequal(state, -1, -2); ffi::lua_pop(state, 2); - if !res { + if res == 0 { return ptr::null_mut(); } ud @@ -208,7 +221,7 @@ pub unsafe fn get_gc_userdata(state: *mut ffi::lua_State, index: c_int) // (capturing previous one) to lookup in `field_getters` first, then `methods` and falling back to the // captured `__index` if no matches found. // The same is also applicable for `__newindex` metamethod and `field_setters` table. -// Internally uses 8 stack spaces and does not call checkstack. +// Internally uses 9 stack spaces and does not call checkstack. pub unsafe fn init_userdata_metatable( state: *mut ffi::lua_State, metatable: c_int, @@ -277,15 +290,15 @@ pub unsafe extern "C" fn userdata_destructor(state: *mut ffi::lua_State) -> c // In the context of a lua callback, this will call the given function and if the given function // returns an error, *or if the given function panics*, this will result in a call to lua_error (a -// longjmp). The error or panic is wrapped in such a way that when calling pop_error back on -// the rust side, it will resume the panic. +// longjmp) by a C shim. The error or panic is wrapped in such a way that when calling pop_error back +// on the rust side, it will resume the panic (or when popping a panic value from the stack). // // This function assumes the structure of the stack at the beginning of a callback, that the only // elements on the stack are the arguments to the callback. // // This function uses some of the bottom of the stack for error handling, the given callback will be // given the number of arguments available as an argument, and should return the number of returns -// as normal, but cannot assume that the arguments available start at 0. +// as normal, but cannot assume that the arguments available start at 1. pub unsafe fn callback_error(state: *mut ffi::lua_State, f: F) -> c_int where F: FnOnce(c_int) -> Result, @@ -315,6 +328,9 @@ where } } +// A part of the C shim (error_traceback). +// Receives absolute index of error in the stack, a pointer to pre-allocated WrappedError memory, +// and optional boolean flag if a traceback value is on top of the stack. #[no_mangle] pub unsafe extern "C" fn wrapped_error_traceback( state: *mut ffi::lua_State, @@ -348,6 +364,7 @@ pub unsafe extern "C" fn wrapped_error_traceback( ffi::lua_setmetatable(state, -2); } +// Returns Lua main thread for Lua >= 5.2 or checks that the passed thread is main for Lua 5.1. // Does not call lua_checkstack, uses 1 stack space. pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua_State> { #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] @@ -370,10 +387,14 @@ pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua } } -// Pushes a WrappedError to the top of the stack. Uses two stack spaces and does not call -// lua_checkstack. +// Pushes a WrappedError to the top of the stack. +// Uses 2 stack spaces and does not call checkstack. pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> { - push_gc_userdata::(state, WrappedError(err)) + let error_ud = ffi::safe::lua_newwrappederror(state)? as *mut WrappedError; + ptr::write(error_ud, WrappedError(err)); + get_gc_metatable_for::(state); + ffi::lua_setmetatable(state, -2); + Ok(()) } // Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it, @@ -387,13 +408,15 @@ pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *co &(*ud).0 } -// Initialize the internal (with __gc) metatable for a type T +// Initialize the internal (with __gc method) metatable for a type T. +// Uses 6 stack spaces and calls checkstack. pub unsafe fn init_gc_metatable_for( state: *mut ffi::lua_State, customize_fn: Option Result<()>>, ) -> Result<*const u8> { - let type_id = TypeId::of::(); + check_stack(state, 6)?; + let type_id = TypeId::of::(); let ref_addr = { let mut mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache"); mlua_assert!( @@ -433,7 +456,7 @@ pub unsafe fn get_gc_metatable_for(state: *mut ffi::lua_State) { // Initialize the error, panic, and destructed userdata metatables. // Returns address of WrappedError and WrappedPanic metatables in Lua registry. pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const u8, *const u8)> { - assert_stack(state, 8); + check_stack(state, 7)?; // Create error and panic metatables @@ -442,11 +465,8 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const check_stack(state, 3)?; let err_buf = if let Some(error) = get_wrapped_error(state, -1).as_ref() { - ffi::lua_pushlightuserdata( - state, - &ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void, - ); - ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX); + let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void; + ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key); let err_buf = ffi::lua_touserdata(state, -1) as *mut String; ffi::lua_pop(state, 2); @@ -507,22 +527,16 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int { callback_error(state, |_| { - check_stack(state, 3)?; - let ud = ffi::safe::lua_newuserdata(state, mem::size_of::())? - as *mut WrappedError; - ptr::write(ud, WrappedError(Error::CallbackDestructed)); + check_stack(state, 2)?; + let error_ud = ffi::safe::lua_newwrappederror(state)? as *mut WrappedError; + ptr::write(error_ud, WrappedError(Error::CallbackDestructed)); get_gc_metatable_for::(state); ffi::lua_setmetatable(state, -2); Ok(-1) // to trigger lua_error }) } - ffi::lua_pushlightuserdata( - state, - &DESTRUCTED_USERDATA_METATABLE as *const u8 as *mut c_void, - ); ffi::safe::lua_createtable(state, 0, 26)?; - ffi::safe::lua_pushrclosure(state, destructed_error, 0)?; for &method in &[ "__add", @@ -567,22 +581,14 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const } ffi::lua_pop(state, 1); - ffi::safe::lua_rawset(state, ffi::LUA_REGISTRYINDEX)?; + let destructed_metatable_key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void; + ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, destructed_metatable_key)?; // Create error print buffer - - ffi::lua_pushlightuserdata(state, &ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void); - - let ud = ffi::safe::lua_newuserdata(state, mem::size_of::())? as *mut String; - ptr::write(ud, String::new()); - - ffi::safe::lua_createtable(state, 0, 1)?; - ffi::safe::lua_pushrclosure(state, userdata_destructor::, 0)?; - ffi::safe::lua_rawsetfield(state, -2, "__gc")?; - - ffi::lua_setmetatable(state, -2); - - ffi::safe::lua_rawset(state, ffi::LUA_REGISTRYINDEX)?; + init_gc_metatable_for::(state, None)?; + push_gc_userdata(state, String::new())?; + let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void; + ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key)?; Ok((wrapped_error_key, wrapped_panic_key)) } @@ -625,11 +631,8 @@ unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> String { } pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) { - ffi::lua_pushlightuserdata( - state, - &DESTRUCTED_USERDATA_METATABLE as *const u8 as *mut c_void, - ); - ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX); + let key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void; + ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key); } static DESTRUCTED_USERDATA_METATABLE: u8 = 0; diff --git a/src/value.rs b/src/value.rs index ba2e13e..7399844 100644 --- a/src/value.rs +++ b/src/value.rs @@ -16,10 +16,9 @@ use crate::thread::Thread; use crate::types::{Integer, LightUserData, Number}; use crate::userdata::AnyUserData; -/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData` -/// variants contain handle types into the internal Lua state. It is a logic error to mix handle -/// types between separate `Lua` instances, or between a parent `Lua` instance and one received as a -/// parameter in a Rust callback, and doing so will result in a panic. +/// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData` +/// variants contain handle types into the internal Lua state. It is a logic error to mix handle +/// types between separate `Lua` instances, and doing so will result in a panic. #[derive(Debug, Clone)] pub enum Value<'lua> { /// 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`. /// Special builtin userdata types will be represented as other `Value` variants. 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), } pub use self::Value::Nil;