summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ffi/safe.rs23
-rw-r--r--src/ffi/shim/shim.c20
-rw-r--r--src/function.rs8
-rw-r--r--src/hook.rs6
-rw-r--r--src/lua.rs225
-rw-r--r--src/scope.rs35
-rw-r--r--src/serde/mod.rs16
-rw-r--r--src/serde/ser.rs26
-rw-r--r--src/stdlib.rs6
-rw-r--r--src/table.rs96
-rw-r--r--src/thread.rs30
-rw-r--r--src/types.rs2
-rw-r--r--src/userdata.rs39
-rw-r--r--src/util.rs117
-rw-r--r--src/value.rs9
15 files changed, 342 insertions, 316 deletions
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
+// 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 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);
- super::lua_pushinteger(state, n as lua_Integer);
- protect_lua(state, n + 2, lua_pushcclosure_s)
+ protect_lua(state, n + 1, lua_pushcclosure_s)
}
-// Uses 4 stack spaces
+// 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<u8> as *mut c_void;
let strip = if strip { 1 } else { 0 };
- ffi::lua_dump(
- lua.state,
- writer,
- &mut data as *mut Vec<u8> as *mut c_void,
- strip,
- );
+ ffi::lua_dump(lua.state, writer, data_ptr, strip);
ffi::lua_pop(lua.state, 1);
}
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<Lua> {
if libs.contains(StdLib::DEBUG) {
@@ -188,7 +194,7 @@ impl Lua {
/// Use the [`StdLib`] flags to specifiy the libraries you want to load.
///
/// # Safety
- /// The created Lua state would not have safety guarantees and would allow to load C modules.
+ /// The created Lua state will not have safety guarantees and allow to load C modules.
///
/// [`StdLib`]: struct.StdLib.html
pub unsafe fn unsafe_new_with(libs: StdLib) -> Lua {
@@ -289,10 +295,18 @@ impl Lua {
let main_state = maybe_main_state.unwrap_or(state);
let main_state_top = ffi::lua_gettop(main_state);
- let (ref_thread, wrapped_error_key, wrapped_panic_key) = mlua_expect!(
+ let ref_thread = mlua_expect!(
(|state| {
+ // Before initializing the error registry, we must set Error/Panic size.
+ // Error/Panic keys are not needed during the registry initialization.
+ ffi::safe::WRAPPED_ERROR_SIZE = mem::size_of::<WrappedError>();
+ ffi::safe::WRAPPED_PANIC_SIZE = mem::size_of::<WrappedPanic>();
+
let (wrapped_error_key, wrapped_panic_key) = init_error_registry(state)?;
+ ffi::safe::WRAPPED_ERROR_KEY = wrapped_error_key as *const c_void;
+ ffi::safe::WRAPPED_PANIC_KEY = wrapped_panic_key as *const c_void;
+
// Create the internal metatables and place them in the registry
// to prevent them from being garbage collected.
@@ -317,7 +331,7 @@ impl Lua {
let ref_thread = ffi::safe::lua_newthread(state)?;
ffi::safe::luaL_ref(state, ffi::LUA_REGISTRYINDEX)?;
- Ok::<_, Error>((ref_thread, wrapped_error_key, wrapped_panic_key))
+ Ok::<_, Error>(ref_thread)
})(main_state),
"Error during Lua construction",
);
@@ -342,12 +356,9 @@ impl Lua {
push_gc_userdata(main_state, Arc::downgrade(&extra)),
"Error while storing extra data",
);
+ let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
mlua_expect!(
- ffi::safe::lua_rawsetp(
- main_state,
- ffi::LUA_REGISTRYINDEX,
- &EXTRA_REGISTRY_KEY as *const u8 as *const c_void
- ),
+ ffi::safe::lua_rawsetp(main_state, ffi::LUA_REGISTRYINDEX, extra_key,),
"Error while storing extra data"
);
@@ -357,11 +368,6 @@ impl Lua {
);
assert_stack(main_state, ffi::LUA_MINSTACK);
- ffi::safe::WRAPPED_ERROR_SIZE = mem::size_of::<WrappedError>();
- ffi::safe::WRAPPED_PANIC_SIZE = mem::size_of::<WrappedPanic>();
- ffi::safe::WRAPPED_ERROR_KEY = wrapped_error_key as *const c_void;
- ffi::safe::WRAPPED_PANIC_KEY = wrapped_panic_key as *const c_void;
-
Lua {
state,
main_state: maybe_main_state,
@@ -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<bool> {
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<Function<'lua>> {
unsafe {
let _sg = StackGuard::new(self.state);
- assert_stack(self.state, 1);
+ check_stack(self.state, 1)?;
let mode_str = match mode {
Some(ChunkMode::Binary) if self.safe => {
@@ -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<S>(&self, s: &S) -> Result<String>
where
- S: ?Sized + AsRef<[u8]>,
+ S: AsRef<[u8]> + ?Sized,
{
unsafe {
let _sg = StackGuard::new(self.state);
- assert_stack(self.state, 4);
+ check_stack(self.state, 3)?;
ffi::safe::lua_pushstring(self.state, s)?;
Ok(String(self.pop_ref()))
}
@@ -768,7 +767,12 @@ impl Lua {
/// Creates and returns a new empty table.
pub fn create_table(&self) -> Result<Table> {
- self.create_table_with_capacity(0, 0)
+ unsafe {
+ let _sg = StackGuard::new(self.state);
+ check_stack(self.state, 2)?;
+ ffi::safe::lua_newtable(self.state)?;
+ Ok(Table(self.pop_ref()))
+ }
}
/// Creates and returns a new empty table, with the specified capacity.
@@ -778,14 +782,14 @@ impl Lua {
pub fn create_table_with_capacity(&self, narr: c_int, nrec: c_int) -> Result<Table> {
unsafe {
let _sg = StackGuard::new(self.state);
- assert_stack(self.state, 4);
+ check_stack(self.state, 4)?;
ffi::safe::lua_createtable(self.state, narr, nrec)?;
Ok(Table(self.pop_ref()))
}
}
/// Creates a table and fills it with values from an iterator.
- pub fn create_table_from<'lua, K, V, I>(&'lua self, cont: I) -> Result<Table<'lua>>
+ pub fn create_table_from<'lua, K, V, I>(&'lua self, iter: I) -> Result<Table<'lua>>
where
K: ToLua<'lua>,
V: ToLua<'lua>,
@@ -793,12 +797,12 @@ impl Lua {
{
unsafe {
let _sg = StackGuard::new(self.state);
- // `Lua` instance assumes that on any callback, the Lua stack has at least LUA_MINSTACK
- // slots available to avoid panics.
- check_stack(self.state, 5 + ffi::LUA_MINSTACK)?;
+ check_stack(self.state, 6)?;
- ffi::safe::lua_createtable(self.state, 0, 0)?;
- for (k, v) in cont {
+ let iter = iter.into_iter();
+ let lower_bound = iter.size_hint().0;
+ ffi::safe::lua_createtable(self.state, 0, lower_bound as c_int)?;
+ for (k, v) in iter {
self.push_value(k.to_lua(self)?)?;
self.push_value(v.to_lua(self)?)?;
ffi::safe::lua_rawset(self.state, -3)?;
@@ -809,12 +813,25 @@ impl Lua {
}
/// Creates a table from an iterator of values, using `1..` as the keys.
- pub fn create_sequence_from<'lua, T, I>(&'lua self, cont: I) -> Result<Table<'lua>>
+ pub fn create_sequence_from<'lua, T, I>(&'lua self, iter: I) -> Result<Table<'lua>>
where
T: ToLua<'lua>,
I: IntoIterator<Item = T>,
{
- self.create_table_from(cont.into_iter().enumerate().map(|(k, v)| (k + 1, v)))
+ unsafe {
+ let _sg = StackGuard::new(self.state);
+ check_stack(self.state, 6)?;
+
+ let iter = iter.into_iter();
+ let lower_bound = iter.size_hint().0;
+ ffi::safe::lua_createtable(self.state, lower_bound as c_int, 0)?;
+ for (i, v) in iter.enumerate() {
+ self.push_value(v.to_lua(self)?)?;
+ ffi::safe::lua_rawseti(self.state, -2, (i + 1) as Integer)?;
+ }
+
+ Ok(Table(self.pop_ref()))
+ }
}
/// Wraps a Rust function or closure, creating a callable Lua function handle to it.
@@ -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<Thread<'lua>> {
unsafe {
let _sg = StackGuard::new(self.state);
- assert_stack(self.state, 2);
+ check_stack(self.state, 2)?;
let thread_state = ffi::safe::lua_newthread(self.state)?;
self.push_ref(&func.0);
@@ -1000,7 +1017,7 @@ impl Lua {
pub fn globals(&self) -> Table {
unsafe {
let _sg = StackGuard::new(self.state);
- assert_stack(self.state, 2);
+ assert_stack(self.state, 1);
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
#[cfg(any(feature = "lua51", feature = "luajit"))]
@@ -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<R>
@@ -1078,7 +1097,7 @@ impl Lua {
Value::String(s) => Some(s),
v => unsafe {
let _sg = StackGuard::new(self.state);
- assert_stack(self.state, 4);
+ check_stack(self.state, 5)?;
self.push_value(v)?;
if !ffi::safe::lua_tolstring(self.state, -1, ptr::null_mut())?.is_null() {
@@ -1101,7 +1120,7 @@ impl Lua {
Value::Integer(i) => Some(i),
v => unsafe {
let _sg = StackGuard::new(self.state);
- assert_stack(self.state, 2);
+ check_stack(self.state, 2)?;
self.push_value(v)?;
let mut isint = 0;
@@ -1125,7 +1144,7 @@ impl Lua {
Value::Number(n) => Some(n),
v => unsafe {
let _sg = StackGuard::new(self.state);
- assert_stack(self.state, 2);
+ check_stack(self.state, 2)?;
self.push_value(v)?;
let mut isnum = 0;
@@ -1168,13 +1187,13 @@ impl Lua {
/// state.
pub fn set_named_registry_value<'lua, S, T>(&'lua self, name: &S, t: T) -> Result<()>
where
- S: ?Sized + AsRef<[u8]>,
+ S: AsRef<[u8]> + ?Sized,
T: ToLua<'lua>,
{
let t = t.to_lua(self)?;
unsafe {
let _sg = StackGuard::new(self.state);
- assert_stack(self.state, 5);
+ check_stack(self.state, 5)?;
self.push_value(t)?;
ffi::safe::lua_rawsetfield(self.state, ffi::LUA_REGISTRYINDEX, name)
@@ -1189,12 +1208,12 @@ impl Lua {
/// [`set_named_registry_value`]: #method.set_named_registry_value
pub fn named_registry_value<'lua, S, T>(&'lua self, name: &S) -> Result<T>
where
- S: ?Sized + AsRef<[u8]>,
+ S: AsRef<[u8]> + ?Sized,
T: FromLua<'lua>,
{
let value = unsafe {
let _sg = StackGuard::new(self.state);
- assert_stack(self.state, 4);
+ check_stack(self.state, 3)?;
ffi::safe::lua_pushstring(self.state, name)?;
ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX);
@@ -1211,7 +1230,7 @@ impl Lua {
/// [`set_named_registry_value`]: #method.set_named_registry_value
pub fn unset_named_registry_value<S>(&self, name: &S) -> Result<()>
where
- S: ?Sized + AsRef<[u8]>,
+ S: AsRef<[u8]> + ?Sized,
{
self.set_named_registry_value(name, Nil)
}
@@ -1229,7 +1248,7 @@ impl Lua {
let t = t.to_lua(self)?;
unsafe {
let _sg = StackGuard::new(self.state);
- assert_stack(self.state, 2);
+ check_stack(self.state, 4)?;
self.push_value(t)?;
let registry_id = ffi::safe::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)?;
@@ -1250,13 +1269,13 @@ impl Lua {
///
/// [`create_registry_value`]: #method.create_registry_value
pub fn registry_value<'lua, T: FromLua<'lua>>(&'lua self, key: &RegistryKey) -> Result<T> {
- let value = unsafe {
- if !self.owns_registry_value(key) {
- return Err(Error::MismatchedRegistryKey);
- }
+ 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<()> {
+ if !self.owns_registry_value(&key) {
+ return Err(Error::MismatchedRegistryKey);
+ }
unsafe {
- if !self.owns_registry_value(&key) {
- return Err(Error::MismatchedRegistryKey);
- }
-
ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, key.take());
- Ok(())
}
+ Ok(())
}
/// Returns true if the given `RegistryKey` was created by a `Lua` which shares the underlying
@@ -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<T: 'static + UserData>(&self) -> Result<c_int> {
+ pub(crate) unsafe fn push_userdata_metatable<T: 'static + UserData>(&self) -> Result<()> {
let type_id = TypeId::of::<T>();
- if let Some(table_id) = mlua_expect!(self.extra.lock(), "extra is poisoned")
+ if let Some(&table_id) = mlua_expect!(self.extra.lock(), "extra is poisoned")
.registered_userdata
.get(&type_id)
{
- return Ok(*table_id);
+ ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, table_id as Integer);
+ return Ok(());
}
- let _sg = StackGuard::new(self.state);
- assert_stack(self.state, 10);
+ let _sg = StackGuard::new_extra(self.state, 1);
+ check_stack(self.state, 13)?;
let mut fields = StaticUserDataFields::default();
let mut methods = StaticUserDataMethods::default();
@@ -1565,13 +1584,14 @@ impl Lua {
ffi::lua_pop(self.state, extra_tables_count);
let ptr = ffi::lua_topointer(self.state, -1);
+ ffi::lua_pushvalue(self.state, -1);
let id = ffi::safe::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)?;
let mut extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
extra.registered_userdata.insert(type_id, id);
extra.registered_userdata_mt.insert(ptr as isize);
- Ok(id)
+ Ok(())
}
pub(crate) fn register_userdata_metatable(&self, id: isize) {
@@ -1586,7 +1606,7 @@ impl Lua {
// Pushes a LuaRef value onto the stack, checking that it's a registered
// and not destructed UserData.
- // Uses 3 stack spaces, does not call checkstack
+ // Uses 3 stack spaces, does not call checkstack.
pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<()> {
self.push_ref(lref);
if ffi::lua_getmetatable(self.state, -1) == 0 {
@@ -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<for<'lua> Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>)>
@@ -1663,7 +1683,7 @@ impl Lua {
unsafe {
let _sg = StackGuard::new(self.state);
- assert_stack(self.state, 6);
+ check_stack(self.state, 5)?;
push_gc_userdata::<Callback>(self.state, mem::transmute(func))?;
push_gc_userdata(self.state, self.clone())?;
@@ -1744,8 +1764,8 @@ impl Lua {
let mut waker = noop_waker();
// Try to get an outer poll waker
- ffi::lua_pushlightuserdata(state, &WAKER_REGISTRY_KEY as *const u8 as *mut c_void);
- ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
+ let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
+ ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key);
if let Some(w) = get_gc_userdata::<Waker>(state, -1).as_ref() {
waker = (*w).clone();
}
@@ -1775,7 +1795,7 @@ impl Lua {
let get_poll = unsafe {
let _sg = StackGuard::new(self.state);
- assert_stack(self.state, 6);
+ check_stack(self.state, 5)?;
push_gc_userdata::<AsyncCallback>(self.state, mem::transmute(func))?;
push_gc_userdata(self.state, self.clone())?;
@@ -1786,7 +1806,7 @@ impl Lua {
let coroutine = self.globals().get::<_, Table>("coroutine")?;
- let env = self.create_table()?;
+ let env = self.create_table_with_capacity(0, 4)?;
env.set("get_poll", get_poll)?;
env.set("yield", coroutine.get::<_, Function>("yield")?)?;
env.set(
@@ -1800,7 +1820,7 @@ impl Lua {
)?;
env.set("pending", unsafe {
let _sg = StackGuard::new(self.state);
- check_stack(self.state, 5)?;
+ check_stack(self.state, 3)?;
push_gc_userdata(self.state, AsyncPollPending)?;
self.pop_value()
})?;
@@ -1829,12 +1849,10 @@ impl Lua {
T: 'static + UserData,
{
let _sg = StackGuard::new(self.state);
- assert_stack(self.state, 4);
+ check_stack(self.state, 2)?;
- let ud_index = self.userdata_metatable::<T>()?;
push_userdata(self.state, data)?;
-
- ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ud_index as Integer);
+ self.push_userdata_metatable::<T>()?;
ffi::lua_setmetatable(self.state, -2);
Ok(AnyUserData(self.pop_ref()))
@@ -1879,13 +1897,10 @@ impl Lua {
pub(crate) unsafe fn make_from_ptr(state: *mut ffi::lua_State) -> Self {
let _sg = StackGuard::new(state);
- assert_stack(state, 3);
+ assert_stack(state, 1);
- ffi::lua_rawgetp(
- state,
- ffi::LUA_REGISTRYINDEX,
- &EXTRA_REGISTRY_KEY as *const u8 as *mut c_void,
- );
+ let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
+ ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key);
let extra = mlua_expect!(
(*get_gc_userdata::<Weak<Mutex<ExtraData>>>(state, -1)).upgrade(),
"extra is destroyed"
@@ -1929,7 +1944,7 @@ pub enum ChunkMode {
impl<'lua, 'a> Chunk<'lua, 'a> {
/// Sets the name of this chunk, which results in more informative error traces.
- pub fn set_name<S: ?Sized + AsRef<[u8]>>(mut self, name: &S) -> Result<Chunk<'lua, 'a>> {
+ pub fn set_name<S: AsRef<[u8]> + ?Sized>(mut self, name: &S) -> Result<Chunk<'lua, 'a>> {
let name =
CString::new(name.as_ref().to_vec()).map_err(|e| Error::ToLuaConversionError {
from: "&str",
@@ -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<R: FromLuaMulti<'lua>>(self) -> Result<R> {
// 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<Function<'lua>> {
// 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<T>(self, value: &T) -> Result<Value<'lua>>
where
- T: ?Sized + Serialize,
+ T: Serialize + ?Sized,
{
value.serialize(self)
}
@@ -173,7 +173,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
#[inline]
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value<'lua>>
where
- T: ?Sized + Serialize,
+ T: Serialize + ?Sized,
{
value.serialize(self)
}
@@ -187,7 +187,7 @@ impl<'lua> ser::Serializer for Serializer<'lua> {
value: &T,
) -> Result<Value<'lua>>
where
- T: ?Sized + Serialize,
+ T: Serialize + ?Sized,
{
let table = self.lua.create_table()?;
let variant = self.lua.create_string(variant)?;
@@ -279,13 +279,13 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
- T: ?Sized + Serialize,
+ T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
let value = lua.to_value_with(value, self.options)?;
unsafe {
let _sg = StackGuard::new(lua.state);
- assert_stack(lua.state, 5);
+ check_stack(lua.state, 6)?;
lua.push_ref(&self.table.0);
lua.push_value(value)?;
@@ -305,7 +305,7 @@ impl<'lua> ser::SerializeTuple for SerializeVec<'lua> {
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
- T: ?Sized + Serialize,
+ T: Serialize + ?Sized,
{
ser::SerializeSeq::serialize_element(self, value)
}
@@ -321,7 +321,7 @@ impl<'lua> ser::SerializeTupleStruct for SerializeVec<'lua> {
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
- T: ?Sized + Serialize,
+ T: Serialize + ?Sized,
{
ser::SerializeSeq::serialize_element(self, value)
}
@@ -344,7 +344,7 @@ impl<'lua> ser::SerializeTupleVariant for SerializeTupleVariant<'lua> {
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
- T: ?Sized + Serialize,
+ T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
let idx = self.table.raw_len() + 1;
@@ -373,7 +373,7 @@ impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
where
- T: ?Sized + Serialize,
+ T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
self.key = Some(lua.to_value_with(key, self.options)?);
@@ -382,7 +382,7 @@ impl<'lua> ser::SerializeMap for SerializeMap<'lua> {
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
where
- T: ?Sized + Serialize,
+ T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
let key = mlua_expect!(
@@ -404,7 +404,7 @@ impl<'lua> ser::SerializeStruct for SerializeMap<'lua> {
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
where
- T: ?Sized + Serialize,
+ T: Serialize + ?Sized,
{
ser::SerializeMap::serialize_key(self, key)?;
ser::SerializeMap::serialize_value(self, value)
@@ -428,7 +428,7 @@ impl<'lua> ser::SerializeStructVariant for SerializeStructVariant<'lua> {
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
where
- T: ?Sized + Serialize,
+ T: Serialize + ?Sized,
{
let lua = self.table.0.lua;
self.table
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<K: FromLua<'lua>, V: FromLua<'lua>>(self) -> TablePairs<'lua, K, V> {
TablePairs {
table: self.0,
- next_key: Some(Nil),
+ key: Some(Nil),
_phantom: PhantomData,
}
}
@@ -635,7 +635,7 @@ impl<'lua> Serialize for Table<'lua> {
/// [`Table::pairs`]: struct.Table.html#method.pairs
pub struct TablePairs<'lua, K, V> {
table: LuaRef<'lua>,
- next_key: Option<Value<'lua>>,
+ key: Option<Value<'lua>>,
_phantom: PhantomData<(K, V)>,
}
@@ -647,38 +647,34 @@ where
type Item = Result<(K, V)>;
fn next(&mut self) -> Option<Self::Item> {
- if let Some(next_key) = self.next_key.take() {
+ if let Some(prev_key) = self.key.take() {
let lua = self.table.lua;
- let res = (|| {
- let res = unsafe {
- let _sg = StackGuard::new(lua.state);
- assert_stack(lua.state, 4);
-
- lua.push_ref(&self.table);
- lua.push_value(next_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
- }
- };
+ let res = (|| unsafe {
+ let _sg = StackGuard::new(lua.state);
+ check_stack(lua.state, 5)?;
- Ok(if let Some((key, value)) = res {
- Some((K::from_lua(key, lua)?, V::from_lua(value, lua)?))
+ lua.push_ref(&self.table);
+ lua.push_value(prev_key)?;
+
+ 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::<AsyncPollPending>(lua.state, -1)
- .as_ref()
- .is_some();
+ let is_pending = !get_gc_userdata::<AsyncPollPending>(lua.state, -1).is_null();
ffi::lua_pop(lua.state, 1);
return is_pending;
@@ -334,11 +330,11 @@ impl WakerGuard {
pub fn new(state: *mut ffi::lua_State, waker: Waker) -> Result<WakerGuard> {
unsafe {
let _sg = StackGuard::new(state);
- assert_stack(state, 6);
+ check_stack(state, 5)?;
- ffi::lua_pushlightuserdata(state, &WAKER_REGISTRY_KEY as *const u8 as *mut c_void);
push_gc_userdata(state, waker)?;
- ffi::safe::lua_rawset(state, ffi::LUA_REGISTRYINDEX)?;
+ let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
+ ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, waker_key)?;
Ok(WakerGuard(state))
}
@@ -348,14 +344,14 @@ impl WakerGuard {
#[cfg(feature = "async")]
impl Drop for WakerGuard {
fn drop(&mut self) {
+ 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<T> 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<T> {
Arc(Arc<RefCell<UserDataWrapped<T>>>),
Plain(RefCell<UserDataWrapped<T>>),
@@ -596,7 +597,7 @@ impl<T> Clone for UserDataCell<T> {
fn clone(&self) -> Self {
match self {
UserDataCell::Arc(t) => UserDataCell::Arc(t.clone()),
- UserDataCell::Plain(_) => mlua_panic!("cannot clone plain userdata"),
+ UserDataCell::Plain(_) => mlua_panic!("cannot clone non-arc userdata"),
}
}
}
@@ -711,8 +712,8 @@ impl<'lua> AnyUserData<'lua> {
let lua = self.0.lua;
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
let v = {
- // Lua 5.2/5.1 allows to store only a table. Then we will wrap the value.
- let t = lua.create_table()?;
+ // Lua <= 5.2 allows to store only a table. Then we will wrap the value.
+ let t = lua.create_table_with_capacity(1, 0)?;
t.raw_set(1, v)?;
Value::Table(t)
};
@@ -720,10 +721,12 @@ impl<'lua> AnyUserData<'lua> {
let v = v.to_lua(lua)?;
unsafe {
let _sg = StackGuard::new(lua.state);
- assert_stack(lua.state, 3);
+ check_stack(lua.state, 3)?;
+
lua.push_userdata_ref(&self.0)?;
lua.push_value(v)?;
ffi::lua_setuservalue(lua.state, -2);
+
Ok(())
}
}
@@ -737,7 +740,8 @@ impl<'lua> AnyUserData<'lua> {
let lua = self.0.lua;
let res = unsafe {
let _sg = StackGuard::new(lua.state);
- assert_stack(lua.state, 3);
+ check_stack(lua.state, 3)?;
+
lua.push_userdata_ref(&self.0)?;
ffi::lua_getuservalue(lua.state, -1);
lua.pop_value()
@@ -792,13 +796,10 @@ impl<'lua> AnyUserData<'lua> {
unsafe {
let lua = self.0.lua;
let _sg = StackGuard::new(lua.state);
- assert_stack(lua.state, 3);
+ check_stack(lua.state, 3)?;
lua.push_userdata_ref(&self.0)?;
- if ffi::lua_getmetatable(lua.state, -1) == 0 {
- return Err(Error::UserDataTypeMismatch);
- }
-
+ ffi::lua_getmetatable(lua.state, -1); // Checked that non-empty on the previous call
Ok(Table(lua.pop_ref()))
}
}
@@ -829,20 +830,16 @@ impl<'lua> AnyUserData<'lua> {
T: 'static + UserData,
F: FnOnce(&'a UserDataCell<T>) -> Result<R>,
{
+ 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::<T>()? as Integer,
- );
+ lua.push_userdata_metatable::<T>()?;
if ffi::lua_rawequal(lua.state, -1, -2) == 0 {
// Maybe UserData destructed?
@@ -956,7 +953,7 @@ impl<'lua> Serialize for AnyUserData<'lua> {
let res = (|| unsafe {
let lua = self.0.lua;
let _sg = StackGuard::new(lua.state);
- assert_stack(lua.state, 3);
+ check_stack(lua.state, 3)?;
lua.push_userdata_ref(&self.0)?;
let ud = &*get_userdata::<UserDataCell<()>>(lua.state, -1);
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<Mutex<HashMap<TypeId, u8>>> = 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<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
let ud = ffi::safe::lua_newuserdata(state, mem::size_of::<T>())? as *mut T;
ptr::write(ud, t);
@@ -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.
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
- // 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<T: Any>(state: *mut ffi::lua_State, t: T) -> Result<()> {
push_userdata(state, t)?;
get_gc_metatable_for::<T>(state);
@@ -193,9 +206,9 @@ pub unsafe fn get_gc_userdata<T: Any>(state: *mut ffi::lua_State, index: c_int)
return ptr::null_mut();
}
get_gc_metatable_for::<T>(state);
- let res = ffi::lua_rawequal(state, -1, -2) != 0;
+ let res = ffi::lua_rawequal(state, -1, -2);
ffi::lua_pop(state, 2);
- if !res {
+ if res == 0 {
return ptr::null_mut();
}
ud
@@ -208,7 +221,7 @@ pub unsafe fn get_gc_userdata<T: Any>(state: *mut ffi::lua_State, index: c_int)
// (capturing previous one) to lookup in `field_getters` first, then `methods` and falling back to the
// captured `__index` if no matches found.
// The same is also applicable for `__newindex` metamethod and `field_setters` table.
-// Internally uses 8 stack spaces and does not call checkstack.
+// Internally uses 9 stack spaces and does not call checkstack.
pub unsafe fn init_userdata_metatable<T>(
state: *mut ffi::lua_State,
metatable: c_int,
@@ -277,15 +290,15 @@ pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c
// In the context of a lua callback, this will call the given function and if the given function
// returns an error, *or if the given function panics*, this will result in a call to lua_error (a
-// longjmp). The error or panic is wrapped in such a way that when calling pop_error back on
-// the rust side, it will resume the panic.
+// longjmp) by a C shim. The error or panic is wrapped in such a way that when calling pop_error back
+// on the rust side, it will resume the panic (or when popping a panic value from the stack).
//
// This function assumes the structure of the stack at the beginning of a callback, that the only
// elements on the stack are the arguments to the callback.
//
// This function uses some of the bottom of the stack for error handling, the given callback will be
// given the number of arguments available as an argument, and should return the number of returns
-// as normal, but cannot assume that the arguments available start at 0.
+// as normal, but cannot assume that the arguments available start at 1.
pub unsafe fn callback_error<F>(state: *mut ffi::lua_State, f: F) -> c_int
where
F: FnOnce(c_int) -> Result<c_int>,
@@ -315,6 +328,9 @@ where
}
}
+// A part of the C shim (error_traceback).
+// Receives absolute index of error in the stack, a pointer to pre-allocated WrappedError memory,
+// and optional boolean flag if a traceback value is on top of the stack.
#[no_mangle]
pub unsafe extern "C" fn wrapped_error_traceback(
state: *mut ffi::lua_State,
@@ -348,6 +364,7 @@ pub unsafe extern "C" fn wrapped_error_traceback(
ffi::lua_setmetatable(state, -2);
}
+// Returns Lua main thread for Lua >= 5.2 or checks that the passed thread is main for Lua 5.1.
// Does not call lua_checkstack, uses 1 stack space.
pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua_State> {
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
@@ -370,10 +387,14 @@ pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua
}
}
-// Pushes a WrappedError to the top of the stack. Uses two stack spaces and does not call
-// lua_checkstack.
+// Pushes a WrappedError to the top of the stack.
+// Uses 2 stack spaces and does not call checkstack.
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> {
- push_gc_userdata::<WrappedError>(state, WrappedError(err))
+ let error_ud = ffi::safe::lua_newwrappederror(state)? as *mut WrappedError;
+ ptr::write(error_ud, WrappedError(err));
+ get_gc_metatable_for::<WrappedError>(state);
+ ffi::lua_setmetatable(state, -2);
+ Ok(())
}
// Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it,
@@ -387,13 +408,15 @@ pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *co
&(*ud).0
}
-// Initialize the internal (with __gc) metatable for a type T
+// Initialize the internal (with __gc method) metatable for a type T.
+// Uses 6 stack spaces and calls checkstack.
pub unsafe fn init_gc_metatable_for<T: Any>(
state: *mut ffi::lua_State,
customize_fn: Option<fn(*mut ffi::lua_State) -> Result<()>>,
) -> Result<*const u8> {
- let type_id = TypeId::of::<T>();
+ check_stack(state, 6)?;
+ let type_id = TypeId::of::<T>();
let ref_addr = {
let mut mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache");
mlua_assert!(
@@ -433,7 +456,7 @@ pub unsafe fn get_gc_metatable_for<T: Any>(state: *mut ffi::lua_State) {
// Initialize the error, panic, and destructed userdata metatables.
// Returns address of WrappedError and WrappedPanic metatables in Lua registry.
pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const u8, *const u8)> {
- assert_stack(state, 8);
+ check_stack(state, 7)?;
// Create error and panic metatables
@@ -442,11 +465,8 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const
check_stack(state, 3)?;
let err_buf = if let Some(error) = get_wrapped_error(state, -1).as_ref() {
- ffi::lua_pushlightuserdata(
- state,
- &ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void,
- );
- ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
+ let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
+ ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
ffi::lua_pop(state, 2);
@@ -507,22 +527,16 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const
unsafe extern "C" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
callback_error(state, |_| {
- check_stack(state, 3)?;
- let ud = ffi::safe::lua_newuserdata(state, mem::size_of::<WrappedError>())?
- as *mut WrappedError;
- ptr::write(ud, WrappedError(Error::CallbackDestructed));
+ check_stack(state, 2)?;
+ let error_ud = ffi::safe::lua_newwrappederror(state)? as *mut WrappedError;
+ ptr::write(error_ud, WrappedError(Error::CallbackDestructed));
get_gc_metatable_for::<WrappedError>(state);
ffi::lua_setmetatable(state, -2);
Ok(-1) // to trigger lua_error
})
}
- ffi::lua_pushlightuserdata(
- state,
- &DESTRUCTED_USERDATA_METATABLE as *const u8 as *mut c_void,
- );
ffi::safe::lua_createtable(state, 0, 26)?;
-
ffi::safe::lua_pushrclosure(state, destructed_error, 0)?;
for &method in &[
"__add",
@@ -567,22 +581,14 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(*const
}
ffi::lua_pop(state, 1);
- ffi::safe::lua_rawset(state, ffi::LUA_REGISTRYINDEX)?;
+ let destructed_metatable_key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
+ ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, destructed_metatable_key)?;
// Create error print buffer
-
- ffi::lua_pushlightuserdata(state, &ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void);
-
- let ud = ffi::safe::lua_newuserdata(state, mem::size_of::<String>())? as *mut String;
- ptr::write(ud, String::new());
-
- ffi::safe::lua_createtable(state, 0, 1)?;
- ffi::safe::lua_pushrclosure(state, userdata_destructor::<String>, 0)?;
- ffi::safe::lua_rawsetfield(state, -2, "__gc")?;
-
- ffi::lua_setmetatable(state, -2);
-
- ffi::safe::lua_rawset(state, ffi::LUA_REGISTRYINDEX)?;
+ init_gc_metatable_for::<String>(state, None)?;
+ push_gc_userdata(state, String::new())?;
+ let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
+ ffi::safe::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key)?;
Ok((wrapped_error_key, wrapped_panic_key))
}
@@ -625,11 +631,8 @@ unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> String {
}
pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) {
- ffi::lua_pushlightuserdata(
- state,
- &DESTRUCTED_USERDATA_METATABLE as *const u8 as *mut c_void,
- );
- ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
+ let key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
+ ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key);
}
static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
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;