diff options
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | src/function.rs | 60 | ||||
-rw-r--r-- | src/hook.rs | 18 | ||||
-rw-r--r-- | src/lua.rs | 653 | ||||
-rw-r--r-- | src/luau.rs | 10 | ||||
-rw-r--r-- | src/scope.rs | 62 | ||||
-rw-r--r-- | src/serde/ser.rs | 13 | ||||
-rw-r--r-- | src/string.rs | 7 | ||||
-rw-r--r-- | src/table.rs | 140 | ||||
-rw-r--r-- | src/thread.rs | 73 | ||||
-rw-r--r-- | src/types.rs | 20 | ||||
-rw-r--r-- | src/userdata.rs | 71 | ||||
-rw-r--r-- | src/userdata_impl.rs | 52 | ||||
-rw-r--r-- | src/value.rs | 8 |
15 files changed, 656 insertions, 534 deletions
@@ -73,6 +73,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" maplit = "1.0" tempfile = "3" +static_assertions = "1.0" [[bench]] name = "benchmark" @@ -9,7 +9,7 @@ [docs.rs]: https://docs.rs/mlua [Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/master/graph/badge.svg?token=99339FS1CG [codecov.io]: https://codecov.io/gh/khvzak/mlua -[MSRV]: https://img.shields.io/badge/rust-1.56+-brightgreen.svg?&logo=rust +[MSRV]: https://img.shields.io/badge/rust-1.63+-brightgreen.svg?&logo=rust [Guided Tour] | [Benchmarks] | [FAQ] diff --git a/src/function.rs b/src/function.rs index a981007..d5edda4 100644 --- a/src/function.rs +++ b/src/function.rs @@ -95,31 +95,32 @@ impl<'lua> Function<'lua> { /// ``` pub fn call<A: ToLuaMulti<'lua>, R: FromLuaMulti<'lua>>(&self, args: A) -> Result<R> { let lua = self.0.lua; + let state = lua.state(); let mut args = args.to_lua_multi(lua)?; let nargs = args.len() as c_int; let results = unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, nargs + 3)?; + let _sg = StackGuard::new(state); + check_stack(state, nargs + 3)?; - ffi::lua_pushcfunction(lua.state, error_traceback); - let stack_start = ffi::lua_gettop(lua.state); + ffi::lua_pushcfunction(state, error_traceback); + let stack_start = ffi::lua_gettop(state); lua.push_ref(&self.0); for arg in args.drain_all() { lua.push_value(arg)?; } - let ret = ffi::lua_pcall(lua.state, nargs, ffi::LUA_MULTRET, stack_start); + let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start); if ret != ffi::LUA_OK { - return Err(pop_error(lua.state, ret)); + return Err(pop_error(state, ret)); } - let nresults = ffi::lua_gettop(lua.state) - stack_start; + let nresults = ffi::lua_gettop(state) - stack_start; let mut results = args; // Reuse MultiValue container - assert_stack(lua.state, 2); + assert_stack(state, 2); for _ in 0..nresults { results.push_front(lua.pop_value()); } - ffi::lua_pop(lua.state, 1); + ffi::lua_pop(state, 1); results }; R::from_lua_multi(results, lua) @@ -217,6 +218,7 @@ impl<'lua> Function<'lua> { } let lua = self.0.lua; + let state = lua.state(); let args = args.to_lua_multi(lua)?; let nargs = args.len() as c_int; @@ -230,14 +232,14 @@ impl<'lua> Function<'lua> { } let args_wrapper = unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, nargs + 3)?; + let _sg = StackGuard::new(state); + check_stack(state, nargs + 3)?; - ffi::lua_pushinteger(lua.state, nargs as ffi::lua_Integer); + ffi::lua_pushinteger(state, nargs as ffi::lua_Integer); for arg in args { lua.push_value(arg)?; } - protect_lua!(lua.state, nargs + 1, 1, fn(state) { + protect_lua!(state, nargs + 1, 1, fn(state) { ffi::lua_pushcclosure(state, args_wrapper_impl, ffi::lua_gettop(state)); })?; @@ -264,16 +266,17 @@ impl<'lua> Function<'lua> { /// [`lua_getinfo`]: https://www.lua.org/manual/5.4/manual.html#lua_getinfo pub fn info(&self) -> FunctionInfo { let lua = self.0.lua; + let state = lua.state(); unsafe { - let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 1); + let _sg = StackGuard::new(state); + assert_stack(state, 1); let mut ar: ffi::lua_Debug = mem::zeroed(); lua.push_ref(&self.0); #[cfg(not(feature = "luau"))] - let res = ffi::lua_getinfo(lua.state, cstr!(">Sn"), &mut ar); + let res = ffi::lua_getinfo(state, cstr!(">Sn"), &mut ar); #[cfg(feature = "luau")] - let res = ffi::lua_getinfo(lua.state, -1, cstr!("sn"), &mut ar); + let res = ffi::lua_getinfo(state, -1, cstr!("sn"), &mut ar); mlua_assert!(res != 0, "lua_getinfo failed with `>Sn`"); FunctionInfo { @@ -319,15 +322,16 @@ impl<'lua> Function<'lua> { } let lua = self.0.lua; + let state = lua.state(); let mut data: Vec<u8> = Vec::new(); unsafe { - let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 1); + let _sg = StackGuard::new(state); + assert_stack(state, 1); lua.push_ref(&self.0); let data_ptr = &mut data as *mut Vec<u8> as *mut c_void; - ffi::lua_dump(lua.state, writer, data_ptr, strip as i32); - ffi::lua_pop(lua.state, 1); + ffi::lua_dump(state, writer, data_ptr, strip as i32); + ffi::lua_pop(state, 1); } data @@ -375,13 +379,14 @@ impl<'lua> Function<'lua> { } let lua = self.0.lua; + let state = lua.state(); unsafe { - let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 1); + let _sg = StackGuard::new(state); + assert_stack(state, 1); lua.push_ref(&self.0); let func_ptr = &mut func as *mut F as *mut c_void; - ffi::lua_getcoverage(lua.state, -1, func_ptr, callback::<F>); + ffi::lua_getcoverage(state, -1, func_ptr, callback::<F>); } } @@ -397,3 +402,10 @@ impl<'lua> PartialEq for Function<'lua> { self.0 == other.0 } } + +#[cfg(test)] +mod assertions { + use super::*; + + static_assertions::assert_not_impl_any!(Function: Send); +} diff --git a/src/hook.rs b/src/hook.rs index f52fec3..6a0d233 100644 --- a/src/hook.rs +++ b/src/hook.rs @@ -67,12 +67,12 @@ impl<'lua> Debug<'lua> { unsafe { #[cfg(not(feature = "luau"))] mlua_assert!( - ffi::lua_getinfo(self.lua.state, cstr!("n"), self.ar.get()) != 0, + ffi::lua_getinfo(self.lua.state(), cstr!("n"), self.ar.get()) != 0, "lua_getinfo failed with `n`" ); #[cfg(feature = "luau")] mlua_assert!( - ffi::lua_getinfo(self.lua.state, self.level, cstr!("n"), self.ar.get()) != 0, + ffi::lua_getinfo(self.lua.state(), self.level, cstr!("n"), self.ar.get()) != 0, "lua_getinfo failed with `n`" ); @@ -91,12 +91,12 @@ impl<'lua> Debug<'lua> { unsafe { #[cfg(not(feature = "luau"))] mlua_assert!( - ffi::lua_getinfo(self.lua.state, cstr!("S"), self.ar.get()) != 0, + ffi::lua_getinfo(self.lua.state(), cstr!("S"), self.ar.get()) != 0, "lua_getinfo failed with `S`" ); #[cfg(feature = "luau")] mlua_assert!( - ffi::lua_getinfo(self.lua.state, self.level, cstr!("s"), self.ar.get()) != 0, + ffi::lua_getinfo(self.lua.state(), self.level, cstr!("s"), self.ar.get()) != 0, "lua_getinfo failed with `s`" ); @@ -119,12 +119,12 @@ impl<'lua> Debug<'lua> { unsafe { #[cfg(not(feature = "luau"))] mlua_assert!( - ffi::lua_getinfo(self.lua.state, cstr!("l"), self.ar.get()) != 0, + ffi::lua_getinfo(self.lua.state(), cstr!("l"), self.ar.get()) != 0, "lua_getinfo failed with `l`" ); #[cfg(feature = "luau")] mlua_assert!( - ffi::lua_getinfo(self.lua.state, self.level, cstr!("l"), self.ar.get()) != 0, + ffi::lua_getinfo(self.lua.state(), self.level, cstr!("l"), self.ar.get()) != 0, "lua_getinfo failed with `l`" ); @@ -139,7 +139,7 @@ impl<'lua> Debug<'lua> { pub fn is_tail_call(&self) -> bool { unsafe { mlua_assert!( - ffi::lua_getinfo(self.lua.state, cstr!("t"), self.ar.get()) != 0, + ffi::lua_getinfo(self.lua.state(), cstr!("t"), self.ar.get()) != 0, "lua_getinfo failed with `t`" ); (*self.ar.get()).currentline != 0 @@ -151,12 +151,12 @@ impl<'lua> Debug<'lua> { unsafe { #[cfg(not(feature = "luau"))] mlua_assert!( - ffi::lua_getinfo(self.lua.state, cstr!("u"), self.ar.get()) != 0, + ffi::lua_getinfo(self.lua.state(), cstr!("u"), self.ar.get()) != 0, "lua_getinfo failed with `u`" ); #[cfg(feature = "luau")] mlua_assert!( - ffi::lua_getinfo(self.lua.state, self.level, cstr!("a"), self.ar.get()) != 0, + ffi::lua_getinfo(self.lua.state(), self.level, cstr!("a"), self.ar.get()) != 0, "lua_getinfo failed with `a`" ); @@ -5,10 +5,11 @@ use std::ffi::{CStr, CString}; use std::fmt; use std::marker::PhantomData; use std::mem::ManuallyDrop; -use std::ops::{Deref, DerefMut}; +use std::ops::Deref; use std::os::raw::{c_char, c_int, c_void}; use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe, Location}; use std::ptr::NonNull; +use std::sync::atomic::{AtomicPtr, Ordering}; use std::sync::{Arc, Mutex}; use std::{mem, ptr, str}; @@ -67,24 +68,20 @@ use serde::Serialize; /// Top level Lua struct which represents an instance of Lua VM. #[repr(transparent)] -pub struct Lua(Arc<UnsafeCell<LuaInner>>); +pub struct Lua(Arc<LuaInner>); /// An inner Lua struct which holds a raw Lua state. pub struct LuaInner { - pub(crate) state: *mut ffi::lua_State, + // The state is dynamic and depends on context + state: AtomicPtr<ffi::lua_State>, main_state: *mut ffi::lua_State, extra: Arc<UnsafeCell<ExtraData>>, - safe: bool, - #[cfg(feature = "luau")] - compiler: Option<Compiler>, - // Lua has lots of interior mutability, should not be RefUnwindSafe - _no_ref_unwind_safe: PhantomData<UnsafeCell<()>>, } // Data associated with the Lua. pub(crate) struct ExtraData { // Same layout as `Lua` - inner: Option<ManuallyDrop<Arc<UnsafeCell<LuaInner>>>>, + inner: Option<ManuallyDrop<Arc<LuaInner>>>, registered_userdata: FxHashMap<TypeId, c_int>, registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>, @@ -97,6 +94,7 @@ pub(crate) struct ExtraData { #[cfg(feature = "send")] app_data: RefCell<HashMap<TypeId, Box<dyn Any + Send>>>, + safe: bool, libs: StdLib, mem_info: Option<NonNull<MemoryInfo>>, @@ -129,6 +127,8 @@ pub(crate) struct ExtraData { #[cfg(feature = "luau")] sandboxed: bool, + #[cfg(feature = "luau")] + compiler: Option<Compiler>, } #[derive(Default)] @@ -256,10 +256,7 @@ impl Drop for LuaInner { } #[cfg(feature = "luau")] { - let callbacks = ffi::lua_callbacks(self.state); - let extra_ptr = (*callbacks).userdata as *mut Arc<UnsafeCell<ExtraData>>; - drop(Box::from_raw(extra_ptr)); - (*callbacks).userdata = ptr::null_mut(); + (*ffi::lua_callbacks(self.state())).userdata = ptr::null_mut(); } mlua_debug_assert!( ffi::lua_gettop(extra.ref_thread) == extra.ref_stack_top @@ -287,21 +284,16 @@ impl Drop for ExtraData { impl fmt::Debug for Lua { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Lua({:p})", self.state) + write!(f, "Lua({:p})", self.state()) } } impl Deref for Lua { type Target = LuaInner; + #[inline] fn deref(&self) -> &Self::Target { - unsafe { &*(*self.0).get() } - } -} - -impl DerefMut for Lua { - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { &mut *(*self.0).get() } + &self.0 } } @@ -319,7 +311,7 @@ impl Lua { pub fn new() -> Lua { mlua_expect!( Self::new_with(StdLib::ALL_SAFE, LuaOptions::default()), - "can't create new safe Lua state" + "Cannot create new safe Lua state" ) } @@ -346,25 +338,23 @@ impl Lua { #[cfg(not(feature = "luau"))] if libs.contains(StdLib::DEBUG) { return Err(Error::SafetyError( - "the unsafe `debug` module can't be loaded using safe `new_with`".to_string(), + "The unsafe `debug` module can't be loaded using safe `new_with`".to_string(), )); } #[cfg(feature = "luajit")] - { - if libs.contains(StdLib::FFI) { - return Err(Error::SafetyError( - "the unsafe `ffi` module can't be loaded using safe `new_with`".to_string(), - )); - } + if libs.contains(StdLib::FFI) { + return Err(Error::SafetyError( + "The unsafe `ffi` module can't be loaded using safe `new_with`".to_string(), + )); } - let mut lua = unsafe { Self::inner_new(libs, options) }; + let lua = unsafe { Self::inner_new(libs, options) }; #[cfg(not(feature = "luau"))] if libs.contains(StdLib::PACKAGE) { mlua_expect!(lua.disable_c_modules(), "Error during disabling C modules"); } - lua.safe = true; + unsafe { (*lua.extra.get()).safe = true }; Ok(lua) } @@ -383,6 +373,7 @@ impl Lua { Self::inner_new(libs, options) } + /// Creates a new Lua state with required `libs` and `options` unsafe fn inner_new(libs: StdLib, options: LuaOptions) -> Lua { unsafe extern "C" fn allocator( extra_data: *mut c_void, @@ -464,30 +455,29 @@ impl Lua { ffi::lua_pop(state, 1); let lua = Lua::init_from_ptr(state); - let extra = &mut *lua.extra.get(); - extra.mem_info = NonNull::new(mem_info); + (*lua.extra.get()).mem_info = NonNull::new(mem_info); mlua_expect!( load_from_std_lib(state, libs), "Error during loading standard libraries" ); - extra.libs |= libs; + (*lua.extra.get()).libs |= libs; if !options.catch_rust_panics { mlua_expect!( (|| -> Result<()> { - let _sg = StackGuard::new(lua.state); + let _sg = StackGuard::new(state); #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] - ffi::lua_rawgeti(lua.state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS); + ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS); #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] - ffi::lua_pushvalue(lua.state, ffi::LUA_GLOBALSINDEX); + ffi::lua_pushvalue(state, ffi::LUA_GLOBALSINDEX); - ffi::lua_pushcfunction(lua.state, safe_pcall); - rawset_field(lua.state, -2, "pcall")?; + ffi::lua_pushcfunction(state, safe_pcall); + rawset_field(state, -2, "pcall")?; - ffi::lua_pushcfunction(lua.state, safe_xpcall); - rawset_field(lua.state, -2, "xpcall")?; + ffi::lua_pushcfunction(state, safe_xpcall); + rawset_field(state, -2, "xpcall")?; Ok(()) })(), @@ -497,7 +487,8 @@ impl Lua { #[cfg(feature = "async")] if options.thread_cache_size > 0 { - extra.recycled_thread_cache = Vec::with_capacity(options.thread_cache_size); + (*lua.extra.get()).recycled_thread_cache = + Vec::with_capacity(options.thread_cache_size); } #[cfg(feature = "luau")] @@ -513,13 +504,13 @@ impl Lua { #[allow(clippy::missing_safety_doc)] pub unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> Lua { assert!(!state.is_null(), "Lua state is NULL"); - let main_state = get_main_state(state).unwrap_or(state); - let main_state_top = ffi::lua_gettop(main_state); - - if let Some(lua) = Lua::make_from_ptr(state) { + if let Some(lua) = Lua::try_from_ptr(state) { return lua; } + let main_state = get_main_state(state).unwrap_or(state); + let main_state_top = ffi::lua_gettop(main_state); + mlua_expect!( (|state| { init_error_registry(state)?; @@ -550,7 +541,7 @@ impl Lua { // Create ref stack thread and place it in the registry to prevent it from being garbage // collected. let ref_thread = mlua_expect!( - protect_lua!(state, 0, 0, |state| { + protect_lua!(main_state, 0, 0, |state| { let thread = ffi::lua_newthread(state); ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX); thread @@ -559,9 +550,9 @@ impl Lua { ); let wrapped_failure_mt_ptr = { - get_gc_metatable::<WrappedFailure>(state); - let ptr = ffi::lua_topointer(state, -1); - ffi::lua_pop(state, 1); + get_gc_metatable::<WrappedFailure>(main_state); + let ptr = ffi::lua_topointer(main_state, -1); + ffi::lua_pop(main_state, 1); ptr }; @@ -577,16 +568,16 @@ impl Lua { let ref_stack_top = ffi::lua_gettop(ref_thread); // Create ExtraData - let extra = Arc::new(UnsafeCell::new(ExtraData { inner: None, registered_userdata: FxHashMap::default(), registered_userdata_mt: FxHashMap::default(), registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))), app_data: RefCell::new(HashMap::new()), - ref_thread, + safe: false, libs: StdLib::NONE, mem_info: None, + ref_thread, // We need 1 extra stack space to move values in and out of the ref stack. ref_stack_size: ffi::LUA_MINSTACK - 1, ref_stack_top, @@ -606,8 +597,11 @@ impl Lua { interrupt_callback: None, #[cfg(feature = "luau")] sandboxed: false, + #[cfg(feature = "luau")] + compiler: None, })); + // Store it in the registry mlua_expect!( (|state| { push_gc_userdata(state, Arc::clone(&extra), true)?; @@ -638,19 +632,14 @@ impl Lua { // We can use global callbacks userdata since we don't allow C modules in Luau #[cfg(feature = "luau")] { - let extra_raw = Box::into_raw(Box::new(Arc::clone(&extra))); - (*ffi::lua_callbacks(main_state)).userdata = extra_raw as *mut c_void; + (*ffi::lua_callbacks(main_state)).userdata = extra.get() as *mut c_void; } - let inner = Arc::new(UnsafeCell::new(LuaInner { - state, + let inner = Arc::new(LuaInner { + state: AtomicPtr::new(state), main_state, extra: Arc::clone(&extra), - safe: false, - #[cfg(feature = "luau")] - compiler: None, - _no_ref_unwind_safe: PhantomData, - })); + }); (*extra.get()).inner = Some(ManuallyDrop::new(Arc::clone(&inner))); #[cfg(not(feature = "module"))] @@ -666,32 +655,32 @@ impl Lua { /// [`StdLib`]: crate::StdLib pub fn load_from_std_lib(&self, libs: StdLib) -> Result<()> { #[cfg(not(feature = "luau"))] - if self.safe && libs.contains(StdLib::DEBUG) { + let is_safe = unsafe { (*self.extra.get()).safe }; + + #[cfg(not(feature = "luau"))] + if is_safe && libs.contains(StdLib::DEBUG) { return Err(Error::SafetyError( "the unsafe `debug` module can't be loaded in safe mode".to_string(), )); } #[cfg(feature = "luajit")] - { - if self.safe && libs.contains(StdLib::FFI) { - return Err(Error::SafetyError( - "the unsafe `ffi` module can't be loaded in safe mode".to_string(), - )); - } + if is_safe && libs.contains(StdLib::FFI) { + return Err(Error::SafetyError( + "the unsafe `ffi` module can't be loaded in safe mode".to_string(), + )); } let res = unsafe { load_from_std_lib(self.main_state, libs) }; // If `package` library loaded into a safe lua state then disable C modules - let extra = unsafe { &mut *self.extra.get() }; #[cfg(not(feature = "luau"))] { - let curr_libs = extra.libs; - if self.safe && (curr_libs ^ (curr_libs | libs)).contains(StdLib::PACKAGE) { + let curr_libs = unsafe { (*self.extra.get()).libs }; + if is_safe && (curr_libs ^ (curr_libs | libs)).contains(StdLib::PACKAGE) { mlua_expect!(self.disable_c_modules(), "Error during disabling C modules"); } } - extra.libs |= libs; + unsafe { (*self.extra.get()).libs |= libs }; res } @@ -715,10 +704,11 @@ impl Lua { where T: FromLua<'lua>, { + let state = self.state(); let loaded = unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 2)?; - protect_lua!(self.state, 0, 1, fn(state) { + let _sg = StackGuard::new(state); + check_stack(state, 2)?; + protect_lua!(state, 0, 1, fn(state) { ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED")); })?; Table(self.pop_ref()) @@ -747,10 +737,11 @@ impl Lua { /// /// [`package.loaded`]: https://www.lua.org/manual/5.4/manual.html#pdf-package.loaded pub fn unload(&self, modname: &str) -> Result<()> { + let state = self.state(); let loaded = unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 2)?; - protect_lua!(self.state, 0, 1, fn(state) { + let _sg = StackGuard::new(state); + check_stack(state, 2)?; + protect_lua!(state, 0, 1, fn(state) { ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED")); })?; Table(self.pop_ref()) @@ -798,8 +789,9 @@ impl Lua { F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>, { let entrypoint_inner = |lua: &'lua Lua, func: F| { - let nargs = ffi::lua_gettop(lua.state); - check_stack(lua.state, 3)?; + let state = lua.state(); + let nargs = ffi::lua_gettop(state); + check_stack(state, 3)?; let mut args = MultiValue::new(); args.reserve(nargs as usize); @@ -822,7 +814,7 @@ impl Lua { } Err(err) => { self.push_value(Value::Error(err))?; - let state = self.state; + let state = self.state(); // Lua (self) must be dropped before triggering longjmp drop(self); ffi::lua_error(state) @@ -873,8 +865,7 @@ impl Lua { #[cfg_attr(docsrs, doc(cfg(feature = "luau")))] pub fn sandbox(&self, enabled: bool) -> Result<()> { unsafe { - let extra = &mut *self.extra.get(); - if extra.sandboxed != enabled { + if (*self.extra.get()).sandboxed != enabled { let state = self.main_state; check_stack(state, 3)?; protect_lua!(state, 0, 0, |state| { @@ -888,7 +879,7 @@ impl Lua { ffi::luaL_sandbox(state, 0); } })?; - extra.sandboxed = enabled; + (*self.extra.get()).sandboxed = enabled; } Ok(()) } @@ -934,14 +925,14 @@ impl Lua { F: 'static + MaybeSend + Fn(&Lua, Debug) -> Result<()>, { unsafe extern "C" fn hook_proc(state: *mut ffi::lua_State, ar: *mut ffi::lua_Debug) { - let lua = match Lua::make_from_ptr(state) { + let lua = match Lua::try_from_ptr(state) { Some(lua) => lua, None => return, }; let extra = lua.extra.get(); callback_error_ext(state, extra, move |_| { let debug = Debug::new(&lua, ar); - let hook_cb = (*lua.extra.get()).hook_callback.clone(); + let hook_cb = (*extra).hook_callback.clone(); let hook_cb = mlua_expect!(hook_cb, "no hook callback set in hook_proc"); if Arc::strong_count(&hook_cb) > 2 { return Ok(()); // Don't allow recursion @@ -1028,10 +1019,10 @@ impl Lua { // We don't support GC interrupts since they cannot survive Lua exceptions return; } - let extra = match extra_data(state) { - Some(e) => e.get(), - None => return, - }; + let extra = extra_data(state); + if extra.is_null() { + return; + } let result = callback_error_ext(state, extra, move |_| { let interrupt_cb = (*extra).interrupt_callback.clone(); let interrupt_cb = @@ -1077,15 +1068,11 @@ impl Lua { F: 'static + MaybeSend + Fn(&Lua, &CStr, bool) -> Result<()>, { unsafe extern "C" fn warn_proc(ud: *mut c_void, msg: *const c_char, tocont: c_int) { - let state = ud as *mut ffi::lua_State; - let lua = match Lua::make_from_ptr(state) { - Some(lua) => lua, - None => return, - }; - let extra = lua.extra.get(); - callback_error_ext(state, extra, move |_| { + let extra = ud as *mut ExtraData; + let lua: &Lua = mem::transmute((*extra).inner.as_ref().unwrap()); + callback_error_ext(lua.state(), extra, |_| { let cb = mlua_expect!( - (*lua.extra.get()).warn_callback.as_ref(), + (*extra).warn_callback.as_ref(), "no warning callback set in warn_proc" ); let msg = CStr::from_ptr(msg); @@ -1096,7 +1083,7 @@ impl Lua { let state = self.main_state; unsafe { (*self.extra.get()).warn_callback = Some(Box::new(callback)); - ffi::lua_setwarnf(state, Some(warn_proc), state as *mut c_void); + ffi::lua_setwarnf(state, Some(warn_proc), self.extra.get() as *mut c_void); } } @@ -1123,7 +1110,7 @@ impl Lua { #[cfg_attr(docsrs, doc(cfg(feature = "lua54")))] pub fn warning<S: Into<Vec<u8>>>(&self, msg: S, tocont: bool) -> Result<()> { let msg = CString::new(msg).map_err(|err| Error::RuntimeError(err.to_string()))?; - unsafe { ffi::lua_warning(self.state, msg.as_ptr(), tocont as c_int) }; + unsafe { ffi::lua_warning(self.state(), msg.as_ptr(), tocont as c_int) }; Ok(()) } @@ -1139,11 +1126,11 @@ impl Lua { let mut ar: ffi::lua_Debug = mem::zeroed(); let level = level as c_int; #[cfg(not(feature = "luau"))] - if ffi::lua_getstack(self.state, level, &mut ar) == 0 { + if ffi::lua_getstack(self.state(), level, &mut ar) == 0 { return None; } #[cfg(feature = "luau")] - if ffi::lua_getinfo(self.state, level, cstr!(""), &mut ar) == 0 { + if ffi::lua_getinfo(self.state(), level, cstr!(""), &mut ar) == 0 { return None; } Some(Debug::new_owned(self, level, ar)) @@ -1350,7 +1337,7 @@ impl Lua { #[cfg(any(feature = "luau", doc))] #[cfg_attr(docsrs, doc(cfg(feature = "luau")))] pub fn set_compiler(&self, compiler: Compiler) { - unsafe { (*self.0.get()).compiler = Some(compiler) }; + unsafe { (*self.extra.get()).compiler = Some(compiler) }; } /// Returns Lua source code as a `Chunk` builder type. @@ -1375,7 +1362,7 @@ impl Lua { env: chunk.env(self), mode: chunk.mode(), #[cfg(feature = "luau")] - compiler: self.compiler.clone(), + compiler: unsafe { (*self.extra.get()).compiler.clone() }, } } @@ -1386,9 +1373,10 @@ impl Lua { env: Option<Value<'lua>>, mode: Option<ChunkMode>, ) -> Result<Function<'lua>> { + let state = self.state(); unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 1)?; + let _sg = StackGuard::new(state); + check_stack(state, 1)?; let mode_str = match mode { Some(ChunkMode::Binary) => cstr!("b"), @@ -1397,7 +1385,7 @@ impl Lua { }; match ffi::luaL_loadbufferx( - self.state, + state, source.as_ptr() as *const c_char, source.len(), name.map(|n| n.as_ptr()).unwrap_or_else(ptr::null), @@ -1407,13 +1395,13 @@ impl Lua { if let Some(env) = env { self.push_value(env)?; #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] - ffi::lua_setupvalue(self.state, -2, 1); + ffi::lua_setupvalue(state, -2, 1); #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] - ffi::lua_setfenv(self.state, -2); + ffi::lua_setfenv(state, -2); } Ok(Function(self.pop_ref())) } - err => Err(pop_error(self.state, err)), + err => Err(pop_error(state, err)), } } } @@ -1422,16 +1410,16 @@ impl Lua { /// embedded nulls, so in addition to `&str` and `&String`, you can also pass plain `&[u8]` /// here. pub fn create_string(&self, s: impl AsRef<[u8]>) -> Result<String> { + let state = self.state(); unsafe { if self.unlikely_memory_error() { push_string(self.ref_thread(), s.as_ref(), false)?; return Ok(String(self.pop_ref_thread())); } - let _sg = StackGuard::new(self.state); - check_stack(self.state, 3)?; - - push_string(self.state, s.as_ref(), true)?; + let _sg = StackGuard::new(state); + check_stack(state, 3)?; + push_string(state, s.as_ref(), true)?; Ok(String(self.pop_ref())) } } @@ -1446,16 +1434,16 @@ impl Lua { /// `nrec` is a hint for how many other elements the table will have. /// Lua may use these hints to preallocate memory for the new table. pub fn create_table_with_capacity(&self, narr: c_int, nrec: c_int) -> Result<Table> { + let state = self.state(); unsafe { if self.unlikely_memory_error() { push_table(self.ref_thread(), narr, nrec, false)?; return Ok(Table(self.pop_ref_thread())); } - let _sg = StackGuard::new(self.state); - check_stack(self.state, 3)?; - - push_table(self.state, narr, nrec, true)?; + let _sg = StackGuard::new(state); + check_stack(state, 3)?; + push_table(state, narr, nrec, true)?; Ok(Table(self.pop_ref())) } } @@ -1467,21 +1455,22 @@ impl Lua { V: ToLua<'lua>, I: IntoIterator<Item = (K, V)>, { + let state = self.state(); unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 6)?; + let _sg = StackGuard::new(state); + check_stack(state, 6)?; let iter = iter.into_iter(); let lower_bound = iter.size_hint().0; let protect = !self.unlikely_memory_error(); - push_table(self.state, 0, lower_bound as c_int, protect)?; + push_table(state, 0, lower_bound as c_int, protect)?; for (k, v) in iter { self.push_value(k.to_lua(self)?)?; self.push_value(v.to_lua(self)?)?; if protect { - protect_lua!(self.state, 3, 1, fn(state) ffi::lua_rawset(state, -3))?; + protect_lua!(state, 3, 1, fn(state) ffi::lua_rawset(state, -3))?; } else { - ffi::lua_rawset(self.state, -3); + ffi::lua_rawset(state, -3); } } @@ -1495,22 +1484,23 @@ impl Lua { T: ToLua<'lua>, I: IntoIterator<Item = T>, { + let state = self.state(); unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 5)?; + let _sg = StackGuard::new(state); + check_stack(state, 5)?; let iter = iter.into_iter(); let lower_bound = iter.size_hint().0; let protect = !self.unlikely_memory_error(); - push_table(self.state, lower_bound as c_int, 0, protect)?; + push_table(state, lower_bound as c_int, 0, protect)?; for (i, v) in iter.enumerate() { self.push_value(v.to_lua(self)?)?; if protect { - protect_lua!(self.state, 2, 1, |state| { + protect_lua!(state, 2, 1, |state| { ffi::lua_rawseti(state, -2, (i + 1) as Integer); })?; } else { - ffi::lua_rawseti(self.state, -2, (i + 1) as Integer); + ffi::lua_rawseti(state, -2, (i + 1) as Integer); } } @@ -1599,8 +1589,9 @@ impl Lua { /// # Safety /// This function is unsafe because provides a way to execute unsafe C function. pub unsafe fn create_c_function(&self, func: ffi::lua_CFunction) -> Result<Function> { - check_stack(self.state, 1)?; - ffi::lua_pushcfunction(self.state, func); + let state = self.state(); + check_stack(state, 1)?; + ffi::lua_pushcfunction(state, func); Ok(Function(self.pop_ref())) } @@ -1666,17 +1657,18 @@ impl Lua { /// /// Equivalent to `coroutine.create`. pub fn create_thread<'lua>(&'lua self, func: Function<'lua>) -> Result<Thread<'lua>> { + let state = self.state(); unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 3)?; + let _sg = StackGuard::new(state); + check_stack(state, 3)?; let thread_state = if self.unlikely_memory_error() { - ffi::lua_newthread(self.state) + ffi::lua_newthread(state) } else { - protect_lua!(self.state, 0, 1, |state| ffi::lua_newthread(state))? + protect_lua!(state, 0, 1, |state| ffi::lua_newthread(state))? }; self.push_ref(&func.0); - ffi::lua_xmove(self.state, thread_state, 1); + ffi::lua_xmove(state, thread_state, 1); Ok(Thread(self.pop_ref())) } @@ -1694,19 +1686,20 @@ impl Lua { feature = "luau", ))] unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 1)?; + let state = self.state(); + let _sg = StackGuard::new(state); + check_stack(state, 1)?; - let extra = &mut *self.extra.get(); - if let Some(index) = extra.recycled_thread_cache.pop() { - let thread_state = ffi::lua_tothread(extra.ref_thread, index); + if let Some(index) = (*self.extra.get()).recycled_thread_cache.pop() { + let ref_thread = (*self.extra.get()).ref_thread; + let thread_state = ffi::lua_tothread(ref_thread, index); self.push_ref(&func.0); - ffi::lua_xmove(self.state, thread_state, 1); + ffi::lua_xmove(state, thread_state, 1); #[cfg(feature = "luau")] { // Inherit `LUA_GLOBALSINDEX` from the caller - ffi::lua_xpush(self.state, thread_state, ffi::LUA_GLOBALSINDEX); + ffi::lua_xpush(state, thread_state, ffi::LUA_GLOBALSINDEX); ffi::lua_replace(thread_state, ffi::LUA_GLOBALSINDEX); } @@ -1735,7 +1728,7 @@ impl Lua { ffi::lua_settop(thread_state, 0); } #[cfg(all(feature = "luajit", feature = "vendored"))] - ffi::lua_resetthread(self.state, thread_state); + ffi::lua_resetthread(self.state(), thread_state); #[cfg(feature = "luau")] ffi::lua_resetthread(thread_state); extra.recycled_thread_cache.push(thread.0.index); @@ -1811,13 +1804,14 @@ impl Lua { /// Returns a handle to the global environment. pub fn globals(&self) -> Table { + let state = self.state(); unsafe { - let _sg = StackGuard::new(self.state); - assert_stack(self.state, 1); + let _sg = StackGuard::new(state); + assert_stack(state, 1); #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] - ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS); + ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS); #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] - ffi::lua_pushvalue(self.state, ffi::LUA_GLOBALSINDEX); + ffi::lua_pushvalue(state, ffi::LUA_GLOBALSINDEX); Table(self.pop_ref()) } } @@ -1825,10 +1819,11 @@ impl Lua { /// 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 { + let state = self.state(); unsafe { - let _sg = StackGuard::new(self.state); - assert_stack(self.state, 1); - ffi::lua_pushthread(self.state); + let _sg = StackGuard::new(state); + assert_stack(state, 1); + ffi::lua_pushthread(state); Thread(self.pop_ref()) } } @@ -1892,14 +1887,15 @@ impl Lua { Ok(match v { Value::String(s) => Some(s), v => unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 4)?; + let state = self.state(); + let _sg = StackGuard::new(state); + check_stack(state, 4)?; self.push_value(v)?; let res = if self.unlikely_memory_error() { - ffi::lua_tolstring(self.state, -1, ptr::null_mut()) + ffi::lua_tolstring(state, -1, ptr::null_mut()) } else { - protect_lua!(self.state, 1, 1, |state| { + protect_lua!(state, 1, 1, |state| { ffi::lua_tolstring(state, -1, ptr::null_mut()) })? }; @@ -1922,12 +1918,13 @@ impl Lua { Ok(match v { Value::Integer(i) => Some(i), v => unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 2)?; + let state = self.state(); + let _sg = StackGuard::new(state); + check_stack(state, 2)?; self.push_value(v)?; let mut isint = 0; - let i = ffi::lua_tointegerx(self.state, -1, &mut isint); + let i = ffi::lua_tointegerx(state, -1, &mut isint); if isint == 0 { None } else { @@ -1946,12 +1943,13 @@ impl Lua { Ok(match v { Value::Number(n) => Some(n), v => unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 2)?; + let state = self.state(); + let _sg = StackGuard::new(state); + check_stack(state, 2)?; self.push_value(v)?; let mut isnum = 0; - let n = ffi::lua_tonumberx(self.state, -1, &mut isnum); + let n = ffi::lua_tonumberx(state, -1, &mut isnum); if isnum == 0 { None } else { @@ -1992,13 +1990,14 @@ impl Lua { where T: ToLua<'lua>, { + let state = self.state(); let t = t.to_lua(self)?; unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 5)?; + let _sg = StackGuard::new(state); + check_stack(state, 5)?; self.push_value(t)?; - rawset_field(self.state, ffi::LUA_REGISTRYINDEX, name) + rawset_field(state, ffi::LUA_REGISTRYINDEX, name) } } @@ -2012,13 +2011,14 @@ impl Lua { where T: FromLua<'lua>, { + let state = self.state(); let value = unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 3)?; + let _sg = StackGuard::new(state); + check_stack(state, 3)?; let protect = !self.unlikely_memory_error(); - push_string(self.state, name.as_bytes(), protect)?; - ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX); + push_string(state, name.as_bytes(), protect)?; + ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX); self.pop_value() }; @@ -2052,9 +2052,10 @@ impl Lua { return Ok(RegistryKey::new(ffi::LUA_REFNIL, unref_list)); } + let state = self.state(); unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 4)?; + let _sg = StackGuard::new(state); + check_stack(state, 4)?; let unref_list = (*self.extra.get()).registry_unref_list.clone(); self.push_value(t)?; @@ -2064,12 +2065,12 @@ impl Lua { let mut unref_list2 = mlua_expect!(unref_list2.lock(), "unref list poisoned"); if let Some(registry_id) = unref_list2.as_mut().and_then(|x| x.pop()) { // It must be safe to replace the value without triggering memory error - ffi::lua_rawseti(self.state, ffi::LUA_REGISTRYINDEX, registry_id as Integer); + ffi::lua_rawseti(state, ffi::LUA_REGISTRYINDEX, registry_id as Integer); return Ok(RegistryKey::new(registry_id, unref_list)); } // Allocate a new RegistryKey - let registry_id = protect_lua!(self.state, 1, 0, |state| { + let registry_id = protect_lua!(state, 1, 0, |state| { ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX) })?; Ok(RegistryKey::new(registry_id, unref_list)) @@ -2087,14 +2088,15 @@ impl Lua { return Err(Error::MismatchedRegistryKey); } + let state = self.state(); let value = match key.is_nil() { true => Value::Nil, false => unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 1)?; + let _sg = StackGuard::new(state); + check_stack(state, 1)?; let id = key.registry_id as Integer; - ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, id); + ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, id); self.pop_value() }, }; @@ -2114,8 +2116,9 @@ impl Lua { if !self.owns_registry_value(&key) { return Err(Error::MismatchedRegistryKey); } + unsafe { - ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, key.take()); + ffi::luaL_unref(self.state(), ffi::LUA_REGISTRYINDEX, key.take()); } Ok(()) } @@ -2144,9 +2147,10 @@ impl Lua { return Err(Error::RuntimeError(err)); } + let state = self.state(); unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 2)?; + let _sg = StackGuard::new(state); + check_stack(state, 2)?; let id = key.registry_id as Integer; if t == Value::Nil { @@ -2157,7 +2161,7 @@ impl Lua { key.set_nil(false); } // It must be safe to replace the value without triggering memory error - ffi::lua_rawseti(self.state, ffi::LUA_REGISTRYINDEX, id); + ffi::lua_rawseti(state, ffi::LUA_REGISTRYINDEX, id); } Ok(()) } @@ -2179,6 +2183,7 @@ impl Lua { /// but you can call this method to remove any unreachable registry values not manually removed /// by `Lua::remove_registry_value`. pub fn expire_registry_values(&self) { + let state = self.state(); unsafe { let mut unref_list = mlua_expect!( (*self.extra.get()).registry_unref_list.lock(), @@ -2186,7 +2191,7 @@ impl Lua { ); let unref_list = mem::replace(&mut *unref_list, Some(Vec::new())); for id in mlua_expect!(unref_list, "unref list not set") { - ffi::luaL_unref(self.state, ffi::LUA_REGISTRYINDEX, id); + ffi::luaL_unref(state, ffi::LUA_REGISTRYINDEX, id); } } } @@ -2219,7 +2224,7 @@ impl Lua { /// ``` #[track_caller] pub fn set_app_data<T: 'static + MaybeSend>(&self, data: T) { - let extra = unsafe { &mut (*self.extra.get()) }; + let extra = unsafe { &*self.extra.get() }; extra .app_data .try_borrow_mut() @@ -2230,19 +2235,21 @@ impl Lua { /// Gets a reference to an application data object stored by [`Lua::set_app_data()`] of type `T`. #[track_caller] pub fn app_data_ref<T: 'static>(&self) -> Option<Ref<T>> { - let extra = unsafe { &(*self.extra.get()) }; + let extra = unsafe { &*self.extra.get() }; let app_data = extra .app_data .try_borrow() .expect("cannot borrow app data container"); - let value = app_data.get(&TypeId::of::<T>())?.downcast_ref::<T>()? as *const _; - Some(Ref::map(app_data, |_| unsafe { &*value })) + Ref::filter_map(app_data, |data| { + data.get(&TypeId::of::<T>())?.downcast_ref::<T>() + }) + .ok() } /// Gets a mutable reference to an application data object stored by [`Lua::set_app_data()`] of type `T`. #[track_caller] pub fn app_data_mut<T: 'static>(&self) -> Option<RefMut<T>> { - let extra = unsafe { &(*self.extra.get()) }; + let extra = unsafe { &*self.extra.get() }; let mut app_data = extra .app_data .try_borrow_mut() @@ -2254,41 +2261,42 @@ impl Lua { /// Removes an application data of type `T`. #[track_caller] pub fn remove_app_data<T: 'static>(&self) -> Option<T> { - let extra = unsafe { &mut (*self.extra.get()) }; + let extra = unsafe { &*self.extra.get() }; extra .app_data .try_borrow_mut() .expect("cannot mutably borrow app data container") .remove(&TypeId::of::<T>()) - .and_then(|data| data.downcast().ok().map(|data| *data)) + .and_then(|data| data.downcast().ok().map(|data: Box<T>| *data)) } // Uses 2 stack spaces, does not call checkstack pub(crate) unsafe fn push_value(&self, value: Value) -> Result<()> { + let state = self.state(); match value { Value::Nil => { - ffi::lua_pushnil(self.state); + ffi::lua_pushnil(state); } Value::Boolean(b) => { - ffi::lua_pushboolean(self.state, b as c_int); + ffi::lua_pushboolean(state, b as c_int); } Value::LightUserData(ud) => { - ffi::lua_pushlightuserdata(self.state, ud.0); + ffi::lua_pushlightuserdata(state, ud.0); } Value::Integer(i) => { - ffi::lua_pushinteger(self.state, i); + ffi::lua_pushinteger(state, i); } Value::Number(n) => { - ffi::lua_pushnumber(self.state, n); + ffi::lua_pushnumber(state, n); } #[cfg(feature = "luau")] Value::Vector(x, y, z) => { - ffi::lua_pushvector(self.state, x, y, z); + ffi::lua_pushvector(state, x, y, z); } Value::String(s) => { @@ -2313,7 +2321,7 @@ impl Lua { Value::Error(err) => { let protect = !self.unlikely_memory_error(); - push_gc_userdata(self.state, WrappedFailure::Error(err), protect)?; + push_gc_userdata(state, WrappedFailure::Error(err), protect)?; } } @@ -2322,9 +2330,7 @@ impl Lua { // Uses 2 stack spaces, does not call checkstack pub(crate) unsafe fn pop_value(&self) -> Value { - let state = self.state; - let extra = &mut *self.extra.get(); - + let state = self.state(); match ffi::lua_type(state, -1) { ffi::LUA_TNIL => { ffi::lua_pop(state, 1); @@ -2385,7 +2391,7 @@ impl Lua { ffi::LUA_TFUNCTION => Value::Function(Function(self.pop_ref())), ffi::LUA_TUSERDATA => { - let wrapped_failure_mt_ptr = extra.wrapped_failure_mt_ptr; + let wrapped_failure_mt_ptr = (*self.extra.get()).wrapped_failure_mt_ptr; // We must prevent interaction with userdata types other than UserData OR a WrappedError. // WrappedPanics are automatically resumed. match get_gc_userdata::<WrappedFailure>(state, -1, wrapped_failure_mt_ptr).as_mut() @@ -2427,8 +2433,7 @@ impl Lua { Arc::ptr_eq(&lref.lua.0, &self.0), "Lua instance passed Value created from a different main Lua state" ); - let extra = &*self.extra.get(); - ffi::lua_xpush(extra.ref_thread, self.state, lref.index); + ffi::lua_xpush(self.ref_thread(), self.state(), lref.index); } // Pops the topmost element of the stack and stores a reference to it. This pins the object, @@ -2441,34 +2446,31 @@ impl Lua { // number of short term references being created, and `RegistryKey` being used for long term // references. pub(crate) unsafe fn pop_ref(&self) -> LuaRef { - let extra = &mut *self.extra.get(); - ffi::lua_xmove(self.state, extra.ref_thread, 1); - let index = ref_stack_pop(extra); + ffi::lua_xmove(self.state(), self.ref_thread(), 1); + let index = ref_stack_pop(&mut *self.extra.get()); LuaRef::new(self, index) } // Same as `pop_ref` but assumes the value is already on the reference thread pub(crate) unsafe fn pop_ref_thread(&self) -> LuaRef { - let extra = &mut *self.extra.get(); - let index = ref_stack_pop(extra); + let index = ref_stack_pop(&mut *self.extra.get()); LuaRef::new(self, index) } - pub(crate) fn clone_ref<'lua>(&'lua self, lref: &LuaRef<'lua>) -> LuaRef<'lua> { + pub(crate) fn clone_ref(&self, lref: &LuaRef) -> LuaRef { unsafe { - let extra = &mut *self.extra.get(); - ffi::lua_pushvalue(extra.ref_thread, lref.index); - let index = ref_stack_pop(extra); + ffi::lua_pushvalue(self.ref_thread(), lref.index); + let index = ref_stack_pop(&mut *self.extra.get()); LuaRef::new(self, index) } } pub(crate) fn drop_ref(&self, lref: &LuaRef) { unsafe { - let extra = &mut *self.extra.get(); - ffi::lua_pushnil(extra.ref_thread); - ffi::lua_replace(extra.ref_thread, lref.index); - extra.ref_free.push(lref.index); + let ref_thread = self.ref_thread(); + ffi::lua_pushnil(ref_thread); + ffi::lua_replace(ref_thread, lref.index); + (*self.extra.get()).ref_free.push(lref.index); } } @@ -2495,17 +2497,17 @@ impl Lua { LuaRef::new(self, index) } - unsafe fn push_userdata_metatable<T: 'static + UserData>(&self) -> Result<()> { - let extra = &mut *self.extra.get(); + unsafe fn push_userdata_metatable<T: UserData + 'static>(&self) -> Result<()> { + let state = self.state(); let type_id = TypeId::of::<T>(); - if let Some(&table_id) = extra.registered_userdata.get(&type_id) { - ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, table_id as Integer); + if let Some(&table_id) = (*self.extra.get()).registered_userdata.get(&type_id) { + ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, table_id as Integer); return Ok(()); } - let _sg = StackGuard::new_extra(self.state, 1); - check_stack(self.state, 13)?; + let _sg = StackGuard::new_extra(state, 1); + check_stack(state, 13)?; let mut fields = StaticUserDataFields::default(); let mut methods = StaticUserDataMethods::default(); @@ -2516,45 +2518,45 @@ impl Lua { let metatable_nrec = methods.meta_methods.len() + fields.meta_fields.len(); #[cfg(feature = "async")] let metatable_nrec = metatable_nrec + methods.async_meta_methods.len(); - push_table(self.state, 0, metatable_nrec as c_int, true)?; + push_table(state, 0, metatable_nrec as c_int, true)?; for (k, m) in methods.meta_methods { self.push_value(Value::Function(self.create_callback(m)?))?; - rawset_field(self.state, -2, k.validate()?.name())?; + rawset_field(state, -2, k.validate()?.name())?; } #[cfg(feature = "async")] for (k, m) in methods.async_meta_methods { self.push_value(Value::Function(self.create_async_callback(m)?))?; - rawset_field(self.state, -2, k.validate()?.name())?; + rawset_field(state, -2, k.validate()?.name())?; } for (k, f) in fields.meta_fields { self.push_value(f(self)?)?; - rawset_field(self.state, -2, k.validate()?.name())?; + rawset_field(state, -2, k.validate()?.name())?; } - let metatable_index = ffi::lua_absindex(self.state, -1); + let metatable_index = ffi::lua_absindex(state, -1); let mut extra_tables_count = 0; let mut field_getters_index = None; let field_getters_nrec = fields.field_getters.len(); if field_getters_nrec > 0 { - push_table(self.state, 0, field_getters_nrec as c_int, true)?; + push_table(state, 0, field_getters_nrec as c_int, true)?; for (k, m) in fields.field_getters { self.push_value(Value::Function(self.create_callback(m)?))?; - rawset_field(self.state, -2, &k)?; + rawset_field(state, -2, &k)?; } - field_getters_index = Some(ffi::lua_absindex(self.state, -1)); + field_getters_index = Some(ffi::lua_absindex(state, -1)); extra_tables_count += 1; } let mut field_setters_index = None; let field_setters_nrec = fields.field_setters.len(); if field_setters_nrec > 0 { - push_table(self.state, 0, field_setters_nrec as c_int, true)?; + push_table(state, 0, field_setters_nrec as c_int, true)?; for (k, m) in fields.field_setters { self.push_value(Value::Function(self.create_callback(m)?))?; - rawset_field(self.state, -2, &k)?; + rawset_field(state, -2, &k)?; } - field_setters_index = Some(ffi::lua_absindex(self.state, -1)); + field_setters_index = Some(ffi::lua_absindex(state, -1)); extra_tables_count += 1; } @@ -2563,22 +2565,22 @@ impl Lua { #[cfg(feature = "async")] let methods_nrec = methods_nrec + methods.async_methods.len(); if methods_nrec > 0 { - push_table(self.state, 0, methods_nrec as c_int, true)?; + push_table(state, 0, methods_nrec as c_int, true)?; for (k, m) in methods.methods { self.push_value(Value::Function(self.create_callback(m)?))?; - rawset_field(self.state, -2, &k)?; + rawset_field(state, -2, &k)?; } #[cfg(feature = "async")] for (k, m) in methods.async_methods { self.push_value(Value::Function(self.create_async_callback(m)?))?; - rawset_field(self.state, -2, &k)?; + rawset_field(state, -2, &k)?; } - methods_index = Some(ffi::lua_absindex(self.state, -1)); + methods_index = Some(ffi::lua_absindex(state, -1)); extra_tables_count += 1; } init_userdata_metatable::<UserDataCell<T>>( - self.state, + state, metatable_index, field_getters_index, field_setters_index, @@ -2586,29 +2588,34 @@ impl Lua { )?; // Pop extra tables to get metatable on top of the stack - ffi::lua_pop(self.state, extra_tables_count); + ffi::lua_pop(state, extra_tables_count); - let mt_ptr = ffi::lua_topointer(self.state, -1); - ffi::lua_pushvalue(self.state, -1); - let id = protect_lua!(self.state, 1, 0, |state| { + let mt_ptr = ffi::lua_topointer(state, -1); + ffi::lua_pushvalue(state, -1); + let id = protect_lua!(state, 1, 0, |state| { ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX) })?; - extra.registered_userdata.insert(type_id, id); - extra.registered_userdata_mt.insert(mt_ptr, Some(type_id)); + (*self.extra.get()).registered_userdata.insert(type_id, id); + (*self.extra.get()) + .registered_userdata_mt + .insert(mt_ptr, Some(type_id)); Ok(()) } + #[inline] pub(crate) unsafe fn register_userdata_metatable( &self, ptr: *const c_void, type_id: Option<TypeId>, ) { - let extra = &mut *self.extra.get(); - extra.registered_userdata_mt.insert(ptr, type_id); + (*self.extra.get()) + .registered_userdata_mt + .insert(ptr, type_id); } + #[inline] pub(crate) unsafe fn deregister_userdata_metatable(&self, ptr: *const c_void) { (*self.extra.get()).registered_userdata_mt.remove(&ptr); } @@ -2617,16 +2624,16 @@ impl Lua { // and not destructed UserData. // Uses 2 stack spaces, does not call checkstack. pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<Option<TypeId>> { + let state = self.state(); self.push_ref(lref); - if ffi::lua_getmetatable(self.state, -1) == 0 { - ffi::lua_pop(self.state, 1); + if ffi::lua_getmetatable(state, -1) == 0 { + ffi::lua_pop(state, 1); return Err(Error::UserDataTypeMismatch); } - let mt_ptr = ffi::lua_topointer(self.state, -1); - ffi::lua_pop(self.state, 1); + let mt_ptr = ffi::lua_topointer(state, -1); + ffi::lua_pop(state, 1); - let extra = &*self.extra.get(); - match extra.registered_userdata_mt.get(&mt_ptr) { + match (*self.extra.get()).registered_userdata_mt.get(&mt_ptr) { Some(&type_id) if type_id == Some(TypeId::of::<DestructedUserdata>()) => { Err(Error::UserDataDestructed) } @@ -2667,7 +2674,7 @@ impl Lua { } let lua: &Lua = mem::transmute((*extra).inner.as_ref().unwrap()); - let _guard = StateGuard::new(&mut *lua.0.get(), state); + let _guard = StateGuard::new(&lua.0, state); let mut args = MultiValue::new_or_cached(lua); args.reserve(nargs as usize); @@ -2689,20 +2696,21 @@ impl Lua { }) } + let state = self.state(); unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 4)?; + let _sg = StackGuard::new(state); + check_stack(state, 4)?; let func = mem::transmute(func); let extra = Arc::clone(&self.extra); let protect = !self.unlikely_memory_error(); - push_gc_userdata(self.state, CallbackUpvalue { data: func, extra }, protect)?; + push_gc_userdata(state, CallbackUpvalue { data: func, extra }, protect)?; if protect { - protect_lua!(self.state, 1, 1, fn(state) { + protect_lua!(state, 1, 1, fn(state) { ffi::lua_pushcclosure(state, call_callback, 1); })?; } else { - ffi::lua_pushcclosure(self.state, call_callback, 1); + ffi::lua_pushcclosure(state, call_callback, 1); } Ok(Function(self.pop_ref())) @@ -2721,9 +2729,9 @@ impl Lua { feature = "luau" ))] unsafe { - let libs = (*self.extra.get()).libs; - if !libs.contains(StdLib::COROUTINE) { - self.load_from_std_lib(StdLib::COROUTINE)?; + if !(*self.extra.get()).libs.contains(StdLib::COROUTINE) { + load_from_std_lib(self.main_state, StdLib::COROUTINE)?; + (*self.extra.get()).libs |= StdLib::COROUTINE; } } @@ -2748,7 +2756,7 @@ impl Lua { } let lua: &Lua = mem::transmute((*extra).inner.as_ref().unwrap()); - let _guard = StateGuard::new(&mut *lua.0.get(), state); + let _guard = StateGuard::new(&lua.0, state); let mut args = MultiValue::new_or_cached(lua); args.reserve(nargs as usize); @@ -2793,7 +2801,7 @@ impl Lua { } let lua: &Lua = mem::transmute((*extra).inner.as_ref().unwrap()); - let _guard = StateGuard::new(&mut *lua.0.get(), state); + let _guard = StateGuard::new(&lua.0, state); // Try to get an outer poll waker let waker = lua.waker().unwrap_or_else(noop_waker); @@ -2820,21 +2828,22 @@ impl Lua { }) } + let state = self.state(); let get_poll = unsafe { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 4)?; + let _sg = StackGuard::new(state); + check_stack(state, 4)?; let func = mem::transmute(func); let extra = Arc::clone(&self.extra); let protect = !self.unlikely_memory_error(); let upvalue = AsyncCallbackUpvalue { data: func, extra }; - push_gc_userdata(self.state, upvalue, protect)?; + push_gc_userdata(state, upvalue, protect)?; if protect { - protect_lua!(self.state, 1, 1, fn(state) { + protect_lua!(state, 1, 1, fn(state) { ffi::lua_pushcclosure(state, call_callback, 1); })?; } else { - ffi::lua_pushcclosure(self.state, call_callback, 1); + ffi::lua_pushcclosure(state, call_callback, 1); } Function(self.pop_ref()) @@ -2901,32 +2910,33 @@ impl Lua { pub(crate) unsafe fn make_userdata<T>(&self, data: UserDataCell<T>) -> Result<AnyUserData> where - T: 'static + UserData, + T: UserData + 'static, { - let _sg = StackGuard::new(self.state); - check_stack(self.state, 3)?; + let state = self.state(); + let _sg = StackGuard::new(state); + check_stack(state, 3)?; // We push metatable first to ensure having correct metatable with `__gc` method - ffi::lua_pushnil(self.state); + ffi::lua_pushnil(state); self.push_userdata_metatable::<T>()?; let protect = !self.unlikely_memory_error(); #[cfg(not(feature = "lua54"))] - push_userdata(self.state, data, protect)?; + push_userdata(state, data, protect)?; #[cfg(feature = "lua54")] - push_userdata_uv(self.state, data, USER_VALUE_MAXSLOT as c_int, protect)?; - ffi::lua_replace(self.state, -3); - ffi::lua_setmetatable(self.state, -2); + push_userdata_uv(state, data, USER_VALUE_MAXSLOT as c_int, protect)?; + ffi::lua_replace(state, -3); + ffi::lua_setmetatable(state, -2); // Set empty environment for Lua 5.1 #[cfg(any(feature = "lua51", feature = "luajit"))] if protect { - protect_lua!(self.state, 1, 1, fn(state) { + protect_lua!(state, 1, 1, fn(state) { ffi::lua_newtable(state); ffi::lua_setuservalue(state, -2); })?; } else { - ffi::lua_newtable(self.state); - ffi::lua_setuservalue(self.state, -2); + ffi::lua_newtable(state); + ffi::lua_setuservalue(state, -2); } Ok(AnyUserData(self.pop_ref())) @@ -2959,12 +2969,12 @@ impl Lua { Ok(()) } - pub(crate) unsafe fn make_from_ptr(state: *mut ffi::lua_State) -> Option<Self> { - let _sg = StackGuard::new(state); - assert_stack(state, 1); - let extra = extra_data(state)?; - let inner = (*extra.get()).inner.as_ref().unwrap(); - Some(Lua(Arc::clone(inner))) + pub(crate) unsafe fn try_from_ptr(state: *mut ffi::lua_State) -> Option<Self> { + let extra = extra_data(state); + if extra.is_null() { + return None; + } + (*extra).inner.as_ref().map(|lua| Lua(Arc::clone(lua))) } #[inline] @@ -2988,9 +2998,8 @@ impl Lua { #[inline] pub(crate) unsafe fn unlikely_memory_error(&self) -> bool { - let extra = &mut *self.extra.get(); // MemoryInfo is empty in module mode so we cannot predict memory limits - extra + (*self.extra.get()) .mem_info .map(|x| x.as_ref().memory_limit == 0) .unwrap_or_default() @@ -2999,45 +3008,46 @@ impl Lua { impl LuaInner { #[inline(always)] + pub(crate) fn state(&self) -> *mut ffi::lua_State { + self.state.load(Ordering::Relaxed) + } + + #[inline(always)] pub(crate) fn ref_thread(&self) -> *mut ffi::lua_State { unsafe { (*self.extra.get()).ref_thread } } } -struct StateGuard<'a>(&'a mut LuaInner, *mut ffi::lua_State); +struct StateGuard<'a>(&'a LuaInner, *mut ffi::lua_State); impl<'a> StateGuard<'a> { - fn new(inner: &'a mut LuaInner, mut state: *mut ffi::lua_State) -> Self { - mem::swap(&mut inner.state, &mut state); + fn new(inner: &'a LuaInner, mut state: *mut ffi::lua_State) -> Self { + state = inner.state.swap(state, Ordering::Relaxed); Self(inner, state) } } impl<'a> Drop for StateGuard<'a> { fn drop(&mut self) { - mem::swap(&mut self.0.state, &mut self.1); + self.0.state.store(self.1, Ordering::Relaxed); } } #[cfg(feature = "luau")] -unsafe fn extra_data(state: *mut ffi::lua_State) -> Option<Arc<UnsafeCell<ExtraData>>> { - let extra_ptr = (*ffi::lua_callbacks(state)).userdata as *mut Arc<UnsafeCell<ExtraData>>; - if extra_ptr.is_null() { - return None; - } - Some(Arc::clone(&*extra_ptr)) +unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData { + (*ffi::lua_callbacks(state)).userdata as *mut ExtraData } #[cfg(not(feature = "luau"))] -unsafe fn extra_data(state: *mut ffi::lua_State) -> Option<Arc<UnsafeCell<ExtraData>>> { +unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData { let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void; if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key) != ffi::LUA_TUSERDATA { - return None; + ffi::lua_pop(state, 1); + return ptr::null_mut(); } let extra_ptr = ffi::lua_touserdata(state, -1) as *mut Arc<UnsafeCell<ExtraData>>; - let extra = Arc::clone(&*extra_ptr); ffi::lua_pop(state, 1); - Some(extra) + (*extra_ptr).get() } // Creates required entries in the metatable cache (see `util::METATABLE_CACHE`) @@ -3064,14 +3074,14 @@ where if extra.is_null() { return callback_error(state, f); } - let extra = &mut *extra; + let ref_thread = (*extra).ref_thread; let nargs = ffi::lua_gettop(state); // We need 2 extra stack spaces to store userdata and error/panic metatable. // Luau workaround can be removed after solving https://github.com/Roblox/luau/issues/446 // Also see #142 and #153 - if !cfg!(feature = "luau") || extra.wrapped_failures_cache.is_empty() { + if !cfg!(feature = "luau") || (*extra).wrapped_failures_cache.is_empty() { let extra_stack = if nargs < 2 { 2 - nargs } else { 1 }; ffi::luaL_checkstack( state, @@ -3087,7 +3097,7 @@ where // We cannot shadow Rust errors with Lua ones, so we need to obtain pre-allocated memory // to store a wrapped failure (error or panic) *before* we proceed. - let prealloc_failure = match extra.wrapped_failures_cache.pop() { + let prealloc_failure = match (*extra).wrapped_failures_cache.pop() { Some(index) => PreallocatedFailure::Cached(index), None => { let ud = WrappedFailure::new_userdata(state); @@ -3096,7 +3106,7 @@ where } }; - let mut get_wrapped_failure = || match prealloc_failure { + let get_wrapped_failure = || match prealloc_failure { PreallocatedFailure::New(ud) => { ffi::lua_settop(state, 1); ud @@ -3105,11 +3115,11 @@ where ffi::lua_settop(state, 0); #[cfg(feature = "luau")] assert_stack(state, 2); - ffi::lua_pushvalue(extra.ref_thread, index); - ffi::lua_xmove(extra.ref_thread, state, 1); - ffi::lua_pushnil(extra.ref_thread); - ffi::lua_replace(extra.ref_thread, index); - extra.ref_free.push(index); + ffi::lua_pushvalue(ref_thread, index); + ffi::lua_xmove(ref_thread, state, 1); + ffi::lua_pushnil(ref_thread); + ffi::lua_replace(ref_thread, index); + (*extra).ref_free.push(index); ffi::lua_touserdata(state, -1) as *mut WrappedFailure } }; @@ -3119,25 +3129,25 @@ where // Return unused WrappedFailure to the cache match prealloc_failure { PreallocatedFailure::New(_) - if extra.wrapped_failures_cache.len() < WRAPPED_FAILURES_CACHE_SIZE => + if (*extra).wrapped_failures_cache.len() < WRAPPED_FAILURES_CACHE_SIZE => { ffi::lua_rotate(state, 1, -1); - ffi::lua_xmove(state, extra.ref_thread, 1); - let index = ref_stack_pop(extra); - extra.wrapped_failures_cache.push(index); + ffi::lua_xmove(state, ref_thread, 1); + let index = ref_stack_pop(&mut *extra); + (*extra).wrapped_failures_cache.push(index); } PreallocatedFailure::New(_) => { ffi::lua_remove(state, 1); } PreallocatedFailure::Cached(index) - if extra.wrapped_failures_cache.len() < WRAPPED_FAILURES_CACHE_SIZE => + if (*extra).wrapped_failures_cache.len() < WRAPPED_FAILURES_CACHE_SIZE => { - extra.wrapped_failures_cache.push(index); + (*extra).wrapped_failures_cache.push(index); } PreallocatedFailure::Cached(index) => { - ffi::lua_pushnil(extra.ref_thread); - ffi::lua_replace(extra.ref_thread, index); - extra.ref_free.push(index); + ffi::lua_pushnil(ref_thread); + ffi::lua_replace(ref_thread, index); + (*extra).ref_free.push(index); } } r @@ -3331,3 +3341,16 @@ unsafe fn ref_stack_pop(extra: &mut ExtraData) -> c_int { extra.ref_stack_top += 1; extra.ref_stack_top } + +#[cfg(test)] +mod assertions { + use super::*; + + // Lua has lots of interior mutability, should not be RefUnwindSafe + static_assertions::assert_not_impl_any!(Lua: std::panic::RefUnwindSafe); + + #[cfg(not(feature = "send"))] + static_assertions::assert_not_impl_any!(Lua: Send); + #[cfg(feature = "send")] + static_assertions::assert_impl_all!(Lua: Send); +} diff --git a/src/luau.rs b/src/luau.rs index e7f397b..7dab235 100644 --- a/src/luau.rs +++ b/src/luau.rs @@ -1,5 +1,6 @@ use std::ffi::CStr; use std::os::raw::{c_float, c_int}; +use std::string::String as StdString; use crate::chunk::ChunkMode; use crate::error::{Error, Result}; @@ -69,14 +70,15 @@ unsafe extern "C" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_int { } } -fn lua_require(lua: &Lua, name: Option<std::string::String>) -> Result<Value> { +fn lua_require(lua: &Lua, name: Option<StdString>) -> Result<Value> { let name = name.ok_or_else(|| Error::RuntimeError("invalid module name".into()))?; // Find module in the cache + let state = lua.state(); let loaded = unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 2)?; - protect_lua!(lua.state, 0, 1, fn(state) { + let _sg = StackGuard::new(state); + check_stack(state, 2)?; + protect_lua!(state, 0, 1, fn(state) { ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED")); })?; Table(lua.pop_ref()) diff --git a/src/scope.rs b/src/scope.rs index 69fc0a4..c47ded2 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -190,7 +190,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { #[cfg(any(feature = "lua51", feature = "luajit"))] let newtable = self.lua.create_table()?; let destructor: DestructorCallback = Box::new(move |ud| { - let state = ud.lua.state; + let state = ud.lua.state(); let _sg = StackGuard::new(state); assert_stack(state, 2); @@ -275,11 +275,12 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { // first argument). let check_ud_type = move |lua: &'callback Lua, value| { if let Some(Value::UserData(ud)) = value { + let state = lua.state(); unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 2)?; + let _sg = StackGuard::new(state); + check_stack(state, 2)?; lua.push_userdata_ref(&ud.0)?; - if get_userdata(lua.state, -1) as *const _ == ud_ptr { + if get_userdata(state, -1) as *const _ == ud_ptr { return Ok(()); } } @@ -330,14 +331,15 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { T::add_fields(&mut ud_fields); T::add_methods(&mut ud_methods); + let lua = self.lua; + let state = lua.state(); unsafe { - let lua = self.lua; - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 13)?; + let _sg = StackGuard::new(state); + check_stack(state, 13)?; #[cfg(not(feature = "luau"))] #[allow(clippy::let_and_return)] - let ud_ptr = protect_lua!(lua.state, 0, 1, |state| { + let ud_ptr = protect_lua!(state, 0, 1, |state| { let ud = ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<Rc<RefCell<T>>>>()); @@ -353,67 +355,67 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { #[cfg(feature = "luau")] let ud_ptr = { crate::util::push_userdata::<UserDataCell<Rc<RefCell<T>>>>( - lua.state, + state, UserDataCell::new(data.clone()), true, )?; - ffi::lua_touserdata(lua.state, -1) + ffi::lua_touserdata(state, -1) }; // Prepare metatable, add meta methods first and then meta fields let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1; - push_table(lua.state, 0, meta_methods_nrec as c_int, true)?; + push_table(state, 0, meta_methods_nrec as c_int, true)?; for (k, m) in ud_methods.meta_methods { let data = data.clone(); lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?; - rawset_field(lua.state, -2, k.validate()?.name())?; + rawset_field(state, -2, k.validate()?.name())?; } for (k, f) in ud_fields.meta_fields { lua.push_value(f(mem::transmute(lua))?)?; - rawset_field(lua.state, -2, k.validate()?.name())?; + rawset_field(state, -2, k.validate()?.name())?; } - let metatable_index = ffi::lua_absindex(lua.state, -1); + let metatable_index = ffi::lua_absindex(state, -1); let mut field_getters_index = None; let field_getters_nrec = ud_fields.field_getters.len(); if field_getters_nrec > 0 { - push_table(lua.state, 0, field_getters_nrec as c_int, true)?; + push_table(state, 0, field_getters_nrec as c_int, true)?; for (k, m) in ud_fields.field_getters { let data = data.clone(); lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?; - rawset_field(lua.state, -2, &k)?; + rawset_field(state, -2, &k)?; } - field_getters_index = Some(ffi::lua_absindex(lua.state, -1)); + field_getters_index = Some(ffi::lua_absindex(state, -1)); } let mut field_setters_index = None; let field_setters_nrec = ud_fields.field_setters.len(); if field_setters_nrec > 0 { - push_table(lua.state, 0, field_setters_nrec as c_int, true)?; + push_table(state, 0, field_setters_nrec as c_int, true)?; for (k, m) in ud_fields.field_setters { let data = data.clone(); lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?; - rawset_field(lua.state, -2, &k)?; + rawset_field(state, -2, &k)?; } - field_setters_index = Some(ffi::lua_absindex(lua.state, -1)); + field_setters_index = Some(ffi::lua_absindex(state, -1)); } let mut methods_index = None; let methods_nrec = ud_methods.methods.len(); if methods_nrec > 0 { // Create table used for methods lookup - push_table(lua.state, 0, methods_nrec as c_int, true)?; + push_table(state, 0, methods_nrec as c_int, true)?; for (k, m) in ud_methods.methods { let data = data.clone(); lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?; - rawset_field(lua.state, -2, &k)?; + rawset_field(state, -2, &k)?; } - methods_index = Some(ffi::lua_absindex(lua.state, -1)); + methods_index = Some(ffi::lua_absindex(state, -1)); } init_userdata_metatable::<UserDataCell<Rc<RefCell<T>>>>( - lua.state, + state, metatable_index, field_getters_index, field_setters_index, @@ -423,20 +425,20 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { let count = field_getters_index.map(|_| 1).unwrap_or(0) + field_setters_index.map(|_| 1).unwrap_or(0) + methods_index.map(|_| 1).unwrap_or(0); - ffi::lua_pop(lua.state, count); + ffi::lua_pop(state, count); - let mt_ptr = ffi::lua_topointer(lua.state, -1); + let mt_ptr = ffi::lua_topointer(state, -1); // Write userdata just before attaching metatable with `__gc` metamethod #[cfg(not(feature = "luau"))] std::ptr::write(ud_ptr as _, UserDataCell::new(data)); - ffi::lua_setmetatable(lua.state, -2); + ffi::lua_setmetatable(state, -2); let ud = AnyUserData(lua.pop_ref()); lua.register_userdata_metatable(mt_ptr, None); #[cfg(any(feature = "lua51", feature = "luajit"))] let newtable = lua.create_table()?; let destructor: DestructorCallback = Box::new(move |ud| { - let state = ud.lua.state; + let state = ud.lua.state(); let _sg = StackGuard::new(state); assert_stack(state, 2); @@ -498,7 +500,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { let f = self.lua.create_callback(f)?; let destructor: DestructorCallback = Box::new(|f| { - let state = f.lua.state; + let state = f.lua.state(); let _sg = StackGuard::new(state); assert_stack(state, 3); @@ -532,7 +534,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { let get_poll_str = self.lua.create_string("get_poll")?; let poll_str = self.lua.create_string("poll")?; let destructor: DestructorCallback = Box::new(move |f| { - let state = f.lua.state; + let state = f.lua.state(); let _sg = StackGuard::new(state); assert_stack(state, 5); diff --git a/src/serde/ser.rs b/src/serde/ser.rs index 7df27cd..2a0d691 100644 --- a/src/serde/ser.rs +++ b/src/serde/ser.rs @@ -320,20 +320,21 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> { T: Serialize + ?Sized, { let lua = self.table.0.lua; + let state = lua.state(); let value = lua.to_value_with(value, self.options)?; unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 4)?; + let _sg = StackGuard::new(state); + check_stack(state, 4)?; lua.push_ref(&self.table.0); lua.push_value(value)?; if lua.unlikely_memory_error() { - let len = ffi::lua_rawlen(lua.state, -2) as Integer; - ffi::lua_rawseti(lua.state, -2, len + 1); - ffi::lua_pop(lua.state, 1); + let len = ffi::lua_rawlen(state, -2) as Integer; + ffi::lua_rawseti(state, -2, len + 1); + ffi::lua_pop(state, 1); Ok(()) } else { - protect_lua!(lua.state, 2, 0, fn(state) { + protect_lua!(state, 2, 0, fn(state) { let len = ffi::lua_rawlen(state, -2) as Integer; ffi::lua_rawseti(state, -2, len + 1); }) diff --git a/src/string.rs b/src/string.rs index 70e11d1..60732d8 100644 --- a/src/string.rs +++ b/src/string.rs @@ -190,3 +190,10 @@ impl<'lua> Serialize for String<'lua> { } } } + +#[cfg(test)] +mod assertions { + use super::*; + + static_assertions::assert_not_impl_any!(String: Send); +} diff --git a/src/table.rs b/src/table.rs index 2d9a889..0f0654e 100644 --- a/src/table.rs +++ b/src/table.rs @@ -78,14 +78,15 @@ impl<'lua> Table<'lua> { let key = key.to_lua(lua)?; let value = value.to_lua(lua)?; + let state = lua.state(); unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 5)?; + let _sg = StackGuard::new(state); + check_stack(state, 5)?; lua.push_ref(&self.0); lua.push_value(key)?; lua.push_value(value)?; - protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_settable(state, -3)) + protect_lua!(state, 3, 0, fn(state) ffi::lua_settable(state, -3)) } } @@ -120,15 +121,16 @@ impl<'lua> Table<'lua> { } let lua = self.0.lua; + let state = lua.state(); let key = key.to_lua(lua)?; let value = unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 4)?; + let _sg = StackGuard::new(state); + check_stack(state, 4)?; lua.push_ref(&self.0); lua.push_value(key)?; - protect_lua!(lua.state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?; + protect_lua!(state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?; lua.pop_value() }; @@ -148,14 +150,15 @@ impl<'lua> Table<'lua> { } let lua = self.0.lua; + let state = lua.state(); let value = value.to_lua(lua)?; unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 4)?; + let _sg = StackGuard::new(state); + check_stack(state, 4)?; lua.push_ref(&self.0); lua.push_value(value)?; - protect_lua!(lua.state, 2, 0, fn(state) { + protect_lua!(state, 2, 0, fn(state) { let len = ffi::luaL_len(state, -2) as Integer; ffi::lua_seti(state, -2, len + 1); })? @@ -171,12 +174,13 @@ impl<'lua> Table<'lua> { } let lua = self.0.lua; + let state = lua.state(); let value = unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 4)?; + let _sg = StackGuard::new(state); + check_stack(state, 4)?; lua.push_ref(&self.0); - protect_lua!(lua.state, 1, 1, fn(state) { + protect_lua!(state, 1, 1, fn(state) { let len = ffi::luaL_len(state, -1) as Integer; ffi::lua_geti(state, -1, len); ffi::lua_pushnil(state); @@ -249,23 +253,24 @@ impl<'lua> Table<'lua> { self.check_readonly_write()?; let lua = self.0.lua; + let state = lua.state(); let key = key.to_lua(lua)?; let value = value.to_lua(lua)?; unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 5)?; + let _sg = StackGuard::new(state); + check_stack(state, 5)?; lua.push_ref(&self.0); lua.push_value(key)?; lua.push_value(value)?; if lua.unlikely_memory_error() { - ffi::lua_rawset(lua.state, -3); - ffi::lua_pop(lua.state, 1); + ffi::lua_rawset(state, -3); + ffi::lua_pop(state, 1); Ok(()) } else { - protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_rawset(state, -3)) + protect_lua!(state, 3, 0, fn(state) ffi::lua_rawset(state, -3)) } } } @@ -273,15 +278,16 @@ impl<'lua> Table<'lua> { /// Gets the value associated to `key` without invoking metamethods. pub fn raw_get<K: ToLua<'lua>, V: FromLua<'lua>>(&self, key: K) -> Result<V> { let lua = self.0.lua; + let state = lua.state(); let key = key.to_lua(lua)?; let value = unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 3)?; + let _sg = StackGuard::new(state); + check_stack(state, 3)?; lua.push_ref(&self.0); lua.push_value(key)?; - ffi::lua_rawget(lua.state, -2); + ffi::lua_rawget(state, -2); lua.pop_value() }; @@ -292,6 +298,8 @@ impl<'lua> Table<'lua> { /// The worst case complexity is O(n), where n is the table length. pub fn raw_insert<V: ToLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> { let lua = self.0.lua; + let state = lua.state(); + let size = self.raw_len(); if idx < 1 || idx > size + 1 { return Err(Error::RuntimeError("index out of bounds".to_string())); @@ -299,12 +307,12 @@ impl<'lua> Table<'lua> { let value = value.to_lua(lua)?; unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 5)?; + let _sg = StackGuard::new(state); + check_stack(state, 5)?; lua.push_ref(&self.0); lua.push_value(value)?; - protect_lua!(lua.state, 2, 0, |state| { + protect_lua!(state, 2, 0, |state| { for i in (idx..=size).rev() { // table[i+1] = table[i] ffi::lua_rawgeti(state, -2, i); @@ -321,11 +329,12 @@ impl<'lua> Table<'lua> { self.check_readonly_write()?; let lua = self.0.lua; + let state = lua.state(); let value = value.to_lua(lua)?; unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 4)?; + let _sg = StackGuard::new(state); + check_stack(state, 4)?; lua.push_ref(&self.0); lua.push_value(value)?; @@ -336,9 +345,9 @@ impl<'lua> Table<'lua> { } if lua.unlikely_memory_error() { - callback(lua.state); + callback(state); } else { - protect_lua!(lua.state, 2, 0, fn(state) callback(state))?; + protect_lua!(state, 2, 0, fn(state) callback(state))?; } } Ok(()) @@ -350,16 +359,17 @@ impl<'lua> Table<'lua> { self.check_readonly_write()?; let lua = self.0.lua; + let state = lua.state(); let value = unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 3)?; + let _sg = StackGuard::new(state); + check_stack(state, 3)?; lua.push_ref(&self.0); - let len = ffi::lua_rawlen(lua.state, -1) as Integer; - ffi::lua_rawgeti(lua.state, -1, len); + let len = ffi::lua_rawlen(state, -1) as Integer; + ffi::lua_rawgeti(state, -1, len); // Set slot to nil (it must be safe to do) - ffi::lua_pushnil(lua.state); - ffi::lua_rawseti(lua.state, -3, len); + ffi::lua_pushnil(state); + ffi::lua_rawseti(state, -3, len); lua.pop_value() }; V::from_lua(value, lua) @@ -374,6 +384,7 @@ impl<'lua> Table<'lua> { /// For other key types this is equivalent to setting `table[key] = nil`. pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> { let lua = self.0.lua; + let state = lua.state(); let key = key.to_lua(lua)?; match key { Value::Integer(idx) => { @@ -382,11 +393,11 @@ impl<'lua> Table<'lua> { return Err(Error::RuntimeError("index out of bounds".to_string())); } unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 4)?; + let _sg = StackGuard::new(state); + check_stack(state, 4)?; lua.push_ref(&self.0); - protect_lua!(lua.state, 1, 0, |state| { + protect_lua!(state, 1, 0, |state| { for i in idx..size { ffi::lua_rawgeti(state, -1, i + 1); ffi::lua_rawseti(state, -2, i); @@ -412,12 +423,13 @@ impl<'lua> Table<'lua> { } let lua = self.0.lua; + let state = lua.state(); unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 4)?; + let _sg = StackGuard::new(state); + check_stack(state, 4)?; lua.push_ref(&self.0); - protect_lua!(lua.state, 1, 0, |state| ffi::luaL_len(state, -1)) + protect_lua!(state, 1, 0, |state| ffi::luaL_len(state, -1)) } } @@ -432,12 +444,13 @@ impl<'lua> Table<'lua> { /// Unlike the `getmetatable` Lua function, this method ignores the `__metatable` field. pub fn get_metatable(&self) -> Option<Table<'lua>> { let lua = self.0.lua; + let state = lua.state(); unsafe { - let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 2); + let _sg = StackGuard::new(state); + assert_stack(state, 2); lua.push_ref(&self.0); - if ffi::lua_getmetatable(lua.state, -1) == 0 { + if ffi::lua_getmetatable(state, -1) == 0 { None } else { Some(Table(lua.pop_ref())) @@ -457,17 +470,18 @@ impl<'lua> Table<'lua> { } let lua = self.0.lua; + let state = lua.state(); unsafe { - let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 2); + let _sg = StackGuard::new(state); + assert_stack(state, 2); lua.push_ref(&self.0); if let Some(metatable) = metatable { lua.push_ref(&metatable.0); } else { - ffi::lua_pushnil(lua.state); + ffi::lua_pushnil(state); } - ffi::lua_setmetatable(lua.state, -2); + ffi::lua_setmetatable(state, -2); } } @@ -654,16 +668,17 @@ impl<'lua> Table<'lua> { #[cfg(feature = "serialize")] pub(crate) fn is_array(&self) -> bool { let lua = self.0.lua; + let state = lua.state(); unsafe { - let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 3); + let _sg = StackGuard::new(state); + assert_stack(state, 3); lua.push_ref(&self.0); - if ffi::lua_getmetatable(lua.state, -1) == 0 { + if ffi::lua_getmetatable(state, -1) == 0 { return false; } - crate::serde::push_array_metatable(lua.state); - ffi::lua_rawequal(lua.state, -1, -2) != 0 + crate::serde::push_array_metatable(state); + ffi::lua_rawequal(state, -1, -2) != 0 } } @@ -911,15 +926,16 @@ where fn next(&mut self) -> Option<Self::Item> { if let Some(prev_key) = self.key.take() { let lua = self.table.lua; + let state = lua.state(); let res = (|| unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 5)?; + let _sg = StackGuard::new(state); + check_stack(state, 5)?; lua.push_ref(&self.table); lua.push_value(prev_key)?; - let next = protect_lua!(lua.state, 2, ffi::LUA_MULTRET, |state| { + let next = protect_lua!(state, 2, ffi::LUA_MULTRET, |state| { ffi::lua_next(state, -2) })?; if next != 0 { @@ -971,16 +987,17 @@ where fn next(&mut self) -> Option<Self::Item> { if let Some(index) = self.index.take() { let lua = self.table.lua; + let state = lua.state(); let res = (|| unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 1 + if self.raw { 0 } else { 3 })?; + let _sg = StackGuard::new(state); + check_stack(state, 1 + if self.raw { 0 } else { 3 })?; lua.push_ref(&self.table); let res = if self.raw { - ffi::lua_rawgeti(lua.state, -1, index) + ffi::lua_rawgeti(state, -1, index) } else { - protect_lua!(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index))? + protect_lua!(state, 1, 1, |state| ffi::lua_geti(state, -1, index))? }; match res { ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None), @@ -1001,3 +1018,10 @@ where } } } + +#[cfg(test)] +mod assertions { + use super::*; + + static_assertions::assert_not_impl_any!(Table: Send); +} diff --git a/src/thread.rs b/src/thread.rs index 811c056..4e5bd9e 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -22,7 +22,6 @@ use { }, futures_core::{future::Future, stream::Stream}, std::{ - cell::RefCell, marker::PhantomData, pin::Pin, task::{Context, Poll, Waker}, @@ -67,10 +66,9 @@ impl OwnedThread { /// [`Stream`]: futures_core::stream::Stream #[cfg(feature = "async")] #[cfg_attr(docsrs, doc(cfg(feature = "async")))] -#[derive(Debug)] pub struct AsyncThread<'lua, R> { thread: Thread<'lua>, - args0: RefCell<Option<Result<MultiValue<'lua>>>>, + args0: Option<Result<MultiValue<'lua>>>, ret: PhantomData<R>, recycle: bool, } @@ -123,11 +121,13 @@ impl<'lua> Thread<'lua> { R: FromLuaMulti<'lua>, { let lua = self.0.lua; + let state = lua.state(); + let mut args = args.to_lua_multi(lua)?; let nargs = args.len() as c_int; let results = unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, cmp::max(nargs + 1, 3))?; + let _sg = StackGuard::new(state); + check_stack(state, cmp::max(nargs + 1, 3))?; let thread_state = ffi::lua_tothread(lua.ref_thread(), self.0.index); @@ -140,23 +140,23 @@ impl<'lua> Thread<'lua> { for arg in args.drain_all() { lua.push_value(arg)?; } - ffi::lua_xmove(lua.state, thread_state, nargs); + ffi::lua_xmove(state, thread_state, nargs); let mut nresults = 0; - let ret = ffi::lua_resume(thread_state, lua.state, nargs, &mut nresults as *mut c_int); + let ret = ffi::lua_resume(thread_state, state, nargs, &mut nresults as *mut c_int); if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD { - check_stack(lua.state, 3)?; - protect_lua!(lua.state, 0, 1, |state| error_traceback_thread( + check_stack(state, 3)?; + protect_lua!(state, 0, 1, |state| error_traceback_thread( state, thread_state ))?; - return Err(pop_error(lua.state, ret)); + return Err(pop_error(state, ret)); } let mut results = args; // Reuse MultiValue container - check_stack(lua.state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below - ffi::lua_xmove(thread_state, lua.state, nresults); + check_stack(state, nresults + 2)?; // 2 is extra for `lua.pop_value()` below + ffi::lua_xmove(thread_state, state, nresults); for _ in 0..nresults { results.push_front(lua.pop_value()); @@ -205,12 +205,13 @@ impl<'lua> Thread<'lua> { ))] pub fn reset(&self, func: Function<'lua>) -> Result<()> { let lua = self.0.lua; + let state = lua.state(); unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 2)?; + let _sg = StackGuard::new(state); + check_stack(state, 2)?; lua.push_ref(&self.0); - let thread_state = ffi::lua_tothread(lua.state, -1); + let thread_state = ffi::lua_tothread(state, -1); #[cfg(feature = "lua54")] let status = ffi::lua_resetthread(thread_state); @@ -219,17 +220,17 @@ impl<'lua> Thread<'lua> { return Err(pop_error(thread_state, status)); } #[cfg(all(feature = "luajit", feature = "vendored"))] - ffi::lua_resetthread(lua.state, thread_state); + ffi::lua_resetthread(state, thread_state); #[cfg(feature = "luau")] ffi::lua_resetthread(thread_state); lua.push_ref(&func.0); - ffi::lua_xmove(lua.state, thread_state, 1); + ffi::lua_xmove(state, thread_state, 1); #[cfg(feature = "luau")] { // Inherit `LUA_GLOBALSINDEX` from the caller - ffi::lua_xpush(lua.state, thread_state, ffi::LUA_GLOBALSINDEX); + ffi::lua_xpush(state, thread_state, ffi::LUA_GLOBALSINDEX); ffi::lua_replace(thread_state, ffi::LUA_GLOBALSINDEX); } @@ -292,7 +293,7 @@ impl<'lua> Thread<'lua> { let args = args.to_lua_multi(self.0.lua); AsyncThread { thread: self, - args0: RefCell::new(Some(args)), + args0: Some(args), ret: PhantomData, recycle: false, } @@ -334,14 +335,15 @@ impl<'lua> Thread<'lua> { #[doc(hidden)] pub fn sandbox(&self) -> Result<()> { let lua = self.0.lua; + let state = lua.state(); unsafe { let thread = ffi::lua_tothread(lua.ref_thread(), self.0.index); check_stack(thread, 1)?; - check_stack(lua.state, 3)?; + check_stack(state, 3)?; // Inherit `LUA_GLOBALSINDEX` from the caller - ffi::lua_xpush(lua.state, thread, ffi::LUA_GLOBALSINDEX); + ffi::lua_xpush(state, thread, ffi::LUA_GLOBALSINDEX); ffi::lua_replace(thread, ffi::LUA_GLOBALSINDEX); - protect_lua!(lua.state, 0, 0, |_| ffi::luaL_sandboxthread(thread)) + protect_lua!(state, 0, 0, |_| ffi::luaL_sandboxthread(thread)) } } @@ -406,10 +408,13 @@ where }; let _wg = WakerGuard::new(lua, cx.waker().clone()); - let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() { - self.thread.resume(args?)? + + // This is safe as we are not moving the whole struct + let this = unsafe { self.get_unchecked_mut() }; + let ret: MultiValue = if let Some(args) = this.args0.take() { + this.thread.resume(args?)? } else { - self.thread.resume(())? + this.thread.resume(())? }; if is_poll_pending(&ret) { @@ -437,17 +442,20 @@ where }; let _wg = WakerGuard::new(lua, cx.waker().clone()); - let ret: MultiValue = if let Some(args) = self.args0.borrow_mut().take() { - self.thread.resume(args?)? + + // This is safe as we are not moving the whole struct + let this = unsafe { self.get_unchecked_mut() }; + let ret: MultiValue = if let Some(args) = this.args0.take() { + this.thread.resume(args?)? } else { - self.thread.resume(())? + this.thread.resume(())? }; if is_poll_pending(&ret) { return Poll::Pending; } - if let ThreadStatus::Resumable = self.thread.status() { + if let ThreadStatus::Resumable = this.thread.status() { // Ignore value returned via yield() cx.waker().wake_by_ref(); return Poll::Pending; @@ -493,3 +501,10 @@ impl<'lua> Drop for WakerGuard<'lua> { } } } + +#[cfg(test)] +mod assertions { + use super::*; + + static_assertions::assert_not_impl_any!(Thread: Send); +} diff --git a/src/types.rs b/src/types.rs index d5a5413..a02c21a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -192,6 +192,7 @@ impl<'lua> LuaRef<'lua> { } } + #[inline] pub(crate) fn into_owned(self) -> LuaOwnedRef { self.lua.make_owned_ref(self) } @@ -220,12 +221,13 @@ impl<'lua> Drop for LuaRef<'lua> { impl<'lua> PartialEq for LuaRef<'lua> { fn eq(&self, other: &Self) -> bool { let lua = self.lua; + let state = lua.state(); unsafe { - let _sg = StackGuard::new(lua.state); - assert_stack(lua.state, 2); + let _sg = StackGuard::new(state); + assert_stack(state, 2); lua.push_ref(self); lua.push_ref(other); - ffi::lua_rawequal(lua.state, -1, -2) == 1 + ffi::lua_rawequal(state, -1, -2) == 1 } } } @@ -249,10 +251,10 @@ impl Clone for LuaOwnedRef { impl Drop for LuaOwnedRef { fn drop(&mut self) { - self.lua.drop_ref(&LuaRef { + drop(LuaRef { lua: &self.lua, index: self.index, - drop: false, + drop: true, }); } } @@ -266,3 +268,11 @@ impl LuaOwnedRef { } } } + +#[cfg(test)] +mod assertions { + use super::*; + + static_assertions::assert_impl_all!(RegistryKey: Send, Sync); + static_assertions::assert_not_impl_any!(LuaRef: Send); +} diff --git a/src/userdata.rs b/src/userdata.rs index eaf3800..48d731b 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -829,16 +829,17 @@ impl<'lua> AnyUserData<'lua> { /// Keeps associated user values unchanged (they will be collected by Lua's GC). pub fn take<T: 'static + UserData>(&self) -> Result<T> { let lua = self.0.lua; + let state = lua.state(); unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 2)?; + let _sg = StackGuard::new(state); + check_stack(state, 2)?; let type_id = lua.push_userdata_ref(&self.0)?; match type_id { Some(type_id) if type_id == TypeId::of::<T>() => { // Try to borrow userdata exclusively - let _ = (*get_userdata::<UserDataCell<T>>(lua.state, -1)).try_borrow_mut()?; - Ok(take_userdata::<UserDataCell<T>>(lua.state).into_inner()) + let _ = (*get_userdata::<UserDataCell<T>>(state, -1)).try_borrow_mut()?; + Ok(take_userdata::<UserDataCell<T>>(state).into_inner()) } _ => Err(Error::UserDataTypeMismatch), } @@ -887,21 +888,22 @@ impl<'lua> AnyUserData<'lua> { } let lua = self.0.lua; + let state = lua.state(); unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 5)?; + let _sg = StackGuard::new(state); + check_stack(state, 5)?; lua.push_userdata_ref(&self.0)?; lua.push_value(v.to_lua(lua)?)?; #[cfg(feature = "lua54")] if n < USER_VALUE_MAXSLOT { - ffi::lua_setiuservalue(lua.state, -2, n as c_int); + ffi::lua_setiuservalue(state, -2, n as c_int); return Ok(()); } // Multiple (extra) user values are emulated by storing them in a table - protect_lua!(lua.state, 2, 0, |state| { + protect_lua!(state, 2, 0, |state| { if getuservalue_table(state, -2) != ffi::LUA_TTABLE { // Create a new table to use as uservalue ffi::lua_pop(state, 1); @@ -941,20 +943,21 @@ impl<'lua> AnyUserData<'lua> { } let lua = self.0.lua; + let state = lua.state(); unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 4)?; + let _sg = StackGuard::new(state); + check_stack(state, 4)?; lua.push_userdata_ref(&self.0)?; #[cfg(feature = "lua54")] if n < USER_VALUE_MAXSLOT { - ffi::lua_getiuservalue(lua.state, -1, n as c_int); + ffi::lua_getiuservalue(state, -1, n as c_int); return V::from_lua(lua.pop_value(), lua); } // Multiple (extra) user values are emulated by storing them in a table - protect_lua!(lua.state, 1, 1, |state| { + protect_lua!(state, 1, 1, |state| { if getuservalue_table(state, -1) != ffi::LUA_TTABLE { ffi::lua_pushnil(state); return; @@ -979,15 +982,16 @@ impl<'lua> AnyUserData<'lua> { V: ToLua<'lua>, { let lua = self.0.lua; + let state = lua.state(); unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 5)?; + let _sg = StackGuard::new(state); + check_stack(state, 5)?; lua.push_userdata_ref(&self.0)?; lua.push_value(v.to_lua(lua)?)?; // Multiple (extra) user values are emulated by storing them in a table - protect_lua!(lua.state, 2, 0, |state| { + protect_lua!(state, 2, 0, |state| { if getuservalue_table(state, -2) != ffi::LUA_TTABLE { // Create a new table to use as uservalue ffi::lua_pop(state, 1); @@ -1016,14 +1020,15 @@ impl<'lua> AnyUserData<'lua> { V: FromLua<'lua>, { let lua = self.0.lua; + let state = lua.state(); unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 4)?; + let _sg = StackGuard::new(state); + check_stack(state, 4)?; lua.push_userdata_ref(&self.0)?; // Multiple (extra) user values are emulated by storing them in a table - protect_lua!(lua.state, 1, 1, |state| { + protect_lua!(state, 1, 1, |state| { if getuservalue_table(state, -1) != ffi::LUA_TTABLE { ffi::lua_pushnil(state); return; @@ -1050,13 +1055,14 @@ impl<'lua> AnyUserData<'lua> { } fn get_raw_metatable(&self) -> Result<Table<'lua>> { + let lua = self.0.lua; + let state = lua.state(); unsafe { - let lua = self.0.lua; - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 3)?; + let _sg = StackGuard::new(state); + check_stack(state, 3)?; lua.push_userdata_ref(&self.0)?; - ffi::lua_getmetatable(lua.state, -1); // Checked that non-empty on the previous call + ffi::lua_getmetatable(state, -1); // Checked that non-empty on the previous call Ok(Table(lua.pop_ref())) } } @@ -1093,14 +1099,15 @@ impl<'lua> AnyUserData<'lua> { F: FnOnce(&'a UserDataCell<T>) -> Result<R>, { let lua = self.0.lua; + let state = lua.state(); unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 2)?; + let _sg = StackGuard::new(state); + check_stack(state, 2)?; let type_id = lua.push_userdata_ref(&self.0)?; match type_id { Some(type_id) if type_id == TypeId::of::<T>() => { - func(&*get_userdata::<UserDataCell<T>>(lua.state, -1)) + func(&*get_userdata::<UserDataCell<T>>(state, -1)) } _ => Err(Error::UserDataTypeMismatch), } @@ -1209,12 +1216,13 @@ impl<'lua> Serialize for AnyUserData<'lua> { S: Serializer, { let lua = self.0.lua; + let state = lua.state(); let data = unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 3).map_err(ser::Error::custom)?; + let _sg = StackGuard::new(state); + check_stack(state, 3).map_err(ser::Error::custom)?; lua.push_userdata_ref(&self.0).map_err(ser::Error::custom)?; - let ud = &*get_userdata::<UserDataCell<()>>(lua.state, -1); + let ud = &*get_userdata::<UserDataCell<()>>(state, -1); ud.0.try_borrow() .map_err(|_| ser::Error::custom(Error::UserDataBorrowError))? }; @@ -1224,3 +1232,10 @@ impl<'lua> Serialize for AnyUserData<'lua> { } } } + +#[cfg(test)] +mod assertions { + use super::*; + + static_assertions::assert_not_impl_any!(AnyUserData: Send); +} diff --git a/src/userdata_impl.rs b/src/userdata_impl.rs index 3126b09..8853a90 100644 --- a/src/userdata_impl.rs +++ b/src/userdata_impl.rs @@ -211,42 +211,43 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> { { Box::new(move |lua, mut args| { if let Some(front) = args.pop_front() { + let state = lua.state(); let userdata = AnyUserData::from_lua(front, lua)?; unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 2)?; + let _sg = StackGuard::new(state); + check_stack(state, 2)?; let type_id = lua.push_userdata_ref(&userdata.0)?; match type_id { Some(id) if id == TypeId::of::<T>() => { - let ud = get_userdata_ref::<T>(lua.state)?; + let ud = get_userdata_ref::<T>(state)?; method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) } #[cfg(not(feature = "send"))] Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => { - let ud = get_userdata_ref::<Rc<RefCell<T>>>(lua.state)?; + let ud = get_userdata_ref::<Rc<RefCell<T>>>(state)?; let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?; method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) } Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => { - let ud = get_userdata_ref::<Arc<Mutex<T>>>(lua.state)?; + let ud = get_userdata_ref::<Arc<Mutex<T>>>(state)?; let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?; method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) } #[cfg(feature = "parking_lot")] Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => { - let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(lua.state)?; + let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(state)?; let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?; method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) } Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => { - let ud = get_userdata_ref::<Arc<RwLock<T>>>(lua.state)?; + let ud = get_userdata_ref::<Arc<RwLock<T>>>(state)?; let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?; method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) } #[cfg(feature = "parking_lot")] Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => { - let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(lua.state)?; + let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(state)?; let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?; method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) } @@ -272,49 +273,50 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> { let method = RefCell::new(method); Box::new(move |lua, mut args| { if let Some(front) = args.pop_front() { + let state = lua.state(); let userdata = AnyUserData::from_lua(front, lua)?; let mut method = method .try_borrow_mut() .map_err(|_| Error::RecursiveMutCallback)?; unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 2)?; + let _sg = StackGuard::new(state); + check_stack(state, 2)?; let type_id = lua.push_userdata_ref(&userdata.0)?; match type_id { Some(id) if id == TypeId::of::<T>() => { - let mut ud = get_userdata_mut::<T>(lua.state)?; + let mut ud = get_userdata_mut::<T>(state)?; method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) } #[cfg(not(feature = "send"))] Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => { - let ud = get_userdata_mut::<Rc<RefCell<T>>>(lua.state)?; + let ud = get_userdata_mut::<Rc<RefCell<T>>>(state)?; let mut ud = ud .try_borrow_mut() .map_err(|_| Error::UserDataBorrowMutError)?; method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) } Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => { - let ud = get_userdata_mut::<Arc<Mutex<T>>>(lua.state)?; + let ud = get_userdata_mut::<Arc<Mutex<T>>>(state)?; let mut ud = ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?; method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) } #[cfg(feature = "parking_lot")] Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => { - let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(lua.state)?; + let ud = get_userdata_mut::<Arc<parking_lot::Mutex<T>>>(state)?; let mut ud = ud.try_lock().ok_or(Error::UserDataBorrowMutError)?; method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) } Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => { - let ud = get_userdata_mut::<Arc<RwLock<T>>>(lua.state)?; + let ud = get_userdata_mut::<Arc<RwLock<T>>>(state)?; let mut ud = ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?; method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) } #[cfg(feature = "parking_lot")] Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => { - let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(lua.state)?; + let ud = get_userdata_mut::<Arc<parking_lot::RwLock<T>>>(state)?; let mut ud = ud.try_write().ok_or(Error::UserDataBorrowMutError)?; method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) } @@ -343,43 +345,43 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> { Box::new(move |lua, mut args| { let fut_res = || { if let Some(front) = args.pop_front() { + let state = lua.state(); let userdata = AnyUserData::from_lua(front, lua)?; unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 2)?; + let _sg = StackGuard::new(state); + check_stack(state, 2)?; let type_id = lua.push_userdata_ref(&userdata.0)?; match type_id { Some(id) if id == TypeId::of::<T>() => { - let ud = get_userdata_ref::<T>(lua.state)?; + let ud = get_userdata_ref::<T>(state)?; Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?)) } #[cfg(not(feature = "send"))] Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => { - let ud = get_userdata_ref::<Rc<RefCell<T>>>(lua.state)?; + let ud = get_userdata_ref::<Rc<RefCell<T>>>(state)?; let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?; Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?)) } Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => { - let ud = get_userdata_ref::<Arc<Mutex<T>>>(lua.state)?; + let ud = get_userdata_ref::<Arc<Mutex<T>>>(state)?; let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?; Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?)) } #[cfg(feature = "parking_lot")] Some(id) if id == TypeId::of::<Arc<parking_lot::Mutex<T>>>() => { - let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(lua.state)?; + let ud = get_userdata_ref::<Arc<parking_lot::Mutex<T>>>(state)?; let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?; Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?)) } Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => { - let ud = get_userdata_ref::<Arc<RwLock<T>>>(lua.state)?; + let ud = get_userdata_ref::<Arc<RwLock<T>>>(state)?; let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?; Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?)) } #[cfg(feature = "parking_lot")] Some(id) if id == TypeId::of::<Arc<parking_lot::RwLock<T>>>() => { - let ud = - get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(lua.state)?; + let ud = get_userdata_ref::<Arc<parking_lot::RwLock<T>>>(state)?; let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?; Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?)) } diff --git a/src/value.rs b/src/value.rs index eb184d4..8d90b62 100644 --- a/src/value.rs +++ b/src/value.rs @@ -357,3 +357,11 @@ pub trait FromLuaMulti<'lua>: Sized { /// any missing values are nil. fn from_lua_multi(values: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self>; } + +#[cfg(test)] +mod assertions { + use super::*; + + static_assertions::assert_not_impl_any!(Value: Send); + static_assertions::assert_not_impl_any!(MultiValue: Send); +} |