diff --git a/mlua-sys/Cargo.toml b/mlua-sys/Cargo.toml index 8cfa28d..0da8fcc 100644 --- a/mlua-sys/Cargo.toml +++ b/mlua-sys/Cargo.toml @@ -40,4 +40,4 @@ cfg-if = "1.0" pkg-config = "0.3.17" lua-src = { version = ">= 546.0.0, < 546.1.0", optional = true } luajit-src = { version = ">= 210.5.0, < 210.6.0", optional = true } -luau0-src = { version = "0.7.0", optional = true } +luau0-src = { version = "0.7.7", optional = true } diff --git a/mlua-sys/src/luau/lua.rs b/mlua-sys/src/luau/lua.rs index 51168c0..596db0c 100644 --- a/mlua-sys/src/luau/lua.rs +++ b/mlua-sys/src/luau/lua.rs @@ -278,6 +278,7 @@ extern "C-unwind" { pub fn lua_getuserdatadtor(L: *mut lua_State, tag: c_int) -> Option; pub fn lua_clonefunction(L: *mut lua_State, idx: c_int); pub fn lua_cleartable(L: *mut lua_State, idx: c_int); + pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc; } // diff --git a/src/lua.rs b/src/lua.rs index 11765ce..e80be65 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -3,15 +3,14 @@ use std::cell::{RefCell, UnsafeCell}; use std::ffi::{CStr, CString}; use std::fmt; use std::marker::PhantomData; -use std::mem::MaybeUninit; +use std::mem::{self, MaybeUninit}; 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::ptr; use std::result::Result as StdResult; use std::sync::atomic::{AtomicPtr, Ordering}; use std::sync::{Arc, Mutex}; -use std::{mem, ptr, str}; use rustc_hash::FxHashMap; @@ -60,6 +59,7 @@ use { crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue}, futures_util::future::{self, Future}, futures_util::task::{noop_waker_ref, Context, Poll, Waker}, + std::ptr::NonNull, }; #[cfg(feature = "serialize")] @@ -94,7 +94,6 @@ pub(crate) struct ExtraData { safe: bool, libs: StdLib, - mem_state: Option>, #[cfg(feature = "module")] skip_memory_check: bool, @@ -223,6 +222,7 @@ impl LuaOptions { #[cfg(feature = "async")] pub(crate) static ASYNC_POLL_PENDING: u8 = 0; +#[cfg(not(feature = "luau"))] pub(crate) static EXTRA_REGISTRY_KEY: u8 = 0; const WRAPPED_FAILURE_POOL_SIZE: usize = 64; @@ -244,11 +244,14 @@ impl Drop for Lua { impl Drop for LuaInner { fn drop(&mut self) { unsafe { - #[cfg(feature = "luau")] - { - (*ffi::lua_callbacks(self.state())).userdata = ptr::null_mut(); - } + let mem_state = MemoryState::get(self.main_state); + ffi::lua_close(self.main_state); + + // Deallocate MemoryState + if !mem_state.is_null() { + drop(Box::from_raw(mem_state)); + } } } } @@ -261,9 +264,6 @@ impl Drop for ExtraData { } *mlua_expect!(self.registry_unref_list.lock(), "unref list poisoned") = None; - if let Some(mem_state) = self.mem_state { - drop(unsafe { Box::from_raw(mem_state.as_ptr()) }); - } } } @@ -383,12 +383,11 @@ impl Lua { /// Creates a new Lua state with required `libs` and `options` unsafe fn inner_new(libs: StdLib, options: LuaOptions) -> Lua { - let mut mem_state: *mut MemoryState = Box::into_raw(Box::default()); + let mem_state: *mut MemoryState = Box::into_raw(Box::default()); let mut state = ffi::lua_newstate(ALLOCATOR, mem_state as *mut c_void); // If state is null then switch to Lua internal allocator if state.is_null() { drop(Box::from_raw(mem_state)); - mem_state = ptr::null_mut(); state = ffi::luaL_newstate(); } assert!(!state.is_null(), "Failed to instantiate Lua VM"); @@ -404,7 +403,6 @@ impl Lua { let lua = Lua::init_from_ptr(state); let extra = lua.extra.get(); - (*extra).mem_state = NonNull::new(mem_state); mlua_expect!( load_from_std_lib(state, libs), @@ -514,7 +512,6 @@ impl Lua { app_data: AppData::default(), safe: false, libs: StdLib::NONE, - mem_state: None, #[cfg(feature = "module")] skip_memory_check: false, ref_thread, @@ -547,14 +544,8 @@ impl Lua { // Store it in the registry mlua_expect!( - (|state| { - push_gc_userdata(state, Arc::clone(&extra), true)?; - protect_lua!(state, 1, 0, fn(state) { - let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void; - ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key); - }) - })(main_state), - "Error while storing extra data", + set_extra_data(main_state, &extra), + "Error while storing extra data" ); // Register `DestructedUserdata` type @@ -572,13 +563,6 @@ impl Lua { ); assert_stack(main_state, ffi::LUA_MINSTACK); - // Set Luau callbacks userdata to extra data - // We can use global callbacks userdata since we don't allow C modules in Luau - #[cfg(feature = "luau")] - { - (*ffi::lua_callbacks(main_state)).userdata = extra.get() as *mut c_void; - } - let inner = Arc::new(LuaInner { state: AtomicPtr::new(state), main_state, @@ -1098,9 +1082,9 @@ impl Lua { /// Returns the amount of memory (in bytes) currently used inside this Lua state. pub fn used_memory(&self) -> usize { unsafe { - match (*self.extra.get()).mem_state.map(|x| x.as_ref()) { - Some(mem_state) => mem_state.used_memory(), - None => { + match MemoryState::get(self.main_state) { + mem_state if !mem_state.is_null() => (*mem_state).used_memory(), + _ => { // Get data from the Lua GC let used_kbytes = ffi::lua_gc(self.main_state, ffi::LUA_GCCOUNT, 0); let used_kbytes_rem = ffi::lua_gc(self.main_state, ffi::LUA_GCCOUNTB, 0); @@ -1119,9 +1103,9 @@ impl Lua { /// Does not work in module mode where Lua state is managed externally. pub fn set_memory_limit(&self, limit: usize) -> Result { unsafe { - match (*self.extra.get()).mem_state.map(|mut x| x.as_mut()) { - Some(mem_state) => Ok(mem_state.set_memory_limit(limit)), - None => Err(Error::MemoryLimitNotAvailable), + match MemoryState::get(self.main_state) { + mem_state if !mem_state.is_null() => Ok((*mem_state).set_memory_limit(limit)), + _ => Err(Error::MemoryLimitNotAvailable), } } } @@ -3169,16 +3153,13 @@ impl Lua { #[inline] pub(crate) unsafe fn unlikely_memory_error(&self) -> bool { // MemoryInfo is empty in module mode so we cannot predict memory limits - (*self.extra.get()) - .mem_state - .map(|x| x.as_ref().memory_limit() == 0) - .unwrap_or_else(|| { - // Alternatively, check the special flag (only for module mode) - #[cfg(feature = "module")] - return (*self.extra.get()).skip_memory_check; - #[cfg(not(feature = "module"))] - return false; - }) + match MemoryState::get(self.main_state) { + mem_state if !mem_state.is_null() => (*mem_state).memory_limit() == 0, + #[cfg(feature = "module")] + _ => (*self.extra.get()).skip_memory_check, // Check the special flag (only for module mode) + #[cfg(not(feature = "module"))] + _ => false, + } } #[cfg(feature = "unstable")] @@ -3223,14 +3204,6 @@ impl LuaInner { } } -impl ExtraData { - #[cfg(feature = "luau")] - #[inline] - pub(crate) fn mem_state(&self) -> NonNull { - self.mem_state.unwrap() - } -} - struct StateGuard<'a>(&'a LuaInner, *mut ffi::lua_State); impl<'a> StateGuard<'a> { @@ -3251,6 +3224,15 @@ unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData { (*ffi::lua_callbacks(state)).userdata as *mut ExtraData } +#[cfg(feature = "luau")] +unsafe fn set_extra_data( + state: *mut ffi::lua_State, + extra: &Arc>, +) -> Result<()> { + (*ffi::lua_callbacks(state)).userdata = extra.get() as *mut _; + Ok(()) +} + #[cfg(not(feature = "luau"))] unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData { let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void; @@ -3265,6 +3247,18 @@ unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData { (*extra_ptr).get() } +#[cfg(not(feature = "luau"))] +unsafe fn set_extra_data( + state: *mut ffi::lua_State, + extra: &Arc>, +) -> Result<()> { + push_gc_userdata(state, Arc::clone(extra), true)?; + protect_lua!(state, 1, 0, fn(state) { + let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void; + ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key); + }) +} + // Creates required entries in the metatable cache (see `util::METATABLE_CACHE`) pub(crate) fn init_metatable_cache(cache: &mut FxHashMap) { cache.insert(TypeId::of::>>(), 0); diff --git a/src/memory.rs b/src/memory.rs index e199a75..ed68514 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -2,9 +2,6 @@ use std::alloc::{self, Layout}; use std::os::raw::c_void; use std::ptr; -#[cfg(feature = "luau")] -use crate::lua::ExtraData; - pub(crate) static ALLOCATOR: ffi::lua_Alloc = allocator; #[derive(Default)] @@ -20,6 +17,21 @@ pub(crate) struct MemoryState { } impl MemoryState { + #[inline] + pub(crate) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self { + let mut mem_state = ptr::null_mut(); + #[cfg(feature = "luau")] + { + ffi::lua_getallocf(state, &mut mem_state); + mlua_assert!(!mem_state.is_null(), "Luau state has no allocator userdata"); + } + #[cfg(not(feature = "luau"))] + if ffi::lua_getallocf(state, &mut mem_state) != ALLOCATOR { + mem_state = ptr::null_mut(); + } + mem_state as *mut MemoryState + } + #[inline] pub(crate) fn used_memory(&self) -> usize { self.used_memory as usize @@ -37,36 +49,21 @@ impl MemoryState { prev_limit as usize } - // This function is used primarily for calling `lua_pushcfunction` in lua5.1/jit + // This function is used primarily for calling `lua_pushcfunction` in lua5.1/jit/luau // to bypass the memory limit (if set). - #[cfg(any(feature = "lua51", feature = "luajit"))] + #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))] #[inline] pub(crate) unsafe fn relax_limit_with(state: *mut ffi::lua_State, f: impl FnOnce()) { - let mut mem_state: *mut c_void = ptr::null_mut(); - if ffi::lua_getallocf(state, &mut mem_state) == ALLOCATOR { - (*(mem_state as *mut MemoryState)).ignore_limit = true; + let mem_state = Self::get(state); + if !mem_state.is_null() { + (*mem_state).ignore_limit = true; f(); - (*(mem_state as *mut MemoryState)).ignore_limit = false; + (*mem_state).ignore_limit = false; } else { f(); } } - // Same as the above but for Luau - // It does not have `lua_getallocf` function, so instead we use `lua_callbacks` - #[cfg(feature = "luau")] - #[inline] - pub(crate) unsafe fn relax_limit_with(state: *mut ffi::lua_State, f: impl FnOnce()) { - let extra = (*ffi::lua_callbacks(state)).userdata as *mut ExtraData; - if extra.is_null() { - return f(); - } - let mem_state = (*extra).mem_state(); - (*mem_state.as_ptr()).ignore_limit = true; - f(); - (*mem_state.as_ptr()).ignore_limit = false; - } - // Does nothing apart from calling `f()`, we don't need to bypass any limits #[cfg(any(feature = "lua52", feature = "lua53", feature = "lua54"))] #[inline] @@ -76,12 +73,9 @@ impl MemoryState { // Returns `true` if the memory limit was reached on the last memory operation #[cfg(feature = "luau")] + #[inline] pub(crate) unsafe fn limit_reached(state: *mut ffi::lua_State) -> bool { - let extra = (*ffi::lua_callbacks(state)).userdata as *mut ExtraData; - if extra.is_null() { - return false; - } - (*(*extra).mem_state().as_ptr()).limit_reached + (*Self::get(state)).limit_reached } }