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
45pub struct RawLua {
47 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 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() {
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 #[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 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 #[cfg(feature = "serialize")]
189 crate::serde::init_metatables(state)?;
190
191 Ok::<_, Error>(())
192 })(main_state),
193 "Error during Lua initialization",
194 );
195
196 let extra = ExtraData::init(main_state, owned);
198
199 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 #[inline(always)]
234 pub(super) unsafe fn set_safe(&self) {
235 (*self.extra.get()).safe = true;
236 }
237
238 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 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 #[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 #[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 #[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 #[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 #[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 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(()); }
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; ffi::lua_sethook(state, Some(hook_proc), triggers.mask(), triggers.count());
379 }
380
381 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 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 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 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 #[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 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 #[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 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; return true;
497 }
498 false
499 }
500
501 #[doc(hidden)]
505 #[inline(always)]
506 pub unsafe fn push(&self, value: impl IntoLua) -> Result<()> {
507 value.push_into_stack(self)
508 }
509
510 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 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 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 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 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 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 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 #[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 #[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 #[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 #[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 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, }
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 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 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 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 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 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 #[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 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 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 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 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 }
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 let index_type = ffi::lua_getfield(state, metatable_index, cstr!("__index"));
889 match index_type {
890 ffi::LUA_TTABLE => {} _ => {
892 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); }
910 ffi::LUA_TNIL => {
911 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 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 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 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 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 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 pub(crate) fn create_callback(&self, func: Callback) -> Result<Function> {
1031 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 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 #[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 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 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 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 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 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
1219unsafe 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 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 #[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}