1use std::any::TypeId;
2use std::cell::UnsafeCell;
3use std::mem::MaybeUninit;
4use std::os::raw::{c_int, c_void};
5use std::ptr;
6use std::rc::Rc;
7use std::sync::Arc;
8
9use parking_lot::Mutex;
10use rustc_hash::FxHashMap;
11
12use crate::error::Result;
13use crate::state::RawLua;
14use crate::stdlib::StdLib;
15use crate::types::{AppData, ReentrantMutex, XRc};
16use crate::util::{get_internal_metatable, push_internal_userdata, TypeKey, WrappedFailure};
17
18#[cfg(any(feature = "luau", doc))]
19use crate::chunk::Compiler;
20
21#[cfg(feature = "async")]
22use {futures_util::task::noop_waker_ref, std::ptr::NonNull, std::task::Waker};
23
24use super::{Lua, WeakLua};
25
26static EXTRA_REGISTRY_KEY: u8 = 0;
28
29const WRAPPED_FAILURE_POOL_SIZE: usize = 64;
30const REF_STACK_RESERVE: c_int = 1;
31
32pub(crate) struct ExtraData {
34 pub(super) lua: MaybeUninit<Lua>,
35 pub(super) weak: MaybeUninit<WeakLua>,
36 pub(super) owned: bool,
37
38 pub(super) registered_userdata: FxHashMap<TypeId, c_int>,
39 pub(super) registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>,
40 pub(super) last_checked_userdata_mt: (*const c_void, Option<TypeId>),
41
42 pub(super) registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
44
45 pub(super) app_data: AppData,
47
48 pub(super) safe: bool,
49 pub(super) libs: StdLib,
50 pub(super) skip_memory_check: bool,
52
53 pub(super) ref_thread: *mut ffi::lua_State,
55 pub(super) ref_stack_size: c_int,
56 pub(super) ref_stack_top: c_int,
57 pub(super) ref_free: Vec<c_int>,
58
59 pub(super) wrapped_failure_pool: Vec<c_int>,
61 #[cfg(feature = "async")]
63 pub(super) thread_pool: Vec<c_int>,
64
65 pub(super) wrapped_failure_mt_ptr: *const c_void,
67
68 #[cfg(feature = "async")]
70 pub(super) waker: NonNull<Waker>,
71
72 #[cfg(not(feature = "luau"))]
73 pub(super) hook_callback: Option<crate::types::HookCallback>,
74 #[cfg(not(feature = "luau"))]
75 pub(super) hook_thread: *mut ffi::lua_State,
76 #[cfg(feature = "lua54")]
77 pub(super) warn_callback: Option<crate::types::WarnCallback>,
78 #[cfg(feature = "luau")]
79 pub(super) interrupt_callback: Option<crate::types::InterruptCallback>,
80
81 #[cfg(feature = "luau")]
82 pub(super) sandboxed: bool,
83 #[cfg(feature = "luau")]
84 pub(super) compiler: Option<Compiler>,
85 #[cfg(feature = "luau-jit")]
86 pub(super) enable_jit: bool,
87}
88
89impl Drop for ExtraData {
90 fn drop(&mut self) {
91 unsafe {
92 if !self.owned {
93 self.lua.assume_init_drop();
94 }
95
96 self.weak.assume_init_drop();
97 }
98 *self.registry_unref_list.lock() = None;
99 }
100}
101
102static EXTRA_TYPE_KEY: u8 = 0;
103
104impl TypeKey for XRc<UnsafeCell<ExtraData>> {
105 #[inline(always)]
106 fn type_key() -> *const c_void {
107 &EXTRA_TYPE_KEY as *const u8 as *const c_void
108 }
109}
110
111impl ExtraData {
112 #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
114 pub(super) const ERROR_TRACEBACK_IDX: c_int = 1;
115
116 pub(super) unsafe fn init(state: *mut ffi::lua_State, owned: bool) -> XRc<UnsafeCell<Self>> {
117 let ref_thread = mlua_expect!(
120 protect_lua!(state, 0, 0, |state| {
121 let thread = ffi::lua_newthread(state);
122 ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
123 thread
124 }),
125 "Error while creating ref thread",
126 );
127
128 let wrapped_failure_mt_ptr = {
129 get_internal_metatable::<WrappedFailure>(state);
130 let ptr = ffi::lua_topointer(state, -1);
131 ffi::lua_pop(state, 1);
132 ptr
133 };
134
135 #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
137 {
138 ffi::lua_pushcfunction(ref_thread, crate::util::error_traceback);
139 assert_eq!(ffi::lua_gettop(ref_thread), Self::ERROR_TRACEBACK_IDX);
140 }
141
142 #[allow(clippy::arc_with_non_send_sync)]
143 let extra = XRc::new(UnsafeCell::new(ExtraData {
144 lua: MaybeUninit::uninit(),
145 weak: MaybeUninit::uninit(),
146 owned,
147 registered_userdata: FxHashMap::default(),
148 registered_userdata_mt: FxHashMap::default(),
149 last_checked_userdata_mt: (ptr::null(), None),
150 registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
151 app_data: AppData::default(),
152 safe: false,
153 libs: StdLib::NONE,
154 skip_memory_check: false,
155 ref_thread,
156 ref_stack_size: ffi::LUA_MINSTACK - REF_STACK_RESERVE,
158 ref_stack_top: ffi::lua_gettop(ref_thread),
159 ref_free: Vec::new(),
160 wrapped_failure_pool: Vec::with_capacity(WRAPPED_FAILURE_POOL_SIZE),
161 #[cfg(feature = "async")]
162 thread_pool: Vec::new(),
163 wrapped_failure_mt_ptr,
164 #[cfg(feature = "async")]
165 waker: NonNull::from(noop_waker_ref()),
166 #[cfg(not(feature = "luau"))]
167 hook_callback: None,
168 #[cfg(not(feature = "luau"))]
169 hook_thread: ptr::null_mut(),
170 #[cfg(feature = "lua54")]
171 warn_callback: None,
172 #[cfg(feature = "luau")]
173 interrupt_callback: None,
174 #[cfg(feature = "luau")]
175 sandboxed: false,
176 #[cfg(feature = "luau")]
177 compiler: None,
178 #[cfg(feature = "luau-jit")]
179 enable_jit: true,
180 }));
181
182 mlua_expect!(Self::store(&extra, state), "Error while storing extra data");
184
185 extra
186 }
187
188 pub(super) unsafe fn set_lua(&mut self, raw: &XRc<ReentrantMutex<RawLua>>) {
189 self.lua.write(Lua {
190 raw: XRc::clone(raw),
191 collect_garbage: false,
192 });
193 if self.owned {
194 XRc::decrement_strong_count(XRc::as_ptr(raw));
195 }
196 self.weak.write(WeakLua(XRc::downgrade(raw)));
197 }
198
199 pub(super) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self {
200 #[cfg(feature = "luau")]
201 if cfg!(not(feature = "module")) {
202 return (*ffi::lua_callbacks(state)).userdata as *mut _;
204 }
205
206 let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
207 if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key) != ffi::LUA_TUSERDATA {
208 ffi::lua_pop(state, 1);
211 return ptr::null_mut();
212 }
213 let extra_ptr = ffi::lua_touserdata(state, -1) as *mut Rc<UnsafeCell<ExtraData>>;
214 ffi::lua_pop(state, 1);
215 (*extra_ptr).get()
216 }
217
218 unsafe fn store(extra: &XRc<UnsafeCell<Self>>, state: *mut ffi::lua_State) -> Result<()> {
219 #[cfg(feature = "luau")]
220 if cfg!(not(feature = "module")) {
221 (*ffi::lua_callbacks(state)).userdata = extra.get() as *mut _;
222 return Ok(());
223 }
224
225 push_internal_userdata(state, XRc::clone(extra), true)?;
226 protect_lua!(state, 1, 0, fn(state) {
227 let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
228 ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key);
229 })
230 }
231
232 #[inline(always)]
233 pub(super) unsafe fn lua(&self) -> &Lua {
234 self.lua.assume_init_ref()
235 }
236
237 #[inline(always)]
238 pub(super) unsafe fn raw_lua(&self) -> &RawLua {
239 &*self.lua.assume_init_ref().raw.data_ptr()
240 }
241
242 #[inline(always)]
243 pub(super) unsafe fn weak(&self) -> &WeakLua {
244 self.weak.assume_init_ref()
245 }
246}