mlua_codemp_patch/state/
raw.rs

1use std::any::TypeId;
2use std::cell::{Cell, UnsafeCell};
3use std::ffi::{CStr, CString};
4use std::os::raw::{c_char, c_int, c_void};
5use std::panic::resume_unwind;
6use std::result::Result as StdResult;
7use std::sync::Arc;
8use std::{mem, ptr};
9
10use crate::chunk::ChunkMode;
11use crate::error::{Error, Result};
12use crate::function::Function;
13use crate::memory::{MemoryState, ALLOCATOR};
14use crate::state::util::{callback_error_ext, ref_stack_pop, StateGuard};
15use crate::stdlib::StdLib;
16use crate::string::String;
17use crate::table::Table;
18use crate::thread::Thread;
19use crate::types::{
20    AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData,
21    MaybeSend, ReentrantMutex, RegistryKey, SubtypeId, ValueRef, XRc,
22};
23use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataRegistry, UserDataVariant};
24use crate::util::{
25    assert_stack, check_stack, get_destructed_userdata_metatable, get_internal_userdata, get_main_state,
26    get_userdata, init_error_registry, init_internal_metatable, init_userdata_metatable, pop_error,
27    push_internal_userdata, push_string, push_table, rawset_field, safe_pcall, safe_xpcall, short_type_name,
28    StackGuard, WrappedFailure,
29};
30use crate::value::{FromLuaMulti, IntoLua, MultiValue, Nil, Value};
31
32use super::extra::ExtraData;
33use super::{Lua, LuaOptions, WeakLua};
34
35#[cfg(not(feature = "luau"))]
36use crate::hook::{Debug, HookTriggers};
37
38#[cfg(feature = "async")]
39use {
40    crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
41    std::ptr::NonNull,
42    std::task::{Context, Poll, Waker},
43};
44
45/// An inner Lua struct which holds a raw Lua state.
46pub struct RawLua {
47    // The state is dynamic and depends on context
48    pub(super) state: Cell<*mut ffi::lua_State>,
49    pub(super) main_state: *mut ffi::lua_State,
50    pub(super) extra: XRc<UnsafeCell<ExtraData>>,
51}
52
53impl Drop for RawLua {
54    fn drop(&mut self) {
55        unsafe {
56            if !(*self.extra.get()).owned {
57                return;
58            }
59
60            let mem_state = MemoryState::get(self.main_state);
61
62            ffi::lua_close(self.main_state);
63
64            // Deallocate `MemoryState`
65            if !mem_state.is_null() {
66                drop(Box::from_raw(mem_state));
67            }
68        }
69    }
70}
71
72#[cfg(feature = "send")]
73unsafe impl Send for RawLua {}
74
75impl RawLua {
76    #[inline(always)]
77    pub(crate) fn lua(&self) -> &Lua {
78        unsafe { (*self.extra.get()).lua() }
79    }
80
81    #[inline(always)]
82    pub(crate) fn weak(&self) -> &WeakLua {
83        unsafe { (*self.extra.get()).weak() }
84    }
85
86    #[inline(always)]
87    pub(crate) fn state(&self) -> *mut ffi::lua_State {
88        self.state.get()
89    }
90
91    #[cfg(feature = "luau")]
92    #[inline(always)]
93    pub(crate) fn main_state(&self) -> *mut ffi::lua_State {
94        self.main_state
95    }
96
97    #[inline(always)]
98    pub(crate) fn ref_thread(&self) -> *mut ffi::lua_State {
99        unsafe { (*self.extra.get()).ref_thread }
100    }
101
102    pub(super) unsafe fn new(libs: StdLib, options: LuaOptions) -> XRc<ReentrantMutex<Self>> {
103        let mem_state: *mut MemoryState = Box::into_raw(Box::default());
104        let mut state = ffi::lua_newstate(ALLOCATOR, mem_state as *mut c_void);
105        // If state is null then switch to Lua internal allocator
106        if state.is_null() {
107            drop(Box::from_raw(mem_state));
108            state = ffi::luaL_newstate();
109        }
110        assert!(!state.is_null(), "Failed to create a Lua VM");
111
112        ffi::luaL_requiref(state, cstr!("_G"), ffi::luaopen_base, 1);
113        ffi::lua_pop(state, 1);
114
115        // Init Luau code generator (jit)
116        #[cfg(feature = "luau-jit")]
117        if ffi::luau_codegen_supported() != 0 {
118            ffi::luau_codegen_create(state);
119        }
120
121        let rawlua = Self::init_from_ptr(state, true);
122        let extra = rawlua.lock().extra.get();
123
124        mlua_expect!(
125            load_from_std_lib(state, libs),
126            "Error during loading standard libraries"
127        );
128        (*extra).libs |= libs;
129
130        if !options.catch_rust_panics {
131            mlua_expect!(
132                (|| -> Result<()> {
133                    let _sg = StackGuard::new(state);
134
135                    #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
136                    ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
137                    #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
138                    ffi::lua_pushvalue(state, ffi::LUA_GLOBALSINDEX);
139
140                    ffi::lua_pushcfunction(state, safe_pcall);
141                    rawset_field(state, -2, "pcall")?;
142
143                    ffi::lua_pushcfunction(state, safe_xpcall);
144                    rawset_field(state, -2, "xpcall")?;
145
146                    Ok(())
147                })(),
148                "Error during applying option `catch_rust_panics`"
149            )
150        }
151
152        #[cfg(feature = "async")]
153        if options.thread_pool_size > 0 {
154            (*extra).thread_pool.reserve_exact(options.thread_pool_size);
155        }
156
157        rawlua
158    }
159
160    pub(super) unsafe fn init_from_ptr(state: *mut ffi::lua_State, owned: bool) -> XRc<ReentrantMutex<Self>> {
161        assert!(!state.is_null(), "Lua state is NULL");
162        if let Some(lua) = Self::try_from_ptr(state) {
163            return lua;
164        }
165
166        let main_state = get_main_state(state).unwrap_or(state);
167        let main_state_top = ffi::lua_gettop(main_state);
168
169        mlua_expect!(
170            (|state| {
171                init_error_registry(state)?;
172
173                // Create the internal metatables and store them in the registry
174                // to prevent from being garbage collected.
175
176                init_internal_metatable::<XRc<UnsafeCell<ExtraData>>>(state, None)?;
177                init_internal_metatable::<Callback>(state, None)?;
178                init_internal_metatable::<CallbackUpvalue>(state, None)?;
179                #[cfg(feature = "async")]
180                {
181                    init_internal_metatable::<AsyncCallback>(state, None)?;
182                    init_internal_metatable::<AsyncCallbackUpvalue>(state, None)?;
183                    init_internal_metatable::<AsyncPollUpvalue>(state, None)?;
184                    init_internal_metatable::<Option<Waker>>(state, None)?;
185                }
186
187                // Init serde metatables
188                #[cfg(feature = "serialize")]
189                crate::serde::init_metatables(state)?;
190
191                Ok::<_, Error>(())
192            })(main_state),
193            "Error during Lua initialization",
194        );
195
196        // Init ExtraData
197        let extra = ExtraData::init(main_state, owned);
198
199        // Register `DestructedUserdata` type
200        get_destructed_userdata_metatable(main_state);
201        let destructed_mt_ptr = ffi::lua_topointer(main_state, -1);
202        let destructed_ud_typeid = TypeId::of::<DestructedUserdata>();
203        (*extra.get())
204            .registered_userdata_mt
205            .insert(destructed_mt_ptr, Some(destructed_ud_typeid));
206        ffi::lua_pop(main_state, 1);
207
208        mlua_debug_assert!(
209            ffi::lua_gettop(main_state) == main_state_top,
210            "stack leak during creation"
211        );
212        assert_stack(main_state, ffi::LUA_MINSTACK);
213
214        #[allow(clippy::arc_with_non_send_sync)]
215        let rawlua = XRc::new(ReentrantMutex::new(RawLua {
216            state: Cell::new(state),
217            main_state,
218            extra: XRc::clone(&extra),
219        }));
220        (*extra.get()).set_lua(&rawlua);
221
222        rawlua
223    }
224
225    unsafe fn try_from_ptr(state: *mut ffi::lua_State) -> Option<XRc<ReentrantMutex<Self>>> {
226        match ExtraData::get(state) {
227            extra if extra.is_null() => None,
228            extra => Some(XRc::clone(&(*extra).lua().raw)),
229        }
230    }
231
232    /// Marks the Lua state as safe.
233    #[inline(always)]
234    pub(super) unsafe fn set_safe(&self) {
235        (*self.extra.get()).safe = true;
236    }
237
238    /// Loads the specified subset of the standard libraries into an existing Lua state.
239    ///
240    /// Use the [`StdLib`] flags to specify the libraries you want to load.
241    ///
242    /// [`StdLib`]: crate::StdLib
243    pub(super) unsafe fn load_std_libs(&self, libs: StdLib) -> Result<()> {
244        let is_safe = (*self.extra.get()).safe;
245
246        #[cfg(not(feature = "luau"))]
247        if is_safe && libs.contains(StdLib::DEBUG) {
248            return Err(Error::SafetyError(
249                "the unsafe `debug` module can't be loaded in safe mode".to_string(),
250            ));
251        }
252        #[cfg(feature = "luajit")]
253        if is_safe && libs.contains(StdLib::FFI) {
254            return Err(Error::SafetyError(
255                "the unsafe `ffi` module can't be loaded in safe mode".to_string(),
256            ));
257        }
258
259        let res = load_from_std_lib(self.main_state, libs);
260
261        // If `package` library loaded into a safe lua state then disable C modules
262        let curr_libs = (*self.extra.get()).libs;
263        if is_safe && (curr_libs ^ (curr_libs | libs)).contains(StdLib::PACKAGE) {
264            mlua_expect!(self.lua().disable_c_modules(), "Error during disabling C modules");
265        }
266        unsafe { (*self.extra.get()).libs |= libs };
267
268        res
269    }
270
271    /// See [`Lua::try_set_app_data`]
272    #[inline]
273    pub(crate) fn try_set_app_data<T: MaybeSend + 'static>(&self, data: T) -> StdResult<Option<T>, T> {
274        let extra = unsafe { &*self.extra.get() };
275        extra.app_data.try_insert(data)
276    }
277
278    /// See [`Lua::app_data_ref`]
279    #[track_caller]
280    #[inline]
281    pub(crate) fn app_data_ref<T: 'static>(&self) -> Option<AppDataRef<T>> {
282        let extra = unsafe { &*self.extra.get() };
283        extra.app_data.borrow(None)
284    }
285
286    /// See [`Lua::app_data_mut`]
287    #[track_caller]
288    #[inline]
289    pub(crate) fn app_data_mut<T: 'static>(&self) -> Option<AppDataRefMut<T>> {
290        let extra = unsafe { &*self.extra.get() };
291        extra.app_data.borrow_mut(None)
292    }
293
294    /// See [`Lua::create_registry_value`]
295    #[inline]
296    pub(crate) fn owns_registry_value(&self, key: &RegistryKey) -> bool {
297        let registry_unref_list = unsafe { &(*self.extra.get()).registry_unref_list };
298        Arc::ptr_eq(&key.unref_list, registry_unref_list)
299    }
300
301    pub(crate) fn load_chunk(
302        &self,
303        name: Option<&CStr>,
304        env: Option<&Table>,
305        mode: Option<ChunkMode>,
306        source: &[u8],
307    ) -> Result<Function> {
308        let state = self.state();
309        unsafe {
310            let _sg = StackGuard::new(state);
311            check_stack(state, 2)?;
312
313            let mode_str = match mode {
314                Some(ChunkMode::Binary) => cstr!("b"),
315                Some(ChunkMode::Text) => cstr!("t"),
316                None => cstr!("bt"),
317            };
318
319            match ffi::luaL_loadbufferenv(
320                state,
321                source.as_ptr() as *const c_char,
322                source.len(),
323                name.map(|n| n.as_ptr()).unwrap_or_else(ptr::null),
324                mode_str,
325                match env {
326                    Some(env) => {
327                        self.push_ref(&env.0);
328                        -1
329                    }
330                    _ => 0,
331                },
332            ) {
333                ffi::LUA_OK => {
334                    #[cfg(feature = "luau-jit")]
335                    if (*self.extra.get()).enable_jit && ffi::luau_codegen_supported() != 0 {
336                        ffi::luau_codegen_compile(state, -1);
337                    }
338
339                    Ok(Function(self.pop_ref()))
340                }
341                err => Err(pop_error(state, err)),
342            }
343        }
344    }
345
346    /// Sets a 'hook' function for a thread (coroutine).
347    #[cfg(not(feature = "luau"))]
348    pub(crate) unsafe fn set_thread_hook<F>(
349        &self,
350        state: *mut ffi::lua_State,
351        triggers: HookTriggers,
352        callback: F,
353    ) where
354        F: Fn(&Lua, Debug) -> Result<()> + MaybeSend + 'static,
355    {
356        unsafe extern "C-unwind" fn hook_proc(state: *mut ffi::lua_State, ar: *mut ffi::lua_Debug) {
357            let extra = ExtraData::get(state);
358            if (*extra).hook_thread != state {
359                // Hook was destined for a different thread, ignore
360                ffi::lua_sethook(state, None, 0, 0);
361                return;
362            }
363            callback_error_ext(state, extra, move |extra, _| {
364                let hook_cb = (*extra).hook_callback.clone();
365                let hook_cb = mlua_expect!(hook_cb, "no hook callback set in hook_proc");
366                if std::rc::Rc::strong_count(&hook_cb) > 2 {
367                    return Ok(()); // Don't allow recursion
368                }
369                let rawlua = (*extra).raw_lua();
370                let _guard = StateGuard::new(rawlua, state);
371                let debug = Debug::new(rawlua, ar);
372                hook_cb((*extra).lua(), debug)
373            })
374        }
375
376        (*self.extra.get()).hook_callback = Some(std::rc::Rc::new(callback));
377        (*self.extra.get()).hook_thread = state; // Mark for what thread the hook is set
378        ffi::lua_sethook(state, Some(hook_proc), triggers.mask(), triggers.count());
379    }
380
381    /// See [`Lua::create_string`]
382    pub(crate) unsafe fn create_string(&self, s: impl AsRef<[u8]>) -> Result<String> {
383        let state = self.state();
384        if self.unlikely_memory_error() {
385            push_string(self.ref_thread(), s.as_ref(), false)?;
386            return Ok(String(self.pop_ref_thread()));
387        }
388
389        let _sg = StackGuard::new(state);
390        check_stack(state, 3)?;
391        push_string(state, s.as_ref(), true)?;
392        Ok(String(self.pop_ref()))
393    }
394
395    /// See [`Lua::create_table_with_capacity`]
396    pub(crate) unsafe fn create_table_with_capacity(&self, narr: usize, nrec: usize) -> Result<Table> {
397        if self.unlikely_memory_error() {
398            push_table(self.ref_thread(), narr, nrec, false)?;
399            return Ok(Table(self.pop_ref_thread()));
400        }
401
402        let state = self.state();
403        let _sg = StackGuard::new(state);
404        check_stack(state, 3)?;
405        push_table(state, narr, nrec, true)?;
406        Ok(Table(self.pop_ref()))
407    }
408
409    /// See [`Lua::create_sequence_from`]
410    pub(crate) unsafe fn create_sequence_from<T, I>(&self, iter: I) -> Result<Table>
411    where
412        T: IntoLua,
413        I: IntoIterator<Item = T>,
414    {
415        let state = self.state();
416        let _sg = StackGuard::new(state);
417        check_stack(state, 5)?;
418
419        let iter = iter.into_iter();
420        let lower_bound = iter.size_hint().0;
421        let protect = !self.unlikely_memory_error();
422        push_table(state, lower_bound, 0, protect)?;
423        for (i, v) in iter.enumerate() {
424            self.push(v)?;
425            if protect {
426                protect_lua!(state, 2, 1, |state| {
427                    ffi::lua_rawseti(state, -2, (i + 1) as Integer);
428                })?;
429            } else {
430                ffi::lua_rawseti(state, -2, (i + 1) as Integer);
431            }
432        }
433
434        Ok(Table(self.pop_ref()))
435    }
436
437    /// Wraps a Lua function into a new thread (or coroutine).
438    ///
439    /// Takes function by reference.
440    pub(crate) unsafe fn create_thread(&self, func: &Function) -> Result<Thread> {
441        let state = self.state();
442        let _sg = StackGuard::new(state);
443        check_stack(state, 3)?;
444
445        let thread_state = if self.unlikely_memory_error() {
446            ffi::lua_newthread(state)
447        } else {
448            protect_lua!(state, 0, 1, |state| ffi::lua_newthread(state))?
449        };
450        let thread = Thread(self.pop_ref(), thread_state);
451        ffi::lua_xpush(self.ref_thread(), thread_state, func.0.index);
452        Ok(thread)
453    }
454
455    /// Wraps a Lua function into a new or recycled thread (coroutine).
456    #[cfg(feature = "async")]
457    pub(crate) unsafe fn create_recycled_thread(&self, func: &Function) -> Result<Thread> {
458        #[cfg(any(feature = "lua54", feature = "luau"))]
459        if let Some(index) = (*self.extra.get()).thread_pool.pop() {
460            let thread_state = ffi::lua_tothread(self.ref_thread(), index);
461            ffi::lua_xpush(self.ref_thread(), thread_state, func.0.index);
462
463            #[cfg(feature = "luau")]
464            {
465                // Inherit `LUA_GLOBALSINDEX` from the caller
466                ffi::lua_xpush(self.state(), thread_state, ffi::LUA_GLOBALSINDEX);
467                ffi::lua_replace(thread_state, ffi::LUA_GLOBALSINDEX);
468            }
469
470            return Ok(Thread(ValueRef::new(self, index), thread_state));
471        }
472
473        self.create_thread(func)
474    }
475
476    /// Resets thread (coroutine) and returns it to the pool for later use.
477    #[cfg(feature = "async")]
478    #[cfg(any(feature = "lua54", feature = "luau"))]
479    pub(crate) unsafe fn recycle_thread(&self, thread: &mut Thread) -> bool {
480        let extra = &mut *self.extra.get();
481        if extra.thread_pool.len() < extra.thread_pool.capacity() {
482            let thread_state = ffi::lua_tothread(extra.ref_thread, thread.0.index);
483            #[cfg(all(feature = "lua54", not(feature = "vendored")))]
484            let status = ffi::lua_resetthread(thread_state);
485            #[cfg(all(feature = "lua54", feature = "vendored"))]
486            let status = ffi::lua_closethread(thread_state, self.state());
487            #[cfg(feature = "lua54")]
488            if status != ffi::LUA_OK {
489                // Error object is on top, drop it
490                ffi::lua_settop(thread_state, 0);
491            }
492            #[cfg(feature = "luau")]
493            ffi::lua_resetthread(thread_state);
494            extra.thread_pool.push(thread.0.index);
495            thread.0.drop = false; // Prevent thread from being garbage collected
496            return true;
497        }
498        false
499    }
500
501    /// Pushes a value that implements `IntoLua` onto the Lua stack.
502    ///
503    /// Uses 2 stack spaces, does not call checkstack.
504    #[doc(hidden)]
505    #[inline(always)]
506    pub unsafe fn push(&self, value: impl IntoLua) -> Result<()> {
507        value.push_into_stack(self)
508    }
509
510    /// Pushes a `Value` (by reference) onto the Lua stack.
511    ///
512    /// Uses 2 stack spaces, does not call `checkstack`.
513    pub(crate) unsafe fn push_value(&self, value: &Value) -> Result<()> {
514        let state = self.state();
515        match value {
516            Value::Nil => ffi::lua_pushnil(state),
517            Value::Boolean(b) => ffi::lua_pushboolean(state, *b as c_int),
518            Value::LightUserData(ud) => ffi::lua_pushlightuserdata(state, ud.0),
519            Value::Integer(i) => ffi::lua_pushinteger(state, *i),
520            Value::Number(n) => ffi::lua_pushnumber(state, *n),
521            #[cfg(feature = "luau")]
522            Value::Vector(v) => {
523                #[cfg(not(feature = "luau-vector4"))]
524                ffi::lua_pushvector(state, v.x(), v.y(), v.z());
525                #[cfg(feature = "luau-vector4")]
526                ffi::lua_pushvector(state, v.x(), v.y(), v.z(), v.w());
527            }
528            Value::String(s) => self.push_ref(&s.0),
529            Value::Table(t) => self.push_ref(&t.0),
530            Value::Function(f) => self.push_ref(&f.0),
531            Value::Thread(t) => self.push_ref(&t.0),
532            Value::UserData(ud) => self.push_ref(&ud.0),
533            Value::Error(err) => {
534                let protect = !self.unlikely_memory_error();
535                push_internal_userdata(state, WrappedFailure::Error(*err.clone()), protect)?;
536            }
537        }
538        Ok(())
539    }
540
541    /// Pops a value from the Lua stack.
542    ///
543    /// Uses 2 stack spaces, does not call `checkstack`.
544    pub(crate) unsafe fn pop_value(&self) -> Value {
545        let value = self.stack_value(-1, None);
546        ffi::lua_pop(self.state(), 1);
547        value
548    }
549
550    /// Returns value at given stack index without popping it.
551    ///
552    /// Uses 2 stack spaces, does not call checkstack.
553    pub(crate) unsafe fn stack_value(&self, idx: c_int, type_hint: Option<c_int>) -> Value {
554        let state = self.state();
555        match type_hint.unwrap_or_else(|| ffi::lua_type(state, idx)) {
556            ffi::LUA_TNIL => Nil,
557
558            ffi::LUA_TBOOLEAN => Value::Boolean(ffi::lua_toboolean(state, idx) != 0),
559
560            ffi::LUA_TLIGHTUSERDATA => Value::LightUserData(LightUserData(ffi::lua_touserdata(state, idx))),
561
562            #[cfg(any(feature = "lua54", feature = "lua53"))]
563            ffi::LUA_TNUMBER => {
564                if ffi::lua_isinteger(state, idx) != 0 {
565                    Value::Integer(ffi::lua_tointeger(state, idx))
566                } else {
567                    Value::Number(ffi::lua_tonumber(state, idx))
568                }
569            }
570
571            #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit", feature = "luau"))]
572            ffi::LUA_TNUMBER => {
573                use crate::types::Number;
574
575                let n = ffi::lua_tonumber(state, idx);
576                match num_traits::cast(n) {
577                    Some(i) if (n - (i as Number)).abs() < Number::EPSILON => Value::Integer(i),
578                    _ => Value::Number(n),
579                }
580            }
581
582            #[cfg(feature = "luau")]
583            ffi::LUA_TVECTOR => {
584                let v = ffi::lua_tovector(state, idx);
585                mlua_debug_assert!(!v.is_null(), "vector is null");
586                #[cfg(not(feature = "luau-vector4"))]
587                return Value::Vector(crate::types::Vector([*v, *v.add(1), *v.add(2)]));
588                #[cfg(feature = "luau-vector4")]
589                return Value::Vector(crate::types::Vector([*v, *v.add(1), *v.add(2), *v.add(3)]));
590            }
591
592            ffi::LUA_TSTRING => {
593                ffi::lua_xpush(state, self.ref_thread(), idx);
594                Value::String(String(self.pop_ref_thread()))
595            }
596
597            ffi::LUA_TTABLE => {
598                ffi::lua_xpush(state, self.ref_thread(), idx);
599                Value::Table(Table(self.pop_ref_thread()))
600            }
601
602            ffi::LUA_TFUNCTION => {
603                ffi::lua_xpush(state, self.ref_thread(), idx);
604                Value::Function(Function(self.pop_ref_thread()))
605            }
606
607            ffi::LUA_TUSERDATA => {
608                // If the userdata is `WrappedFailure`, process it as an error or panic.
609                let failure_mt_ptr = (*self.extra.get()).wrapped_failure_mt_ptr;
610                match get_internal_userdata::<WrappedFailure>(state, idx, failure_mt_ptr).as_mut() {
611                    Some(WrappedFailure::Error(err)) => Value::Error(Box::new(err.clone())),
612                    Some(WrappedFailure::Panic(panic)) => {
613                        if let Some(panic) = panic.take() {
614                            resume_unwind(panic);
615                        }
616                        // Previously resumed panic?
617                        Value::Nil
618                    }
619                    _ => {
620                        ffi::lua_xpush(state, self.ref_thread(), idx);
621                        Value::UserData(AnyUserData(self.pop_ref_thread(), SubtypeId::None))
622                    }
623                }
624            }
625
626            ffi::LUA_TTHREAD => {
627                ffi::lua_xpush(state, self.ref_thread(), idx);
628                let thread_state = ffi::lua_tothread(self.ref_thread(), -1);
629                Value::Thread(Thread(self.pop_ref_thread(), thread_state))
630            }
631
632            #[cfg(feature = "luau")]
633            ffi::LUA_TBUFFER => {
634                // Buffer is represented as a userdata type
635                ffi::lua_xpush(state, self.ref_thread(), idx);
636                Value::UserData(AnyUserData(self.pop_ref_thread(), SubtypeId::Buffer))
637            }
638
639            #[cfg(feature = "luajit")]
640            ffi::LUA_TCDATA => {
641                // CData is represented as a userdata type
642                ffi::lua_xpush(state, self.ref_thread(), idx);
643                Value::UserData(AnyUserData(self.pop_ref_thread(), SubtypeId::CData))
644            }
645
646            _ => mlua_panic!("unexpected value type on stack"),
647        }
648    }
649
650    // Pushes a ValueRef value onto the stack, uses 1 stack space, does not call checkstack
651    #[inline]
652    pub(crate) fn push_ref(&self, vref: &ValueRef) {
653        assert!(
654            self.weak() == &vref.lua,
655            "Lua instance passed Value created from a different main Lua state"
656        );
657        unsafe { ffi::lua_xpush(self.ref_thread(), self.state(), vref.index) };
658    }
659
660    // Pops the topmost element of the stack and stores a reference to it. This pins the object,
661    // preventing garbage collection until the returned `ValueRef` is dropped.
662    //
663    // References are stored on the stack of a specially created auxiliary thread that exists only
664    // to store reference values. This is much faster than storing these in the registry, and also
665    // much more flexible and requires less bookkeeping than storing them directly in the currently
666    // used stack.
667    #[inline]
668    pub(crate) unsafe fn pop_ref(&self) -> ValueRef {
669        ffi::lua_xmove(self.state(), self.ref_thread(), 1);
670        let index = ref_stack_pop(self.extra.get());
671        ValueRef::new(self, index)
672    }
673
674    // Same as `pop_ref` but assumes the value is already on the reference thread
675    #[inline]
676    pub(crate) unsafe fn pop_ref_thread(&self) -> ValueRef {
677        let index = ref_stack_pop(self.extra.get());
678        ValueRef::new(self, index)
679    }
680
681    #[inline]
682    pub(crate) unsafe fn clone_ref(&self, vref: &ValueRef) -> ValueRef {
683        ffi::lua_pushvalue(self.ref_thread(), vref.index);
684        let index = ref_stack_pop(self.extra.get());
685        ValueRef::new(self, index)
686    }
687
688    pub(crate) unsafe fn drop_ref(&self, vref: &ValueRef) {
689        let ref_thread = self.ref_thread();
690        ffi::lua_pushnil(ref_thread);
691        ffi::lua_replace(ref_thread, vref.index);
692        (*self.extra.get()).ref_free.push(vref.index);
693    }
694
695    #[inline]
696    pub(crate) unsafe fn push_error_traceback(&self) {
697        let state = self.state();
698        #[cfg(any(feature = "lua51", feature = "luajit", feature = "luau"))]
699        ffi::lua_xpush(self.ref_thread(), state, ExtraData::ERROR_TRACEBACK_IDX);
700        // Lua 5.2+ support light C functions that does not require extra allocations
701        #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
702        ffi::lua_pushcfunction(state, crate::util::error_traceback);
703    }
704
705    #[inline]
706    pub(crate) unsafe fn unlikely_memory_error(&self) -> bool {
707        // MemoryInfo is empty in module mode so we cannot predict memory limits
708        match MemoryState::get(self.main_state) {
709            mem_state if !mem_state.is_null() => (*mem_state).memory_limit() == 0,
710            _ => (*self.extra.get()).skip_memory_check, // Check the special flag (only for module mode)
711        }
712    }
713
714    pub(crate) unsafe fn make_userdata<T>(&self, data: UserDataVariant<T>) -> Result<AnyUserData>
715    where
716        T: UserData + 'static,
717    {
718        self.make_userdata_with_metatable(data, || {
719            // Check if userdata/metatable is already registered
720            let type_id = TypeId::of::<T>();
721            if let Some(&table_id) = (*self.extra.get()).registered_userdata.get(&type_id) {
722                return Ok(table_id as Integer);
723            }
724
725            // Create a new metatable from `UserData` definition
726            let mut registry = const { UserDataRegistry::new() };
727            T::register(&mut registry);
728
729            self.register_userdata_metatable(registry)
730        })
731    }
732
733    pub(crate) unsafe fn make_any_userdata<T>(&self, data: UserDataVariant<T>) -> Result<AnyUserData>
734    where
735        T: 'static,
736    {
737        self.make_userdata_with_metatable(data, || {
738            // Check if userdata/metatable is already registered
739            let type_id = TypeId::of::<T>();
740            if let Some(&table_id) = (*self.extra.get()).registered_userdata.get(&type_id) {
741                return Ok(table_id as Integer);
742            }
743
744            // Create an empty metatable
745            let registry = const { UserDataRegistry::new() };
746            self.register_userdata_metatable::<T>(registry)
747        })
748    }
749
750    unsafe fn make_userdata_with_metatable<T>(
751        &self,
752        data: UserDataVariant<T>,
753        get_metatable_id: impl FnOnce() -> Result<Integer>,
754    ) -> Result<AnyUserData> {
755        let state = self.state();
756        let _sg = StackGuard::new(state);
757        check_stack(state, 3)?;
758
759        // We push metatable first to ensure having correct metatable with `__gc` method
760        ffi::lua_pushnil(state);
761        ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, get_metatable_id()?);
762        let protect = !self.unlikely_memory_error();
763        #[cfg(not(feature = "lua54"))]
764        crate::util::push_userdata(state, data, protect)?;
765        #[cfg(feature = "lua54")]
766        crate::util::push_userdata_uv(state, data, crate::userdata::USER_VALUE_MAXSLOT as c_int, protect)?;
767        ffi::lua_replace(state, -3);
768        ffi::lua_setmetatable(state, -2);
769
770        // Set empty environment for Lua 5.1
771        #[cfg(any(feature = "lua51", feature = "luajit"))]
772        if protect {
773            protect_lua!(state, 1, 1, fn(state) {
774                ffi::lua_newtable(state);
775                ffi::lua_setuservalue(state, -2);
776            })?;
777        } else {
778            ffi::lua_newtable(state);
779            ffi::lua_setuservalue(state, -2);
780        }
781
782        Ok(AnyUserData(self.pop_ref(), SubtypeId::None))
783    }
784
785    pub(crate) unsafe fn register_userdata_metatable<T: 'static>(
786        &self,
787        mut registry: UserDataRegistry<T>,
788    ) -> Result<Integer> {
789        let state = self.state();
790        let _sg = StackGuard::new(state);
791        check_stack(state, 13)?;
792
793        // Prepare metatable, add meta methods first and then meta fields
794        let metatable_nrec = registry.meta_methods.len() + registry.meta_fields.len();
795        #[cfg(feature = "async")]
796        let metatable_nrec = metatable_nrec + registry.async_meta_methods.len();
797        push_table(state, 0, metatable_nrec, true)?;
798        for (k, m) in registry.meta_methods {
799            self.push(self.create_callback(m)?)?;
800            rawset_field(state, -2, MetaMethod::validate(&k)?)?;
801        }
802        #[cfg(feature = "async")]
803        for (k, m) in registry.async_meta_methods {
804            self.push(self.create_async_callback(m)?)?;
805            rawset_field(state, -2, MetaMethod::validate(&k)?)?;
806        }
807        let mut has_name = false;
808        for (k, push_field) in registry.meta_fields {
809            has_name = has_name || k == MetaMethod::Type;
810            push_field(self)?;
811            rawset_field(state, -2, MetaMethod::validate(&k)?)?;
812        }
813        // Set `__name/__type` if not provided
814        if !has_name {
815            let type_name = short_type_name::<T>();
816            push_string(state, type_name.as_bytes(), !self.unlikely_memory_error())?;
817            rawset_field(state, -2, MetaMethod::Type.name())?;
818        }
819        let metatable_index = ffi::lua_absindex(state, -1);
820
821        let mut extra_tables_count = 0;
822
823        let fields_nrec = registry.fields.len();
824        if fields_nrec > 0 {
825            // If `__index` is a table then update it in-place
826            let index_type = ffi::lua_getfield(state, metatable_index, cstr!("__index"));
827            match index_type {
828                ffi::LUA_TNIL | ffi::LUA_TTABLE => {
829                    if index_type == ffi::LUA_TNIL {
830                        // Create a new table
831                        ffi::lua_pop(state, 1);
832                        push_table(state, 0, fields_nrec, true)?;
833                    }
834                    for (k, push_field) in mem::take(&mut registry.fields) {
835                        push_field(self)?;
836                        rawset_field(state, -2, &k)?;
837                    }
838                    rawset_field(state, metatable_index, "__index")?;
839                }
840                _ => {
841                    ffi::lua_pop(state, 1);
842                    // Fields will be converted to functions and added to field getters
843                }
844            }
845        }
846
847        let mut field_getters_index = None;
848        let field_getters_nrec = registry.field_getters.len() + registry.fields.len();
849        if field_getters_nrec > 0 {
850            push_table(state, 0, field_getters_nrec, true)?;
851            for (k, m) in registry.field_getters {
852                self.push(self.create_callback(m)?)?;
853                rawset_field(state, -2, &k)?;
854            }
855            for (k, push_field) in registry.fields {
856                unsafe extern "C-unwind" fn return_field(state: *mut ffi::lua_State) -> c_int {
857                    ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
858                    1
859                }
860                push_field(self)?;
861                protect_lua!(state, 1, 1, fn(state) {
862                    ffi::lua_pushcclosure(state, return_field, 1);
863                })?;
864                rawset_field(state, -2, &k)?;
865            }
866            field_getters_index = Some(ffi::lua_absindex(state, -1));
867            extra_tables_count += 1;
868        }
869
870        let mut field_setters_index = None;
871        let field_setters_nrec = registry.field_setters.len();
872        if field_setters_nrec > 0 {
873            push_table(state, 0, field_setters_nrec, true)?;
874            for (k, m) in registry.field_setters {
875                self.push(self.create_callback(m)?)?;
876                rawset_field(state, -2, &k)?;
877            }
878            field_setters_index = Some(ffi::lua_absindex(state, -1));
879            extra_tables_count += 1;
880        }
881
882        let mut methods_index = None;
883        let methods_nrec = registry.methods.len();
884        #[cfg(feature = "async")]
885        let methods_nrec = methods_nrec + registry.async_methods.len();
886        if methods_nrec > 0 {
887            // If `__index` is a table then update it in-place
888            let index_type = ffi::lua_getfield(state, metatable_index, cstr!("__index"));
889            match index_type {
890                ffi::LUA_TTABLE => {} // Update the existing table
891                _ => {
892                    // Create a new table
893                    ffi::lua_pop(state, 1);
894                    push_table(state, 0, methods_nrec, true)?;
895                }
896            }
897            for (k, m) in registry.methods {
898                self.push(self.create_callback(m)?)?;
899                rawset_field(state, -2, &k)?;
900            }
901            #[cfg(feature = "async")]
902            for (k, m) in registry.async_methods {
903                self.push(self.create_async_callback(m)?)?;
904                rawset_field(state, -2, &k)?;
905            }
906            match index_type {
907                ffi::LUA_TTABLE => {
908                    ffi::lua_pop(state, 1); // All done
909                }
910                ffi::LUA_TNIL => {
911                    // Set the new table as `__index`
912                    rawset_field(state, metatable_index, "__index")?;
913                }
914                _ => {
915                    methods_index = Some(ffi::lua_absindex(state, -1));
916                    extra_tables_count += 1;
917                }
918            }
919        }
920
921        #[cfg(feature = "luau")]
922        let extra_init = None;
923        #[cfg(not(feature = "luau"))]
924        let extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>> = Some(|state| {
925            ffi::lua_pushcfunction(state, crate::util::userdata_destructor::<UserDataVariant<T>>);
926            rawset_field(state, -2, "__gc")
927        });
928
929        init_userdata_metatable(
930            state,
931            metatable_index,
932            field_getters_index,
933            field_setters_index,
934            methods_index,
935            extra_init,
936        )?;
937
938        // Pop extra tables to get metatable on top of the stack
939        ffi::lua_pop(state, extra_tables_count);
940
941        let mt_ptr = ffi::lua_topointer(state, -1);
942        let id = protect_lua!(state, 1, 0, |state| {
943            ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
944        })?;
945
946        let type_id = TypeId::of::<T>();
947        (*self.extra.get()).registered_userdata.insert(type_id, id);
948        (*self.extra.get())
949            .registered_userdata_mt
950            .insert(mt_ptr, Some(type_id));
951
952        Ok(id as Integer)
953    }
954
955    // #[inline]
956    // pub(crate) unsafe fn register_raw_userdata_metatable(
957    //     &self,
958    //     ptr: *const c_void,
959    //     type_id: Option<TypeId>,
960    // ) {
961    //     (*self.extra.get())
962    //         .registered_userdata_mt
963    //         .insert(ptr, type_id);
964    // }
965
966    // #[inline]
967    // pub(crate) unsafe fn deregister_raw_userdata_metatable(&self, ptr: *const c_void) {
968    //     (*self.extra.get()).registered_userdata_mt.remove(&ptr);
969    //     if (*self.extra.get()).last_checked_userdata_mt.0 == ptr {
970    //         (*self.extra.get()).last_checked_userdata_mt = (ptr::null(), None);
971    //     }
972    // }
973
974    // #[inline(always)]
975    // pub(crate) unsafe fn get_userdata_ref<T: 'static>(&self, idx: c_int) -> Result<UserDataRef<T>> {
976    //     let guard = self.lua().lock_arc();
977    //     (*get_userdata::<UserDataVariant<T>>(self.state(), idx)).try_make_ref(guard)
978    // }
979
980    // Returns `TypeId` for the userdata ref, checking that it's registered and not destructed.
981    //
982    // Returns `None` if the userdata is registered but non-static.
983    pub(crate) unsafe fn get_userdata_ref_type_id(&self, vref: &ValueRef) -> Result<Option<TypeId>> {
984        self.get_userdata_type_id_inner(self.ref_thread(), vref.index)
985    }
986
987    // Same as `get_userdata_ref_type_id` but assumes the userdata is already on the stack.
988    pub(crate) unsafe fn get_userdata_type_id(&self, idx: c_int) -> Result<Option<TypeId>> {
989        self.get_userdata_type_id_inner(self.state(), idx)
990    }
991
992    unsafe fn get_userdata_type_id_inner(
993        &self,
994        state: *mut ffi::lua_State,
995        idx: c_int,
996    ) -> Result<Option<TypeId>> {
997        if ffi::lua_getmetatable(state, idx) == 0 {
998            return Err(Error::UserDataTypeMismatch);
999        }
1000        let mt_ptr = ffi::lua_topointer(state, -1);
1001        ffi::lua_pop(state, 1);
1002
1003        // Fast path to skip looking up the metatable in the map
1004        let (last_mt, last_type_id) = (*self.extra.get()).last_checked_userdata_mt;
1005        if last_mt == mt_ptr {
1006            return Ok(last_type_id);
1007        }
1008
1009        match (*self.extra.get()).registered_userdata_mt.get(&mt_ptr) {
1010            Some(&type_id) if type_id == Some(TypeId::of::<DestructedUserdata>()) => {
1011                Err(Error::UserDataDestructed)
1012            }
1013            Some(&type_id) => {
1014                (*self.extra.get()).last_checked_userdata_mt = (mt_ptr, type_id);
1015                Ok(type_id)
1016            }
1017            None => Err(Error::UserDataTypeMismatch),
1018        }
1019    }
1020
1021    // Pushes a ValueRef (userdata) value onto the stack, returning their `TypeId`.
1022    // Uses 1 stack space, does not call checkstack.
1023    pub(crate) unsafe fn push_userdata_ref(&self, vref: &ValueRef) -> Result<Option<TypeId>> {
1024        let type_id = self.get_userdata_type_id_inner(self.ref_thread(), vref.index)?;
1025        self.push_ref(vref);
1026        Ok(type_id)
1027    }
1028
1029    // Creates a Function out of a Callback containing a 'static Fn.
1030    pub(crate) fn create_callback(&self, func: Callback) -> Result<Function> {
1031        // This is non-scoped version of the callback (upvalue is always valid)
1032        // TODO: add a scoped version
1033        unsafe extern "C-unwind" fn call_callback(state: *mut ffi::lua_State) -> c_int {
1034            let upvalue = get_userdata::<CallbackUpvalue>(state, ffi::lua_upvalueindex(1));
1035            callback_error_ext(state, (*upvalue).extra.get(), |extra, nargs| {
1036                // Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
1037                // The lock must be already held as the callback is executed
1038                let rawlua = (*extra).raw_lua();
1039                let _guard = StateGuard::new(rawlua, state);
1040                let func = &*(*upvalue).data;
1041                func(rawlua, nargs)
1042            })
1043        }
1044
1045        let state = self.state();
1046        unsafe {
1047            let _sg = StackGuard::new(state);
1048            check_stack(state, 4)?;
1049
1050            let extra = XRc::clone(&self.extra);
1051            let protect = !self.unlikely_memory_error();
1052            push_internal_userdata(state, CallbackUpvalue { data: func, extra }, protect)?;
1053            if protect {
1054                protect_lua!(state, 1, 1, fn(state) {
1055                    ffi::lua_pushcclosure(state, call_callback, 1);
1056                })?;
1057            } else {
1058                ffi::lua_pushcclosure(state, call_callback, 1);
1059            }
1060
1061            Ok(Function(self.pop_ref()))
1062        }
1063    }
1064
1065    #[cfg(feature = "async")]
1066    pub(crate) fn create_async_callback(&self, func: AsyncCallback) -> Result<Function> {
1067        // Ensure that the coroutine library is loaded
1068        #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
1069        unsafe {
1070            if !(*self.extra.get()).libs.contains(StdLib::COROUTINE) {
1071                load_from_std_lib(self.main_state, StdLib::COROUTINE)?;
1072                (*self.extra.get()).libs |= StdLib::COROUTINE;
1073            }
1074        }
1075
1076        unsafe extern "C-unwind" fn call_callback(state: *mut ffi::lua_State) -> c_int {
1077            // Async functions cannot be scoped and therefore destroyed,
1078            // so the first upvalue is always valid
1079            let upvalue = get_userdata::<AsyncCallbackUpvalue>(state, ffi::lua_upvalueindex(1));
1080            let extra = (*upvalue).extra.get();
1081            callback_error_ext(state, extra, |extra, nargs| {
1082                // Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
1083                // The lock must be already held as the callback is executed
1084                let rawlua = (*extra).raw_lua();
1085                let _guard = StateGuard::new(rawlua, state);
1086
1087                let func = &*(*upvalue).data;
1088                let fut = func(rawlua, nargs);
1089                let extra = XRc::clone(&(*upvalue).extra);
1090                let protect = !rawlua.unlikely_memory_error();
1091                push_internal_userdata(state, AsyncPollUpvalue { data: fut, extra }, protect)?;
1092                if protect {
1093                    protect_lua!(state, 1, 1, fn(state) {
1094                        ffi::lua_pushcclosure(state, poll_future, 1);
1095                    })?;
1096                } else {
1097                    ffi::lua_pushcclosure(state, poll_future, 1);
1098                }
1099
1100                Ok(1)
1101            })
1102        }
1103
1104        unsafe extern "C-unwind" fn poll_future(state: *mut ffi::lua_State) -> c_int {
1105            let upvalue = get_userdata::<AsyncPollUpvalue>(state, ffi::lua_upvalueindex(1));
1106            callback_error_ext(state, (*upvalue).extra.get(), |extra, _| {
1107                // Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
1108                // The lock must be already held as the future is polled
1109                let rawlua = (*extra).raw_lua();
1110                let _guard = StateGuard::new(rawlua, state);
1111
1112                let fut = &mut (*upvalue).data;
1113                let mut ctx = Context::from_waker(rawlua.waker());
1114                match fut.as_mut().poll(&mut ctx) {
1115                    Poll::Pending => {
1116                        ffi::lua_pushnil(state);
1117                        ffi::lua_pushlightuserdata(state, Lua::poll_pending().0);
1118                        Ok(2)
1119                    }
1120                    Poll::Ready(nresults) => {
1121                        match nresults? {
1122                            nresults if nresults < 3 => {
1123                                // Fast path for up to 2 results without creating a table
1124                                ffi::lua_pushinteger(state, nresults as _);
1125                                if nresults > 0 {
1126                                    ffi::lua_insert(state, -nresults - 1);
1127                                }
1128                                Ok(nresults + 1)
1129                            }
1130                            nresults => {
1131                                let results = MultiValue::from_stack_multi(nresults, rawlua)?;
1132                                ffi::lua_pushinteger(state, nresults as _);
1133                                rawlua.push(rawlua.create_sequence_from(results)?)?;
1134                                Ok(2)
1135                            }
1136                        }
1137                    }
1138                }
1139            })
1140        }
1141
1142        let state = self.state();
1143        let get_poll = unsafe {
1144            let _sg = StackGuard::new(state);
1145            check_stack(state, 4)?;
1146
1147            let extra = XRc::clone(&self.extra);
1148            let protect = !self.unlikely_memory_error();
1149            let upvalue = AsyncCallbackUpvalue { data: func, extra };
1150            push_internal_userdata(state, upvalue, protect)?;
1151            if protect {
1152                protect_lua!(state, 1, 1, fn(state) {
1153                    ffi::lua_pushcclosure(state, call_callback, 1);
1154                })?;
1155            } else {
1156                ffi::lua_pushcclosure(state, call_callback, 1);
1157            }
1158
1159            Function(self.pop_ref())
1160        };
1161
1162        unsafe extern "C-unwind" fn unpack(state: *mut ffi::lua_State) -> c_int {
1163            let len = ffi::lua_tointeger(state, 2);
1164            ffi::luaL_checkstack(state, len as c_int, ptr::null());
1165            for i in 1..=len {
1166                ffi::lua_rawgeti(state, 1, i);
1167            }
1168            len as c_int
1169        }
1170
1171        let lua = self.lua();
1172        let coroutine = lua.globals().get::<Table>("coroutine")?;
1173
1174        // Prepare environment for the async poller
1175        let env = lua.create_table_with_capacity(0, 3)?;
1176        env.set("get_poll", get_poll)?;
1177        env.set("yield", coroutine.get::<Function>("yield")?)?;
1178        env.set("unpack", unsafe { lua.create_c_function(unpack)? })?;
1179
1180        lua.load(
1181            r#"
1182            local poll = get_poll(...)
1183            while true do
1184                local nres, res, res2 = poll()
1185                if nres ~= nil then
1186                    if nres == 0 then
1187                        return
1188                    elseif nres == 1 then
1189                        return res
1190                    elseif nres == 2 then
1191                        return res, res2
1192                    else
1193                        return unpack(res, nres)
1194                    end
1195                end
1196                yield(res) -- `res` is a "pending" value
1197            end
1198            "#,
1199        )
1200        .try_cache()
1201        .set_name("__mlua_async_poll")
1202        .set_environment(env)
1203        .into_function()
1204    }
1205
1206    #[cfg(feature = "async")]
1207    #[inline]
1208    pub(crate) unsafe fn waker(&self) -> &Waker {
1209        (*self.extra.get()).waker.as_ref()
1210    }
1211
1212    #[cfg(feature = "async")]
1213    #[inline]
1214    pub(crate) unsafe fn set_waker(&self, waker: NonNull<Waker>) -> NonNull<Waker> {
1215        mem::replace(&mut (*self.extra.get()).waker, waker)
1216    }
1217}
1218
1219// Uses 3 stack spaces
1220unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result<()> {
1221    #[inline(always)]
1222    pub unsafe fn requiref(
1223        state: *mut ffi::lua_State,
1224        modname: &str,
1225        openf: ffi::lua_CFunction,
1226        glb: c_int,
1227    ) -> Result<()> {
1228        let modname = mlua_expect!(CString::new(modname), "modname contains nil byte");
1229        protect_lua!(state, 0, 1, |state| {
1230            ffi::luaL_requiref(state, modname.as_ptr() as *const c_char, openf, glb)
1231        })
1232    }
1233
1234    #[cfg(feature = "luajit")]
1235    struct GcGuard(*mut ffi::lua_State);
1236
1237    #[cfg(feature = "luajit")]
1238    impl GcGuard {
1239        fn new(state: *mut ffi::lua_State) -> Self {
1240            // Stop collector during library initialization
1241            unsafe { ffi::lua_gc(state, ffi::LUA_GCSTOP, 0) };
1242            GcGuard(state)
1243        }
1244    }
1245
1246    #[cfg(feature = "luajit")]
1247    impl Drop for GcGuard {
1248        fn drop(&mut self) {
1249            unsafe { ffi::lua_gc(self.0, ffi::LUA_GCRESTART, -1) };
1250        }
1251    }
1252
1253    // Stop collector during library initialization
1254    #[cfg(feature = "luajit")]
1255    let _gc_guard = GcGuard::new(state);
1256
1257    #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
1258    {
1259        if libs.contains(StdLib::COROUTINE) {
1260            requiref(state, ffi::LUA_COLIBNAME, ffi::luaopen_coroutine, 1)?;
1261            ffi::lua_pop(state, 1);
1262        }
1263    }
1264
1265    if libs.contains(StdLib::TABLE) {
1266        requiref(state, ffi::LUA_TABLIBNAME, ffi::luaopen_table, 1)?;
1267        ffi::lua_pop(state, 1);
1268    }
1269
1270    #[cfg(not(feature = "luau"))]
1271    if libs.contains(StdLib::IO) {
1272        requiref(state, ffi::LUA_IOLIBNAME, ffi::luaopen_io, 1)?;
1273        ffi::lua_pop(state, 1);
1274    }
1275
1276    if libs.contains(StdLib::OS) {
1277        requiref(state, ffi::LUA_OSLIBNAME, ffi::luaopen_os, 1)?;
1278        ffi::lua_pop(state, 1);
1279    }
1280
1281    if libs.contains(StdLib::STRING) {
1282        requiref(state, ffi::LUA_STRLIBNAME, ffi::luaopen_string, 1)?;
1283        ffi::lua_pop(state, 1);
1284    }
1285
1286    #[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
1287    {
1288        if libs.contains(StdLib::UTF8) {
1289            requiref(state, ffi::LUA_UTF8LIBNAME, ffi::luaopen_utf8, 1)?;
1290            ffi::lua_pop(state, 1);
1291        }
1292    }
1293
1294    #[cfg(any(feature = "lua52", feature = "luau"))]
1295    {
1296        if libs.contains(StdLib::BIT) {
1297            requiref(state, ffi::LUA_BITLIBNAME, ffi::luaopen_bit32, 1)?;
1298            ffi::lua_pop(state, 1);
1299        }
1300    }
1301
1302    #[cfg(feature = "luajit")]
1303    {
1304        if libs.contains(StdLib::BIT) {
1305            requiref(state, ffi::LUA_BITLIBNAME, ffi::luaopen_bit, 1)?;
1306            ffi::lua_pop(state, 1);
1307        }
1308    }
1309
1310    #[cfg(feature = "luau")]
1311    if libs.contains(StdLib::BUFFER) {
1312        requiref(state, ffi::LUA_BUFFERLIBNAME, ffi::luaopen_buffer, 1)?;
1313        ffi::lua_pop(state, 1);
1314    }
1315
1316    if libs.contains(StdLib::MATH) {
1317        requiref(state, ffi::LUA_MATHLIBNAME, ffi::luaopen_math, 1)?;
1318        ffi::lua_pop(state, 1);
1319    }
1320
1321    if libs.contains(StdLib::DEBUG) {
1322        requiref(state, ffi::LUA_DBLIBNAME, ffi::luaopen_debug, 1)?;
1323        ffi::lua_pop(state, 1);
1324    }
1325
1326    #[cfg(not(feature = "luau"))]
1327    if libs.contains(StdLib::PACKAGE) {
1328        requiref(state, ffi::LUA_LOADLIBNAME, ffi::luaopen_package, 1)?;
1329        ffi::lua_pop(state, 1);
1330    }
1331    #[cfg(feature = "luau")]
1332    if libs.contains(StdLib::PACKAGE) {
1333        let lua = (*ExtraData::get(state)).lua();
1334        crate::luau::register_package_module(lua)?;
1335    }
1336
1337    #[cfg(feature = "luajit")]
1338    {
1339        if libs.contains(StdLib::JIT) {
1340            requiref(state, ffi::LUA_JITLIBNAME, ffi::luaopen_jit, 1)?;
1341            ffi::lua_pop(state, 1);
1342        }
1343
1344        if libs.contains(StdLib::FFI) {
1345            requiref(state, ffi::LUA_FFILIBNAME, ffi::luaopen_ffi, 1)?;
1346            ffi::lua_pop(state, 1);
1347        }
1348    }
1349
1350    Ok(())
1351}