Skip to main content

lua_rs_runtime/
lib.rs

1//! Embedding helper for lua-rs.
2//!
3//! This crate sits above `lua-vm`, `lua-stdlib`, and `lua-parse`, so it can
4//! provide the common setup sequence without creating dependency cycles:
5//! create a state, install the parser hook, install host hooks, open stdlib,
6//! and run chunks.
7
8use std::any::Any;
9use std::cell::{Cell, Ref, RefCell, RefMut};
10use std::collections::HashMap;
11use std::ffi::c_void;
12use std::fmt;
13use std::hash::Hash;
14use std::ops::{Deref, DerefMut};
15use std::panic::{catch_unwind, AssertUnwindSafe};
16use std::rc::Rc;
17
18use lua_stdlib::auxlib::load_buffer;
19use lua_stdlib::init::open_libs;
20use lua_types::closure::{LuaCClosure as RawLuaCClosure, LuaClosure as RawLuaClosure, LuaLClosure};
21use lua_types::gc::GcRef;
22use lua_types::string::LuaString as RawLuaString;
23use lua_types::upval::UpVal;
24use lua_types::userdata::LuaUserData as RawLuaUserData;
25use lua_types::value::{LuaTable as RawLuaTable, LuaValue as RawLuaValue};
26use lua_vm::state::{
27    new_state, CpuClockHook, DynLibLoadHook, DynLibSymbolHook, DynLibUnloadHook, EntropyHook,
28    EnvHook, ExternalRootKey, FileLoaderHook, FileOpenHook, FileRemoveHook, FileRenameHook,
29    InputHook, LuaCallable, LuaRustFunction, LuaState, OsExecuteHook, OutputHook, PopenHook,
30    TempNameHook, UnixTimeHook,
31};
32
33pub use lua_types::{LuaError, LuaFileHandle};
34pub use lua_vm::state::{DynLibId, DynamicSymbol, OsExecuteReason, OsExecuteResult};
35
36pub type Error = LuaError;
37pub type Result<T> = std::result::Result<T, Error>;
38
39/// Host capabilities exposed to Lua stdlib.
40///
41/// Every field is optional. Missing file, process, and dynamic-loading hooks
42/// produce Lua errors or Lua failure tuples. On bare `wasm32-unknown-unknown`,
43/// missing stdio/time/env/temp hooks avoid unsupported Rust `std` stubs and fail
44/// at the Lua boundary. Native builds may still use compatibility fallbacks for
45/// some stdio and OS functions when hooks are absent.
46#[derive(Clone, Copy, Default)]
47pub struct HostHooks {
48    pub file_loader_hook: Option<FileLoaderHook>,
49    pub file_open_hook: Option<FileOpenHook>,
50    pub stdin_hook: Option<InputHook>,
51    pub stdout_hook: Option<OutputHook>,
52    pub stderr_hook: Option<OutputHook>,
53    pub env_hook: Option<EnvHook>,
54    pub unix_time_hook: Option<UnixTimeHook>,
55    pub cpu_clock_hook: Option<CpuClockHook>,
56    pub entropy_hook: Option<EntropyHook>,
57    pub temp_name_hook: Option<TempNameHook>,
58    pub popen_hook: Option<PopenHook>,
59    pub file_remove_hook: Option<FileRemoveHook>,
60    pub file_rename_hook: Option<FileRenameHook>,
61    pub os_execute_hook: Option<OsExecuteHook>,
62    pub dynlib_load_hook: Option<DynLibLoadHook>,
63    pub dynlib_symbol_hook: Option<DynLibSymbolHook>,
64    pub dynlib_unload_hook: Option<DynLibUnloadHook>,
65}
66
67impl HostHooks {
68    pub fn new() -> Self {
69        Self::default()
70    }
71
72    pub fn file_loader(mut self, hook: FileLoaderHook) -> Self {
73        self.file_loader_hook = Some(hook);
74        self
75    }
76
77    pub fn file_open(mut self, hook: FileOpenHook) -> Self {
78        self.file_open_hook = Some(hook);
79        self
80    }
81
82    pub fn stdin(mut self, hook: InputHook) -> Self {
83        self.stdin_hook = Some(hook);
84        self
85    }
86
87    pub fn stdout(mut self, hook: OutputHook) -> Self {
88        self.stdout_hook = Some(hook);
89        self
90    }
91
92    pub fn stderr(mut self, hook: OutputHook) -> Self {
93        self.stderr_hook = Some(hook);
94        self
95    }
96
97    pub fn env(mut self, hook: EnvHook) -> Self {
98        self.env_hook = Some(hook);
99        self
100    }
101
102    pub fn unix_time(mut self, hook: UnixTimeHook) -> Self {
103        self.unix_time_hook = Some(hook);
104        self
105    }
106
107    pub fn cpu_clock(mut self, hook: CpuClockHook) -> Self {
108        self.cpu_clock_hook = Some(hook);
109        self
110    }
111
112    pub fn entropy(mut self, hook: EntropyHook) -> Self {
113        self.entropy_hook = Some(hook);
114        self
115    }
116
117    pub fn temp_name(mut self, hook: TempNameHook) -> Self {
118        self.temp_name_hook = Some(hook);
119        self
120    }
121
122    pub fn popen(mut self, hook: PopenHook) -> Self {
123        self.popen_hook = Some(hook);
124        self
125    }
126
127    pub fn file_remove(mut self, hook: FileRemoveHook) -> Self {
128        self.file_remove_hook = Some(hook);
129        self
130    }
131
132    pub fn file_rename(mut self, hook: FileRenameHook) -> Self {
133        self.file_rename_hook = Some(hook);
134        self
135    }
136
137    pub fn os_execute(mut self, hook: OsExecuteHook) -> Self {
138        self.os_execute_hook = Some(hook);
139        self
140    }
141
142    pub fn dynlib_load(mut self, hook: DynLibLoadHook) -> Self {
143        self.dynlib_load_hook = Some(hook);
144        self
145    }
146
147    pub fn dynlib_symbol(mut self, hook: DynLibSymbolHook) -> Self {
148        self.dynlib_symbol_hook = Some(hook);
149        self
150    }
151
152    pub fn dynlib_unload(mut self, hook: DynLibUnloadHook) -> Self {
153        self.dynlib_unload_hook = Some(hook);
154        self
155    }
156
157    pub fn install(self, state: &mut LuaState) {
158        let global = &mut *state.global_mut();
159        global.file_loader_hook = self.file_loader_hook;
160        global.file_open_hook = self.file_open_hook;
161        global.stdin_hook = self.stdin_hook;
162        global.stdout_hook = self.stdout_hook;
163        global.stderr_hook = self.stderr_hook;
164        global.env_hook = self.env_hook;
165        global.unix_time_hook = self.unix_time_hook;
166        global.cpu_clock_hook = self.cpu_clock_hook;
167        global.entropy_hook = self.entropy_hook;
168        global.temp_name_hook = self.temp_name_hook;
169        global.popen_hook = self.popen_hook;
170        global.file_remove_hook = self.file_remove_hook;
171        global.file_rename_hook = self.file_rename_hook;
172        global.os_execute_hook = self.os_execute_hook;
173        global.dynlib_load_hook = self.dynlib_load_hook;
174        global.dynlib_symbol_hook = self.dynlib_symbol_hook;
175        global.dynlib_unload_hook = self.dynlib_unload_hook;
176    }
177}
178
179/// Primary owned embedding handle.
180///
181/// `Lua` is intentionally cheap to clone and single-threaded. State access is
182/// borrowed at the embedding boundary only; opcode dispatch still runs with
183/// direct `&mut LuaState` access. Captured Rust callbacks will need a call-path
184/// adapter that releases this boundary borrow before invoking user code.
185#[derive(Clone)]
186pub struct Lua {
187    inner: Rc<LuaInner>,
188}
189
190struct LuaInner {
191    state: RefCell<LuaState>,
192    active_state: Cell<*mut LuaState>,
193    pending_external_unroots: RefCell<Vec<ExternalRootKey>>,
194}
195
196struct UserDataCell<T> {
197    value: RefCell<T>,
198}
199
200struct RustCallbackCell {
201    function: LuaRustFunction,
202}
203
204struct ActiveStateGuard<'a> {
205    inner: &'a LuaInner,
206    previous: *mut LuaState,
207}
208
209impl Drop for ActiveStateGuard<'_> {
210    fn drop(&mut self) {
211        self.inner.active_state.set(self.previous);
212    }
213}
214
215impl LuaInner {
216    fn enter_active(&self, state: *mut LuaState) -> ActiveStateGuard<'_> {
217        let previous = self.active_state.replace(state);
218        ActiveStateGuard {
219            inner: self,
220            previous,
221        }
222    }
223
224    fn flush_pending_external_unroots(&self, state: &mut LuaState) {
225        let pending = self.pending_external_unroots.replace(Vec::new());
226        if pending.is_empty() {
227            return;
228        }
229
230        let mut still_pending = Vec::new();
231        for key in pending {
232            if state.try_external_unroot_value(key).is_err() {
233                still_pending.push(key);
234            }
235        }
236
237        if !still_pending.is_empty() {
238            self.pending_external_unroots
239                .borrow_mut()
240                .extend(still_pending);
241        }
242    }
243}
244
245impl fmt::Debug for Lua {
246    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
247        f.debug_struct("Lua").finish_non_exhaustive()
248    }
249}
250
251impl Lua {
252    /// Create a Lua runtime with parser and standard libraries installed.
253    pub fn new() -> Self {
254        Self::try_new().expect("Lua runtime should initialize")
255    }
256
257    /// Fallible variant of [`Lua::new`].
258    pub fn try_new() -> Result<Self> {
259        Self::with_hooks(HostHooks::default())
260    }
261
262    /// Create a Lua runtime with the supplied host capabilities.
263    pub fn with_hooks(hooks: HostHooks) -> Result<Self> {
264        let mut state = new_state().ok_or(LuaError::Memory)?;
265        install_parser_hook(&mut state);
266        hooks.install(&mut state);
267        open_libs(&mut state)?;
268        Ok(Self::from_initialized_state(state))
269    }
270
271    fn from_initialized_state(state: LuaState) -> Self {
272        Lua {
273            inner: Rc::new(LuaInner {
274                state: RefCell::new(state),
275                active_state: Cell::new(std::ptr::null_mut()),
276                pending_external_unroots: RefCell::new(Vec::new()),
277            }),
278        }
279    }
280
281    fn with_state<R>(&self, f: impl FnOnce(&mut LuaState) -> R) -> R {
282        if let Ok(mut state) = self.inner.state.try_borrow_mut() {
283            let _active = self.inner.enter_active(&mut *state);
284            self.inner.flush_pending_external_unroots(&mut state);
285            let result = f(&mut state);
286            self.inner.flush_pending_external_unroots(&mut state);
287            return result;
288        }
289
290        let state = self
291            .active_state_mut()
292            .expect("re-entrant Lua access without an active state");
293        let result = f(state);
294        self.inner.flush_pending_external_unroots(state);
295        result
296    }
297
298    fn active_state_mut(&self) -> Option<&mut LuaState> {
299        let state = self.inner.active_state.get();
300        if state.is_null() {
301            return None;
302        }
303
304        // SAFETY: `active_state` is set only while this `Lua` owns the outer
305        // `RefCell` borrow and is executing VM code. Re-entrant access can only
306        // happen when that VM frame has synchronously transferred control to a
307        // Rust callback and is suspended. The callback path does not touch the
308        // suspended `&mut LuaState` while user code re-enters through `Lua`.
309        Some(unsafe { &mut *state })
310    }
311
312    fn unroot_external_key(&self, key: ExternalRootKey) {
313        let removed = if let Ok(mut state) = self.inner.state.try_borrow_mut() {
314            let _active = self.inner.enter_active(&mut *state);
315            self.inner.flush_pending_external_unroots(&mut state);
316            let removed = state.try_external_unroot_value(key).is_ok();
317            self.inner.flush_pending_external_unroots(&mut state);
318            removed
319        } else {
320            if let Some(state) = self.active_state_mut() {
321                let removed = state.try_external_unroot_value(key).is_ok();
322                self.inner.flush_pending_external_unroots(state);
323                removed
324            } else {
325                false
326            }
327        };
328
329        if !removed {
330            self.inner.pending_external_unroots.borrow_mut().push(key);
331        }
332    }
333
334    fn root_raw(&self, value: RawLuaValue) -> RootedValue {
335        let key = self.with_state(|state| state.external_root_value(value));
336        RootedValue {
337            lua: self.clone(),
338            key,
339        }
340    }
341
342    fn root_raw_in_state(&self, state: &mut LuaState, value: RawLuaValue) -> RootedValue {
343        let key = state.external_root_value(value);
344        RootedValue {
345            lua: self.clone(),
346            key,
347        }
348    }
349
350    fn userdata_cell<'a, T: 'static>(
351        &self,
352        userdata: &'a AnyUserData,
353    ) -> Result<&'a UserDataCell<T>> {
354        if !Rc::ptr_eq(&self.inner, &userdata.root.lua.inner) {
355            return Err(LuaError::runtime(format_args!(
356                "Lua userdata belongs to a different state"
357            )));
358        }
359        userdata.host_cell()
360    }
361
362    /// Load a Lua source chunk.
363    pub fn load(&self, source: impl AsRef<[u8]>) -> Chunk {
364        Chunk {
365            lua: self.clone(),
366            source: source.as_ref().to_vec(),
367            name: b"chunk".to_vec(),
368        }
369    }
370
371    /// Return the global environment table.
372    pub fn globals(&self) -> Table {
373        let raw = self.with_state(|state| state.global().globals.clone());
374        Table {
375            root: self.root_raw(raw),
376        }
377    }
378
379    /// Create a new empty table.
380    pub fn create_table(&self) -> Result<Table> {
381        let root = self.with_state(|state| {
382            let _heap_guard = heap_guard(state);
383            let table = state.new_table();
384            let raw = RawLuaValue::Table(table);
385            let key = state.external_root_value(raw);
386            state.gc().check_step();
387            RootedValue {
388                lua: self.clone(),
389                key,
390            }
391        });
392        Ok(Table { root })
393    }
394
395    /// Create a new Lua string from bytes.
396    pub fn create_string(&self, bytes: impl AsRef<[u8]>) -> Result<LuaString> {
397        let bytes = bytes.as_ref();
398        let root = self.with_state(|state| {
399            let _heap_guard = heap_guard(state);
400            let string = state.new_string(bytes)?;
401            let raw = RawLuaValue::Str(string);
402            let key = state.external_root_value(raw);
403            state.gc().check_step();
404            Ok::<_, LuaError>(RootedValue {
405                lua: self.clone(),
406                key,
407            })
408        })?;
409        Ok(LuaString { root })
410    }
411
412    pub fn create_function<A, R, F>(&self, func: F) -> Result<Function>
413    where
414        A: FromLuaMulti + 'static,
415        R: IntoLuaMulti + 'static,
416        F: Fn(&Lua, A) -> Result<R> + 'static,
417    {
418        let lua = self.clone();
419        let callable: LuaRustFunction = Rc::new(move |state| {
420            match catch_unwind(AssertUnwindSafe(|| {
421                let args = callback_args(state, &lua)?;
422                let args = A::from_lua_multi(args, &lua)?;
423                let returns = func(&lua, args)?;
424                let returns = returns.into_lua_multi(&lua)?;
425                push_callback_returns(state, &lua, returns)
426            })) {
427                Ok(result) => result,
428                Err(_) => Err(LuaError::runtime(format_args!("Rust callback panicked"))),
429            }
430        });
431        self.create_registered_function(callable)
432    }
433
434    pub fn create_function_mut<A, R, F>(&self, func: F) -> Result<Function>
435    where
436        A: FromLuaMulti + 'static,
437        R: IntoLuaMulti + 'static,
438        F: FnMut(&Lua, A) -> Result<R> + 'static,
439    {
440        let func = RefCell::new(func);
441        self.create_function(move |lua, args| {
442            let mut func = func.try_borrow_mut().map_err(|_| {
443                LuaError::runtime(format_args!("mutable Rust callback is already borrowed"))
444            })?;
445            func(lua, args)
446        })
447    }
448
449    fn create_registered_function(&self, callable: LuaRustFunction) -> Result<Function> {
450        let root = self.with_state(|state| {
451            let trampoline = rust_callback_trampoline as lua_vm::state::LuaCFunction;
452            let idx = {
453                let mut global = state.global_mut();
454                match global.c_functions.iter().position(|existing| {
455                    existing
456                        .as_bare()
457                        .is_some_and(|existing| std::ptr::fn_addr_eq(existing, trampoline))
458                }) {
459                    Some(idx) => idx,
460                    None => {
461                        let idx = global.c_functions.len();
462                        global.c_functions.push(LuaCallable::bare(trampoline));
463                        idx
464                    }
465                }
466            };
467            let raw = with_heap_guard(state, || {
468                let callback_payload = GcRef::new(RawLuaUserData {
469                    data: Box::new([]),
470                    uv: Vec::new(),
471                    metatable: RefCell::new(None),
472                    host_value: RefCell::new(Some(
473                        Rc::new(RustCallbackCell { function: callable }) as Rc<dyn Any>,
474                    )),
475                });
476                RawLuaValue::Function(RawLuaClosure::C(GcRef::new(RawLuaCClosure {
477                    func: idx,
478                    upvalues: vec![RawLuaValue::UserData(callback_payload)],
479                })))
480            });
481            let key = state.external_root_value(raw);
482            state.gc().check_step();
483            RootedValue {
484                lua: self.clone(),
485                key,
486            }
487        });
488        Ok(Function { root })
489    }
490
491    fn create_userdata_method<T, A, R, F>(&self, method: F) -> Result<Function>
492    where
493        T: UserData,
494        A: FromLuaMulti + 'static,
495        R: IntoLuaMulti + 'static,
496        F: Fn(&Lua, &T, A) -> Result<R> + 'static,
497    {
498        let lua = self.clone();
499        let callable: LuaRustFunction = Rc::new(move |state| {
500            match catch_unwind(AssertUnwindSafe(|| {
501                let (userdata, args) = callback_userdata_args(state, &lua)?;
502                let args = A::from_lua_multi(args, &lua)?;
503                let cell = lua.userdata_cell::<T>(&userdata)?;
504                let value = cell.value.try_borrow().map_err(|_| {
505                    LuaError::runtime(format_args!("userdata is already mutably borrowed"))
506                })?;
507                let returns = method(&lua, &value, args)?;
508                let returns = returns.into_lua_multi(&lua)?;
509                push_callback_returns(state, &lua, returns)
510            })) {
511                Ok(result) => result,
512                Err(_) => Err(LuaError::runtime(format_args!(
513                    "Rust userdata method panicked"
514                ))),
515            }
516        });
517        self.create_registered_function(callable)
518    }
519
520    fn create_userdata_method_mut<T, A, R, F>(&self, method: F) -> Result<Function>
521    where
522        T: UserData,
523        A: FromLuaMulti + 'static,
524        R: IntoLuaMulti + 'static,
525        F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
526    {
527        let lua = self.clone();
528        let callable: LuaRustFunction = Rc::new(move |state| {
529            match catch_unwind(AssertUnwindSafe(|| {
530                let (userdata, args) = callback_userdata_args(state, &lua)?;
531                let args = A::from_lua_multi(args, &lua)?;
532                let cell = lua.userdata_cell::<T>(&userdata)?;
533                let mut value = cell
534                    .value
535                    .try_borrow_mut()
536                    .map_err(|_| LuaError::runtime(format_args!("userdata is already borrowed")))?;
537                let returns = method(&lua, &mut value, args)?;
538                let returns = returns.into_lua_multi(&lua)?;
539                push_callback_returns(state, &lua, returns)
540            })) {
541                Ok(result) => result,
542                Err(_) => Err(LuaError::runtime(format_args!(
543                    "Rust userdata method panicked"
544                ))),
545            }
546        });
547        self.create_registered_function(callable)
548    }
549
550    pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData>
551    where
552        T: UserData,
553    {
554        let mut methods = UserDataMethodRegistry::<T>::new(self);
555        T::add_methods(&mut methods);
556        T::add_meta_methods(&mut methods);
557        methods.finish(data)
558    }
559
560    /// Run a full garbage-collection cycle.
561    pub fn gc_collect(&self) {
562        self.with_state(|state| state.gc().full_collect());
563    }
564}
565
566pub struct Chunk {
567    lua: Lua,
568    source: Vec<u8>,
569    name: Vec<u8>,
570}
571
572impl Chunk {
573    pub fn set_name(mut self, name: impl AsRef<[u8]>) -> Self {
574        self.name = name.as_ref().to_vec();
575        self
576    }
577
578    pub fn exec(self) -> Result<()> {
579        self.lua
580            .with_state(|state| exec_state(state, &self.source, &self.name))
581    }
582
583    pub fn eval<T: FromLuaMulti>(self) -> Result<T> {
584        let raws = self.lua.with_state(|state| {
585            let saved_top = state.top_idx();
586            let status = load_buffer(state, &self.source, &self.name)?;
587            if status != 0 {
588                let err = state.pop();
589                state.set_top_idx(saved_top);
590                return Err(LuaError::from_value(err));
591            }
592            match lua_vm::api::pcall_k(state, 0, T::NRESULTS, 0, 0, None) {
593                Ok(_) => {
594                    let nresults = if T::NRESULTS < 0 {
595                        state.top_idx().0.saturating_sub(saved_top.0) as i32
596                    } else {
597                        T::NRESULTS
598                    };
599                    let mut values = Vec::with_capacity(nresults as usize);
600                    for _ in 0..nresults {
601                        values.push(state.pop());
602                    }
603                    values.reverse();
604                    state.set_top_idx(saved_top);
605                    Ok(values)
606                }
607                Err(err) => {
608                    state.set_top_idx(saved_top);
609                    Err(err)
610                }
611            }
612        })?;
613        let values = raws
614            .into_iter()
615            .map(|raw| Value::from_raw(&self.lua, raw))
616            .collect::<Result<Vec<_>>>()?;
617        T::from_lua_multi(values, &self.lua)
618    }
619}
620
621#[derive(Debug)]
622struct RootedValue {
623    lua: Lua,
624    key: ExternalRootKey,
625}
626
627impl RootedValue {
628    fn raw(&self) -> Result<RawLuaValue> {
629        self.lua
630            .with_state(|state| state.external_rooted_value(self.key))
631            .ok_or_else(stale_handle_error)
632    }
633
634    fn raw_for_lua(&self, lua: &Lua, state: &LuaState) -> Result<RawLuaValue> {
635        if !Rc::ptr_eq(&self.lua.inner, &lua.inner) {
636            return Err(LuaError::runtime(format_args!(
637                "Lua handle belongs to a different state"
638            )));
639        }
640        state
641            .external_rooted_value(self.key)
642            .ok_or_else(stale_handle_error)
643    }
644}
645
646impl Clone for RootedValue {
647    fn clone(&self) -> Self {
648        let raw = self.raw().expect("rooted Lua handle should not be stale");
649        self.lua.root_raw(raw)
650    }
651}
652
653impl Drop for RootedValue {
654    fn drop(&mut self) {
655        self.lua.unroot_external_key(self.key);
656    }
657}
658
659/// Dynamically typed owned Lua value.
660#[derive(Debug, Clone)]
661pub enum Value {
662    Nil,
663    Boolean(bool),
664    Integer(i64),
665    Number(f64),
666    String(LuaString),
667    Table(Table),
668    Function(Function),
669    UserData(AnyUserData),
670    LightUserData(*mut c_void),
671    Thread(Thread),
672}
673
674impl Value {
675    fn from_raw(lua: &Lua, raw: RawLuaValue) -> Result<Self> {
676        lua.with_state(|state| Self::from_raw_in_state(lua, state, raw))
677    }
678
679    fn from_raw_in_state(lua: &Lua, state: &mut LuaState, raw: RawLuaValue) -> Result<Self> {
680        Ok(match raw {
681            RawLuaValue::Nil => Value::Nil,
682            RawLuaValue::Bool(v) => Value::Boolean(v),
683            RawLuaValue::Int(v) => Value::Integer(v),
684            RawLuaValue::Float(v) => Value::Number(v),
685            RawLuaValue::Str(v) => Value::String(LuaString {
686                root: lua.root_raw_in_state(state, RawLuaValue::Str(v)),
687            }),
688            RawLuaValue::Table(v) => Value::Table(Table {
689                root: lua.root_raw_in_state(state, RawLuaValue::Table(v)),
690            }),
691            RawLuaValue::Function(v) => Value::Function(Function {
692                root: lua.root_raw_in_state(state, RawLuaValue::Function(v)),
693            }),
694            RawLuaValue::UserData(v) => {
695                let host_value = v.host_value();
696                Value::UserData(AnyUserData {
697                    root: lua.root_raw_in_state(state, RawLuaValue::UserData(v)),
698                    host_value,
699                })
700            }
701            RawLuaValue::LightUserData(v) => Value::LightUserData(v),
702            RawLuaValue::Thread(v) => Value::Thread(Thread {
703                root: lua.root_raw_in_state(state, RawLuaValue::Thread(v)),
704            }),
705        })
706    }
707
708    fn to_raw_for_lua(&self, lua: &Lua, state: &LuaState) -> Result<RawLuaValue> {
709        match self {
710            Value::Nil => Ok(RawLuaValue::Nil),
711            Value::Boolean(v) => Ok(RawLuaValue::Bool(*v)),
712            Value::Integer(v) => Ok(RawLuaValue::Int(*v)),
713            Value::Number(v) => Ok(RawLuaValue::Float(*v)),
714            Value::String(v) => v.root.raw_for_lua(lua, state),
715            Value::Table(v) => v.root.raw_for_lua(lua, state),
716            Value::Function(v) => v.root.raw_for_lua(lua, state),
717            Value::UserData(v) => v.root.raw_for_lua(lua, state),
718            Value::LightUserData(v) => Ok(RawLuaValue::LightUserData(*v)),
719            Value::Thread(v) => v.root.raw_for_lua(lua, state),
720        }
721    }
722}
723
724#[derive(Debug, Clone)]
725pub struct Table {
726    root: RootedValue,
727}
728
729impl Table {
730    fn raw_table(&self) -> Result<GcRef<RawLuaTable>> {
731        match self.root.raw()? {
732            RawLuaValue::Table(table) => Ok(table),
733            other => Err(type_error_raw(&other, "table")),
734        }
735    }
736
737    pub fn get<K, V>(&self, key: K) -> Result<V>
738    where
739        K: IntoLua,
740        V: FromLua,
741    {
742        let lua = self.root.lua.clone();
743        let key = key.into_lua(&lua)?;
744        let value_raw = lua.with_state(|state| {
745            let key_raw = key.to_raw_for_lua(&lua, state)?;
746            let table_raw = self.root.raw_for_lua(&lua, state)?;
747            state.table_get_with_tm(&table_raw, &key_raw)
748        })?;
749        let value = Value::from_raw(&lua, value_raw)?;
750        V::from_lua(value, &lua)
751    }
752
753    pub fn set<K, V>(&self, key: K, value: V) -> Result<()>
754    where
755        K: IntoLua,
756        V: IntoLua,
757    {
758        let lua = self.root.lua.clone();
759        let key = key.into_lua(&lua)?;
760        let value = value.into_lua(&lua)?;
761        lua.with_state(|state| {
762            let key_raw = key.to_raw_for_lua(&lua, state)?;
763            let value_raw = value.to_raw_for_lua(&lua, state)?;
764            let table_raw = self.root.raw_for_lua(&lua, state)?;
765            state.table_set_with_tm(&table_raw, key_raw, value_raw)
766        })
767    }
768
769    pub fn len(&self) -> Result<u64> {
770        Ok(self.raw_table()?.getn())
771    }
772}
773
774#[derive(Debug, Clone)]
775pub struct Function {
776    root: RootedValue,
777}
778
779impl Function {
780    pub fn call<A, R>(&self, args: A) -> Result<R>
781    where
782        A: IntoLuaMulti,
783        R: FromLuaMulti,
784    {
785        let lua = self.root.lua.clone();
786        let args = args.into_lua_multi(&lua)?;
787        let result_raws = lua.with_state(|state| {
788            let arg_raws = args
789                .iter()
790                .map(|value| value.to_raw_for_lua(&lua, state))
791                .collect::<Result<Vec<_>>>()?;
792            let function_raw = self.root.raw_for_lua(&lua, state)?;
793            let saved_top = state.top_idx();
794            state.push(function_raw);
795            for arg in &arg_raws {
796                state.push(*arg);
797            }
798            match lua_vm::api::pcall_k(state, arg_raws.len() as i32, R::NRESULTS, 0, 0, None) {
799                Ok(_) => {
800                    let nresults = if R::NRESULTS < 0 {
801                        state.top_idx().0.saturating_sub(saved_top.0) as i32
802                    } else {
803                        R::NRESULTS
804                    };
805                    let mut results = Vec::with_capacity(nresults as usize);
806                    for _ in 0..nresults {
807                        results.push(state.pop());
808                    }
809                    results.reverse();
810                    state.set_top_idx(saved_top);
811                    Ok(results)
812                }
813                Err(err) => {
814                    state.set_top_idx(saved_top);
815                    Err(err)
816                }
817            }
818        })?;
819        let values = result_raws
820            .into_iter()
821            .map(|raw| Value::from_raw(&lua, raw))
822            .collect::<Result<Vec<_>>>()?;
823        R::from_lua_multi(values, &lua)
824    }
825}
826
827#[derive(Debug, Clone)]
828pub struct LuaString {
829    root: RootedValue,
830}
831
832impl LuaString {
833    fn raw_string(&self) -> Result<GcRef<RawLuaString>> {
834        match self.root.raw()? {
835            RawLuaValue::Str(string) => Ok(string),
836            other => Err(type_error_raw(&other, "string")),
837        }
838    }
839
840    pub fn as_bytes(&self) -> Result<Vec<u8>> {
841        Ok(self.raw_string()?.as_bytes().to_vec())
842    }
843
844    pub fn to_str(&self) -> Result<String> {
845        let bytes = self.as_bytes()?;
846        String::from_utf8(bytes)
847            .map_err(|_| LuaError::runtime(format_args!("string is not valid UTF-8")))
848    }
849}
850
851#[derive(Clone)]
852pub struct AnyUserData {
853    root: RootedValue,
854    host_value: Option<Rc<dyn Any>>,
855}
856
857impl fmt::Debug for AnyUserData {
858    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
859        f.debug_struct("AnyUserData")
860            .field("root", &self.root)
861            .field("has_host_value", &self.host_value.is_some())
862            .finish()
863    }
864}
865
866impl AnyUserData {
867    fn host_cell<T: 'static>(&self) -> Result<&UserDataCell<T>> {
868        let host = self
869            .host_value
870            .as_deref()
871            .ok_or_else(|| LuaError::runtime(format_args!("missing Rust userdata payload")))?;
872        host.downcast_ref::<UserDataCell<T>>()
873            .ok_or_else(|| LuaError::runtime(format_args!("userdata type mismatch")))
874    }
875
876    pub fn borrow<T>(&self) -> Result<Ref<'_, T>>
877    where
878        T: 'static,
879    {
880        self.host_cell::<T>()?
881            .value
882            .try_borrow()
883            .map_err(|_| LuaError::runtime(format_args!("userdata is already mutably borrowed")))
884    }
885
886    pub fn borrow_mut<T>(&self) -> Result<RefMut<'_, T>>
887    where
888        T: 'static,
889    {
890        self.host_cell::<T>()?
891            .value
892            .try_borrow_mut()
893            .map_err(|_| LuaError::runtime(format_args!("userdata is already borrowed")))
894    }
895
896    pub fn with_borrow<T, R>(&self, f: impl FnOnce(&T) -> R) -> Result<R>
897    where
898        T: 'static,
899    {
900        let value = self.borrow::<T>()?;
901        Ok(f(&value))
902    }
903
904    pub fn with_borrow_mut<T, R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R>
905    where
906        T: 'static,
907    {
908        let mut value = self.borrow_mut::<T>()?;
909        Ok(f(&mut value))
910    }
911}
912
913#[derive(Debug, Clone)]
914pub struct Thread {
915    root: RootedValue,
916}
917
918/// Variable argument or return list converted element-by-element.
919///
920/// This mirrors mlua's `Variadic<T>` enough for dynamic callback bridges:
921/// `create_function(|_, args: Variadic<Value>| ...)` receives all Lua
922/// arguments, and returning `Variadic<T>` pushes all contained values.
923#[derive(Debug, Clone, Default, PartialEq, Eq)]
924pub struct Variadic<T>(Vec<T>);
925
926impl<T> Variadic<T> {
927    pub const fn new() -> Self {
928        Self(Vec::new())
929    }
930
931    pub fn with_capacity(capacity: usize) -> Self {
932        Self(Vec::with_capacity(capacity))
933    }
934
935    pub fn into_vec(self) -> Vec<T> {
936        self.0
937    }
938}
939
940impl<T> Deref for Variadic<T> {
941    type Target = Vec<T>;
942
943    fn deref(&self) -> &Self::Target {
944        &self.0
945    }
946}
947
948impl<T> DerefMut for Variadic<T> {
949    fn deref_mut(&mut self) -> &mut Self::Target {
950        &mut self.0
951    }
952}
953
954impl<T> From<Vec<T>> for Variadic<T> {
955    fn from(value: Vec<T>) -> Self {
956        Self(value)
957    }
958}
959
960impl<T> From<Variadic<T>> for Vec<T> {
961    fn from(value: Variadic<T>) -> Self {
962        value.0
963    }
964}
965
966impl<T> FromIterator<T> for Variadic<T> {
967    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
968        Self(Vec::from_iter(iter))
969    }
970}
971
972impl<T> IntoIterator for Variadic<T> {
973    type Item = T;
974    type IntoIter = std::vec::IntoIter<T>;
975
976    fn into_iter(self) -> Self::IntoIter {
977        self.0.into_iter()
978    }
979}
980
981pub trait UserData: 'static {
982    fn add_methods<M: UserDataMethods<Self>>(_methods: &mut M)
983    where
984        Self: Sized,
985    {
986    }
987
988    fn add_meta_methods<M: UserDataMethods<Self>>(_methods: &mut M)
989    where
990        Self: Sized,
991    {
992    }
993}
994
995pub trait UserDataMethods<T: UserData> {
996    fn add_method<A, R, F>(&mut self, name: &str, method: F)
997    where
998        A: FromLuaMulti + 'static,
999        R: IntoLuaMulti + 'static,
1000        F: Fn(&Lua, &T, A) -> Result<R> + 'static;
1001
1002    fn add_method_mut<A, R, F>(&mut self, name: &str, method: F)
1003    where
1004        A: FromLuaMulti + 'static,
1005        R: IntoLuaMulti + 'static,
1006        F: Fn(&Lua, &mut T, A) -> Result<R> + 'static;
1007
1008    fn add_meta_method<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
1009    where
1010        A: FromLuaMulti + 'static,
1011        R: IntoLuaMulti + 'static,
1012        F: Fn(&Lua, &T, A) -> Result<R> + 'static;
1013
1014    fn add_meta_method_mut<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
1015    where
1016        A: FromLuaMulti + 'static,
1017        R: IntoLuaMulti + 'static,
1018        F: Fn(&Lua, &mut T, A) -> Result<R> + 'static;
1019}
1020
1021#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1022pub enum MetaMethod {
1023    Index,
1024    NewIndex,
1025    Add,
1026    Sub,
1027    Mul,
1028    Div,
1029    Mod,
1030    Pow,
1031    Unm,
1032    Len,
1033    Eq,
1034    Lt,
1035    Le,
1036    Concat,
1037    Call,
1038    ToString,
1039    Pairs,
1040}
1041
1042impl MetaMethod {
1043    fn name(self) -> &'static str {
1044        match self {
1045            MetaMethod::Index => "__index",
1046            MetaMethod::NewIndex => "__newindex",
1047            MetaMethod::Add => "__add",
1048            MetaMethod::Sub => "__sub",
1049            MetaMethod::Mul => "__mul",
1050            MetaMethod::Div => "__div",
1051            MetaMethod::Mod => "__mod",
1052            MetaMethod::Pow => "__pow",
1053            MetaMethod::Unm => "__unm",
1054            MetaMethod::Len => "__len",
1055            MetaMethod::Eq => "__eq",
1056            MetaMethod::Lt => "__lt",
1057            MetaMethod::Le => "__le",
1058            MetaMethod::Concat => "__concat",
1059            MetaMethod::Call => "__call",
1060            MetaMethod::ToString => "__tostring",
1061            MetaMethod::Pairs => "__pairs",
1062        }
1063    }
1064}
1065
1066struct UserDataMethodRegistry<'lua, T: UserData> {
1067    lua: &'lua Lua,
1068    methods: Vec<(String, Function)>,
1069    meta_methods: Vec<(MetaMethod, Function)>,
1070    error: Option<LuaError>,
1071    _marker: std::marker::PhantomData<T>,
1072}
1073
1074impl<'lua, T: UserData> UserDataMethodRegistry<'lua, T> {
1075    fn new(lua: &'lua Lua) -> Self {
1076        Self {
1077            lua,
1078            methods: Vec::new(),
1079            meta_methods: Vec::new(),
1080            error: None,
1081            _marker: std::marker::PhantomData,
1082        }
1083    }
1084
1085    fn record(&mut self, result: Result<Function>, insert: impl FnOnce(&mut Self, Function)) {
1086        if self.error.is_some() {
1087            return;
1088        }
1089        match result {
1090            Ok(function) => insert(self, function),
1091            Err(err) => self.error = Some(err),
1092        }
1093    }
1094
1095    fn finish(mut self, data: T) -> Result<AnyUserData> {
1096        if let Some(err) = self.error.take() {
1097            return Err(err);
1098        }
1099
1100        let method_table = self.lua.create_table()?;
1101        for (name, function) in &self.methods {
1102            method_table.set(name.as_str(), function)?;
1103        }
1104
1105        let metatable = self.lua.create_table()?;
1106        let mut has_index = false;
1107        for (metamethod, function) in &self.meta_methods {
1108            if *metamethod == MetaMethod::Index {
1109                has_index = true;
1110            }
1111            metatable.set(metamethod.name(), function)?;
1112        }
1113        if !has_index {
1114            metatable.set(MetaMethod::Index.name(), &method_table)?;
1115        }
1116
1117        let cell: Rc<dyn Any> = Rc::new(UserDataCell {
1118            value: RefCell::new(data),
1119        });
1120        let host_value = cell.clone();
1121        let root = self.lua.with_state(|state| {
1122            let userdata = with_heap_guard(state, || {
1123                GcRef::new(RawLuaUserData {
1124                    data: Box::new([]),
1125                    uv: Vec::new(),
1126                    metatable: RefCell::new(None),
1127                    host_value: RefCell::new(None),
1128                })
1129            });
1130            let metatable_raw = metatable.root.raw_for_lua(self.lua, state)?;
1131            let RawLuaValue::Table(metatable) = metatable_raw else {
1132                return Err(type_error_raw(&metatable_raw, "table"));
1133            };
1134            userdata.set_metatable(Some(metatable));
1135            userdata.set_host_value(Some(cell));
1136            let key = state.external_root_value(RawLuaValue::UserData(userdata));
1137            Ok::<_, LuaError>(RootedValue {
1138                lua: self.lua.clone(),
1139                key,
1140            })
1141        })?;
1142        Ok(AnyUserData {
1143            root,
1144            host_value: Some(host_value),
1145        })
1146    }
1147}
1148
1149impl<T: UserData> UserDataMethods<T> for UserDataMethodRegistry<'_, T> {
1150    fn add_method<A, R, F>(&mut self, name: &str, method: F)
1151    where
1152        A: FromLuaMulti + 'static,
1153        R: IntoLuaMulti + 'static,
1154        F: Fn(&Lua, &T, A) -> Result<R> + 'static,
1155    {
1156        let name = name.to_string();
1157        let result = self.lua.create_userdata_method(method);
1158        self.record(result, move |this, function| {
1159            this.methods.push((name, function));
1160        });
1161    }
1162
1163    fn add_method_mut<A, R, F>(&mut self, name: &str, method: F)
1164    where
1165        A: FromLuaMulti + 'static,
1166        R: IntoLuaMulti + 'static,
1167        F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
1168    {
1169        let name = name.to_string();
1170        let result = self.lua.create_userdata_method_mut(method);
1171        self.record(result, move |this, function| {
1172            this.methods.push((name, function));
1173        });
1174    }
1175
1176    fn add_meta_method<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
1177    where
1178        A: FromLuaMulti + 'static,
1179        R: IntoLuaMulti + 'static,
1180        F: Fn(&Lua, &T, A) -> Result<R> + 'static,
1181    {
1182        let result = self.lua.create_userdata_method(method);
1183        self.record(result, move |this, function| {
1184            this.meta_methods.push((metamethod, function));
1185        });
1186    }
1187
1188    fn add_meta_method_mut<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
1189    where
1190        A: FromLuaMulti + 'static,
1191        R: IntoLuaMulti + 'static,
1192        F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
1193    {
1194        let result = self.lua.create_userdata_method_mut(method);
1195        self.record(result, move |this, function| {
1196            this.meta_methods.push((metamethod, function));
1197        });
1198    }
1199}
1200
1201pub trait IntoLua {
1202    fn into_lua(self, lua: &Lua) -> Result<Value>;
1203}
1204
1205pub trait FromLua: Sized {
1206    fn from_lua(value: Value, lua: &Lua) -> Result<Self>;
1207}
1208
1209pub trait IntoLuaMulti {
1210    fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>>;
1211}
1212
1213pub trait FromLuaMulti: Sized {
1214    const NRESULTS: i32;
1215
1216    fn from_lua_multi(values: Vec<Value>, lua: &Lua) -> Result<Self>;
1217}
1218
1219impl IntoLua for Value {
1220    fn into_lua(self, _lua: &Lua) -> Result<Value> {
1221        Ok(self)
1222    }
1223}
1224
1225impl IntoLua for &Value {
1226    fn into_lua(self, _lua: &Lua) -> Result<Value> {
1227        Ok(self.clone())
1228    }
1229}
1230
1231impl FromLua for Value {
1232    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1233        Ok(value)
1234    }
1235}
1236
1237impl IntoLua for bool {
1238    fn into_lua(self, _lua: &Lua) -> Result<Value> {
1239        Ok(Value::Boolean(self))
1240    }
1241}
1242
1243impl FromLua for bool {
1244    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1245        match value {
1246            Value::Boolean(v) => Ok(v),
1247            other => Err(type_error_value(&other, "boolean")),
1248        }
1249    }
1250}
1251
1252impl IntoLua for i64 {
1253    fn into_lua(self, _lua: &Lua) -> Result<Value> {
1254        Ok(Value::Integer(self))
1255    }
1256}
1257
1258impl FromLua for i64 {
1259    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1260        match value {
1261            Value::Integer(v) => Ok(v),
1262            Value::Number(v) if v.fract() == 0.0 && v.is_finite() => Ok(v as i64),
1263            other => Err(type_error_value(&other, "integer")),
1264        }
1265    }
1266}
1267
1268impl IntoLua for i32 {
1269    fn into_lua(self, lua: &Lua) -> Result<Value> {
1270        i64::from(self).into_lua(lua)
1271    }
1272}
1273
1274impl FromLua for i32 {
1275    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1276        let v = i64::from_lua(value, lua)?;
1277        i32::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
1278    }
1279}
1280
1281impl IntoLua for usize {
1282    fn into_lua(self, lua: &Lua) -> Result<Value> {
1283        let v = i64::try_from(self)
1284            .map_err(|_| LuaError::runtime(format_args!("integer out of range")))?;
1285        v.into_lua(lua)
1286    }
1287}
1288
1289impl FromLua for usize {
1290    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1291        let v = i64::from_lua(value, lua)?;
1292        usize::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
1293    }
1294}
1295
1296impl IntoLua for u64 {
1297    fn into_lua(self, lua: &Lua) -> Result<Value> {
1298        let v = i64::try_from(self)
1299            .map_err(|_| LuaError::runtime(format_args!("integer out of range")))?;
1300        v.into_lua(lua)
1301    }
1302}
1303
1304impl FromLua for u64 {
1305    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1306        let v = i64::from_lua(value, lua)?;
1307        u64::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
1308    }
1309}
1310
1311impl IntoLua for u32 {
1312    fn into_lua(self, lua: &Lua) -> Result<Value> {
1313        u64::from(self).into_lua(lua)
1314    }
1315}
1316
1317impl FromLua for u32 {
1318    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1319        let v = u64::from_lua(value, lua)?;
1320        u32::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
1321    }
1322}
1323
1324impl IntoLua for f64 {
1325    fn into_lua(self, _lua: &Lua) -> Result<Value> {
1326        Ok(Value::Number(self))
1327    }
1328}
1329
1330impl FromLua for f64 {
1331    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1332        match value {
1333            Value::Integer(v) => Ok(v as f64),
1334            Value::Number(v) => Ok(v),
1335            other => Err(type_error_value(&other, "number")),
1336        }
1337    }
1338}
1339
1340impl IntoLua for &str {
1341    fn into_lua(self, lua: &Lua) -> Result<Value> {
1342        Ok(Value::String(lua.create_string(self.as_bytes())?))
1343    }
1344}
1345
1346impl IntoLua for String {
1347    fn into_lua(self, lua: &Lua) -> Result<Value> {
1348        Ok(Value::String(lua.create_string(self.into_bytes())?))
1349    }
1350}
1351
1352impl FromLua for String {
1353    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1354        match value {
1355            Value::String(s) => s.to_str(),
1356            other => Err(type_error_value(&other, "string")),
1357        }
1358    }
1359}
1360
1361impl IntoLua for &[u8] {
1362    fn into_lua(self, lua: &Lua) -> Result<Value> {
1363        Ok(Value::String(lua.create_string(self)?))
1364    }
1365}
1366
1367impl IntoLua for LuaString {
1368    fn into_lua(self, _lua: &Lua) -> Result<Value> {
1369        Ok(Value::String(self))
1370    }
1371}
1372
1373impl IntoLua for &LuaString {
1374    fn into_lua(self, _lua: &Lua) -> Result<Value> {
1375        Ok(Value::String(self.clone()))
1376    }
1377}
1378
1379impl FromLua for LuaString {
1380    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1381        match value {
1382            Value::String(v) => Ok(v),
1383            other => Err(type_error_value(&other, "string")),
1384        }
1385    }
1386}
1387
1388impl IntoLua for Table {
1389    fn into_lua(self, _lua: &Lua) -> Result<Value> {
1390        Ok(Value::Table(self))
1391    }
1392}
1393
1394impl IntoLua for &Table {
1395    fn into_lua(self, _lua: &Lua) -> Result<Value> {
1396        Ok(Value::Table(self.clone()))
1397    }
1398}
1399
1400impl FromLua for Table {
1401    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1402        match value {
1403            Value::Table(v) => Ok(v),
1404            other => Err(type_error_value(&other, "table")),
1405        }
1406    }
1407}
1408
1409impl IntoLua for Function {
1410    fn into_lua(self, _lua: &Lua) -> Result<Value> {
1411        Ok(Value::Function(self))
1412    }
1413}
1414
1415impl IntoLua for &Function {
1416    fn into_lua(self, _lua: &Lua) -> Result<Value> {
1417        Ok(Value::Function(self.clone()))
1418    }
1419}
1420
1421impl FromLua for Function {
1422    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1423        match value {
1424            Value::Function(v) => Ok(v),
1425            other => Err(type_error_value(&other, "function")),
1426        }
1427    }
1428}
1429
1430impl IntoLua for AnyUserData {
1431    fn into_lua(self, _lua: &Lua) -> Result<Value> {
1432        Ok(Value::UserData(self))
1433    }
1434}
1435
1436impl IntoLua for &AnyUserData {
1437    fn into_lua(self, _lua: &Lua) -> Result<Value> {
1438        Ok(Value::UserData(self.clone()))
1439    }
1440}
1441
1442impl FromLua for AnyUserData {
1443    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1444        match value {
1445            Value::UserData(v) => Ok(v),
1446            other => Err(type_error_value(&other, "userdata")),
1447        }
1448    }
1449}
1450
1451impl<T> IntoLua for T
1452where
1453    T: UserData,
1454{
1455    fn into_lua(self, lua: &Lua) -> Result<Value> {
1456        Ok(Value::UserData(lua.create_userdata(self)?))
1457    }
1458}
1459
1460impl<T> IntoLua for Option<T>
1461where
1462    T: IntoLua,
1463{
1464    fn into_lua(self, lua: &Lua) -> Result<Value> {
1465        match self {
1466            Some(value) => value.into_lua(lua),
1467            None => Ok(Value::Nil),
1468        }
1469    }
1470}
1471
1472impl<T> FromLua for Option<T>
1473where
1474    T: FromLua,
1475{
1476    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1477        match value {
1478            Value::Nil => Ok(None),
1479            other => T::from_lua(other, lua).map(Some),
1480        }
1481    }
1482}
1483
1484impl<T> IntoLua for Vec<T>
1485where
1486    T: IntoLua,
1487{
1488    fn into_lua(self, lua: &Lua) -> Result<Value> {
1489        let table = lua.create_table()?;
1490        for (idx, value) in self.into_iter().enumerate() {
1491            table.set((idx + 1) as i64, value)?;
1492        }
1493        Ok(Value::Table(table))
1494    }
1495}
1496
1497impl<T> FromLua for Vec<T>
1498where
1499    T: FromLua,
1500{
1501    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1502        let table = Table::from_lua(value, lua)?;
1503        let raw = table.raw_table()?;
1504        let len = raw.getn();
1505        let mut out = Vec::with_capacity(len as usize);
1506        for idx in 1..=len {
1507            let value = Value::from_raw(lua, raw.get_int(idx as i64))?;
1508            out.push(T::from_lua(value, lua)?);
1509        }
1510        Ok(out)
1511    }
1512}
1513
1514impl<K, V> IntoLua for HashMap<K, V>
1515where
1516    K: IntoLua,
1517    V: IntoLua,
1518{
1519    fn into_lua(self, lua: &Lua) -> Result<Value> {
1520        let table = lua.create_table()?;
1521        for (key, value) in self {
1522            table.set(key, value)?;
1523        }
1524        Ok(Value::Table(table))
1525    }
1526}
1527
1528impl<K, V> FromLua for HashMap<K, V>
1529where
1530    K: FromLua + Eq + Hash,
1531    V: FromLua,
1532{
1533    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1534        let table = Table::from_lua(value, lua)?;
1535        let raw = table.raw_table()?;
1536        let mut out = HashMap::new();
1537        let mut result = Ok(());
1538        raw.for_each_entry(|key, value| {
1539            if result.is_err() {
1540                return;
1541            }
1542            result = (|| {
1543                let key = Value::from_raw(lua, *key)?;
1544                let value = Value::from_raw(lua, *value)?;
1545                out.insert(K::from_lua(key, lua)?, V::from_lua(value, lua)?);
1546                Ok(())
1547            })();
1548        });
1549        result?;
1550        Ok(out)
1551    }
1552}
1553
1554impl<T> IntoLuaMulti for Variadic<T>
1555where
1556    T: IntoLua,
1557{
1558    fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1559        self.into_iter().map(|value| value.into_lua(lua)).collect()
1560    }
1561}
1562
1563impl<T> FromLuaMulti for Variadic<T>
1564where
1565    T: FromLua,
1566{
1567    const NRESULTS: i32 = -1;
1568
1569    fn from_lua_multi(values: Vec<Value>, lua: &Lua) -> Result<Self> {
1570        values
1571            .into_iter()
1572            .map(|value| T::from_lua(value, lua))
1573            .collect()
1574    }
1575}
1576
1577impl IntoLuaMulti for () {
1578    fn into_lua_multi(self, _lua: &Lua) -> Result<Vec<Value>> {
1579        Ok(Vec::new())
1580    }
1581}
1582
1583impl<T> IntoLuaMulti for T
1584where
1585    T: IntoLua,
1586{
1587    fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1588        Ok(vec![self.into_lua(lua)?])
1589    }
1590}
1591
1592impl<A, B> IntoLuaMulti for (A, B)
1593where
1594    A: IntoLua,
1595    B: IntoLua,
1596{
1597    fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1598        Ok(vec![self.0.into_lua(lua)?, self.1.into_lua(lua)?])
1599    }
1600}
1601
1602impl<A, T> IntoLuaMulti for (A, Variadic<T>)
1603where
1604    A: IntoLua,
1605    T: IntoLua,
1606{
1607    fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1608        let mut values = vec![self.0.into_lua(lua)?];
1609        values.extend(self.1.into_lua_multi(lua)?);
1610        Ok(values)
1611    }
1612}
1613
1614impl<A, B, C> IntoLuaMulti for (A, B, C)
1615where
1616    A: IntoLua,
1617    B: IntoLua,
1618    C: IntoLua,
1619{
1620    fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1621        Ok(vec![
1622            self.0.into_lua(lua)?,
1623            self.1.into_lua(lua)?,
1624            self.2.into_lua(lua)?,
1625        ])
1626    }
1627}
1628
1629impl<A, B, T> IntoLuaMulti for (A, B, Variadic<T>)
1630where
1631    A: IntoLua,
1632    B: IntoLua,
1633    T: IntoLua,
1634{
1635    fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1636        let mut values = vec![self.0.into_lua(lua)?, self.1.into_lua(lua)?];
1637        values.extend(self.2.into_lua_multi(lua)?);
1638        Ok(values)
1639    }
1640}
1641
1642impl FromLuaMulti for () {
1643    const NRESULTS: i32 = 0;
1644
1645    fn from_lua_multi(_values: Vec<Value>, _lua: &Lua) -> Result<Self> {
1646        Ok(())
1647    }
1648}
1649
1650impl<T> FromLuaMulti for T
1651where
1652    T: FromLua,
1653{
1654    const NRESULTS: i32 = 1;
1655
1656    fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
1657        let value = if values.is_empty() {
1658            Value::Nil
1659        } else {
1660            values.remove(0)
1661        };
1662        T::from_lua(value, lua)
1663    }
1664}
1665
1666impl<A, B> FromLuaMulti for (A, B)
1667where
1668    A: FromLua,
1669    B: FromLua,
1670{
1671    const NRESULTS: i32 = 2;
1672
1673    fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
1674        let first = if values.is_empty() {
1675            Value::Nil
1676        } else {
1677            values.remove(0)
1678        };
1679        let second = if values.is_empty() {
1680            Value::Nil
1681        } else {
1682            values.remove(0)
1683        };
1684        Ok((A::from_lua(first, lua)?, B::from_lua(second, lua)?))
1685    }
1686}
1687
1688impl<A, T> FromLuaMulti for (A, Variadic<T>)
1689where
1690    A: FromLua,
1691    T: FromLua,
1692{
1693    const NRESULTS: i32 = -1;
1694
1695    fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
1696        let first = if values.is_empty() {
1697            Value::Nil
1698        } else {
1699            values.remove(0)
1700        };
1701        Ok((
1702            A::from_lua(first, lua)?,
1703            Variadic::from_lua_multi(values, lua)?,
1704        ))
1705    }
1706}
1707
1708impl<A, B, C> FromLuaMulti for (A, B, C)
1709where
1710    A: FromLua,
1711    B: FromLua,
1712    C: FromLua,
1713{
1714    const NRESULTS: i32 = 3;
1715
1716    fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
1717        let first = if values.is_empty() {
1718            Value::Nil
1719        } else {
1720            values.remove(0)
1721        };
1722        let second = if values.is_empty() {
1723            Value::Nil
1724        } else {
1725            values.remove(0)
1726        };
1727        let third = if values.is_empty() {
1728            Value::Nil
1729        } else {
1730            values.remove(0)
1731        };
1732        Ok((
1733            A::from_lua(first, lua)?,
1734            B::from_lua(second, lua)?,
1735            C::from_lua(third, lua)?,
1736        ))
1737    }
1738}
1739
1740impl<A, B, T> FromLuaMulti for (A, B, Variadic<T>)
1741where
1742    A: FromLua,
1743    B: FromLua,
1744    T: FromLua,
1745{
1746    const NRESULTS: i32 = -1;
1747
1748    fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
1749        let first = if values.is_empty() {
1750            Value::Nil
1751        } else {
1752            values.remove(0)
1753        };
1754        let second = if values.is_empty() {
1755            Value::Nil
1756        } else {
1757            values.remove(0)
1758        };
1759        Ok((
1760            A::from_lua(first, lua)?,
1761            B::from_lua(second, lua)?,
1762            Variadic::from_lua_multi(values, lua)?,
1763        ))
1764    }
1765}
1766
1767fn rust_callback_trampoline(state: &mut LuaState) -> Result<usize> {
1768    let func_idx = state.current_call_info().func;
1769    let callback = match state.get_at(func_idx) {
1770        RawLuaValue::Function(RawLuaClosure::C(closure)) => {
1771            let Some(RawLuaValue::UserData(userdata)) = closure.upvalues.first() else {
1772                return Err(LuaError::runtime(format_args!(
1773                    "missing Rust callback payload"
1774                )));
1775            };
1776            let host = userdata
1777                .host_value()
1778                .ok_or_else(|| LuaError::runtime(format_args!("missing Rust callback payload")))?;
1779            host.downcast::<RustCallbackCell>().map_err(|_| {
1780                LuaError::runtime(format_args!("Rust callback payload type mismatch"))
1781            })?
1782        }
1783        _ => {
1784            return Err(LuaError::runtime(format_args!(
1785                "Rust callback trampoline called without C closure"
1786            )));
1787        }
1788    };
1789    (callback.function)(state)
1790}
1791
1792fn with_heap_guard<R>(state: &LuaState, f: impl FnOnce() -> R) -> R {
1793    let _heap_guard = heap_guard(state);
1794    f()
1795}
1796
1797fn heap_guard(state: &LuaState) -> lua_gc::HeapGuard {
1798    let global = state.global();
1799    lua_gc::HeapGuard::push(&global.heap)
1800}
1801
1802fn callback_args(state: &mut LuaState, lua: &Lua) -> Result<Vec<Value>> {
1803    let func_idx = state.current_call_info().func;
1804    let nargs = state.top_idx().0.saturating_sub(func_idx.0 + 1);
1805    let mut args = Vec::with_capacity(nargs as usize);
1806    for i in 0..nargs {
1807        let raw = state.get_at(func_idx + 1 + i as i32);
1808        args.push(Value::from_raw_in_state(lua, state, raw)?);
1809    }
1810    Ok(args)
1811}
1812
1813fn callback_userdata_args(state: &mut LuaState, lua: &Lua) -> Result<(AnyUserData, Vec<Value>)> {
1814    let mut args = callback_args(state, lua)?;
1815    if args.is_empty() {
1816        return Err(LuaError::runtime(format_args!(
1817            "userdata method missing self argument"
1818        )));
1819    }
1820    let userdata = AnyUserData::from_lua(args.remove(0), lua)?;
1821    Ok((userdata, args))
1822}
1823
1824fn push_callback_returns(state: &mut LuaState, lua: &Lua, returns: Vec<Value>) -> Result<usize> {
1825    let mut count = 0usize;
1826    for value in returns {
1827        let raw = value.to_raw_for_lua(lua, state)?;
1828        state.push(raw);
1829        count += 1;
1830    }
1831    Ok(count)
1832}
1833
1834fn stale_handle_error() -> LuaError {
1835    LuaError::runtime(format_args!("stale Lua handle"))
1836}
1837
1838fn type_error_raw(value: &RawLuaValue, expected: &str) -> LuaError {
1839    LuaError::runtime(format_args!(
1840        "{} expected, got {}",
1841        expected,
1842        value.type_name()
1843    ))
1844}
1845
1846fn type_error_value(value: &Value, expected: &str) -> LuaError {
1847    let got = match value {
1848        Value::Nil => "nil",
1849        Value::Boolean(_) => "boolean",
1850        Value::Integer(_) | Value::Number(_) => "number",
1851        Value::String(_) => "string",
1852        Value::Table(_) => "table",
1853        Value::Function(_) => "function",
1854        Value::UserData(_) | Value::LightUserData(_) => "userdata",
1855        Value::Thread(_) => "thread",
1856    };
1857    LuaError::runtime(format_args!("{} expected, got {}", expected, got))
1858}
1859
1860/// A Lua state with parser and standard libraries installed.
1861pub struct LuaRuntime {
1862    state: LuaState,
1863}
1864
1865impl LuaRuntime {
1866    /// Create a Lua runtime with parser and standard libraries installed.
1867    ///
1868    /// This installs no explicit host hooks. For a strict sandbox, construct
1869    /// with [`LuaRuntime::with_hooks`] and audit the native compatibility
1870    /// fallbacks in `lua-stdlib`.
1871    pub fn new() -> Result<Self> {
1872        Self::with_hooks(HostHooks::default())
1873    }
1874
1875    /// Create a Lua runtime with the supplied host capabilities.
1876    pub fn with_hooks(hooks: HostHooks) -> Result<Self> {
1877        let mut state = new_state().ok_or(LuaError::Memory)?;
1878        install_parser_hook(&mut state);
1879        hooks.install(&mut state);
1880        open_libs(&mut state)?;
1881        Ok(Self { state })
1882    }
1883
1884    pub fn state(&self) -> &LuaState {
1885        &self.state
1886    }
1887
1888    pub fn state_mut(&mut self) -> &mut LuaState {
1889        &mut self.state
1890    }
1891
1892    pub fn into_state(self) -> LuaState {
1893        self.state
1894    }
1895
1896    pub fn into_lua(self) -> Lua {
1897        Lua::from_initialized_state(self.state)
1898    }
1899
1900    /// Load and execute a Lua source chunk.
1901    pub fn exec(&mut self, source: &[u8], name: &[u8]) -> Result<()> {
1902        exec_state(&mut self.state, source, name)
1903    }
1904}
1905
1906fn exec_state(state: &mut LuaState, source: &[u8], name: &[u8]) -> Result<()> {
1907    let status = load_buffer(state, source, name)?;
1908    if status != 0 {
1909        let err = state.pop();
1910        return Err(LuaError::from_value(err));
1911    }
1912    lua_vm::api::pcall_k(state, 0, 0, 0, 0, None)?;
1913    Ok(())
1914}
1915
1916pub fn install_parser_hook(state: &mut LuaState) {
1917    state.global_mut().parser_hook = Some(parser_hook);
1918}
1919
1920fn parser_hook(
1921    state: &mut LuaState,
1922    source: &[u8],
1923    name: &[u8],
1924    firstchar: i32,
1925) -> Result<GcRef<LuaLClosure>> {
1926    let _heap_guard = heap_guard(state);
1927    let proto = lua_parse::parse(
1928        state,
1929        lua_parse::DynData::default(),
1930        source,
1931        name,
1932        firstchar,
1933    )?;
1934    let nupvals = proto.upvalues.len();
1935    let mut upvals = Vec::with_capacity(nupvals);
1936    for _ in 0..nupvals {
1937        upvals.push(std::cell::Cell::new(GcRef::new(UpVal::closed(
1938            RawLuaValue::Nil,
1939        ))));
1940    }
1941    Ok(GcRef::new(LuaLClosure {
1942        proto: GcRef::new(*proto),
1943        upvals,
1944    }))
1945}
1946
1947#[cfg(test)]
1948mod tests {
1949    use super::*;
1950    use std::cell::Cell;
1951
1952    fn external_root_count(lua: &Lua) -> usize {
1953        lua.with_state(|state| state.global().external_roots.len())
1954    }
1955
1956    struct Counter {
1957        value: i64,
1958    }
1959
1960    impl UserData for Counter {
1961        fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
1962            methods.add_method("get", |_lua, this, ()| Ok(this.value));
1963            methods.add_method_mut("inc", |_lua, this, delta: i64| {
1964                this.value += delta;
1965                Ok(this.value)
1966            });
1967        }
1968    }
1969
1970    struct PropertyBag {
1971        value: i64,
1972    }
1973
1974    impl UserData for PropertyBag {
1975        fn add_meta_methods<M: UserDataMethods<Self>>(methods: &mut M) {
1976            methods.add_meta_method(MetaMethod::Index, |_lua, this, key: String| {
1977                if key == "value" {
1978                    Ok(Value::Integer(this.value))
1979                } else {
1980                    Ok(Value::Nil)
1981                }
1982            });
1983            methods.add_meta_method_mut(
1984                MetaMethod::NewIndex,
1985                |_lua, this, (key, value): (String, i64)| {
1986                    if key != "value" {
1987                        return Err(LuaError::runtime(format_args!("unknown property")));
1988                    }
1989                    this.value = value;
1990                    Ok(())
1991                },
1992            );
1993        }
1994    }
1995
1996    #[test]
1997    fn rooted_table_clone_and_drop_manage_root_slots() {
1998        let lua = Lua::new();
1999        assert_eq!(external_root_count(&lua), 0);
2000
2001        let table = lua.create_table().expect("table should allocate");
2002        assert_eq!(external_root_count(&lua), 1);
2003
2004        let cloned = table.clone();
2005        assert_eq!(external_root_count(&lua), 2);
2006
2007        drop(table);
2008        assert_eq!(external_root_count(&lua), 1);
2009
2010        cloned.set("answer", 42_i64).expect("set should succeed");
2011        lua.gc_collect();
2012        assert_eq!(
2013            cloned.get::<_, i64>("answer").expect("get should succeed"),
2014            42
2015        );
2016
2017        drop(cloned);
2018        assert_eq!(external_root_count(&lua), 0);
2019    }
2020
2021    #[test]
2022    fn table_values_survive_forced_collection_between_operations() {
2023        let lua = Lua::new();
2024        let table = lua.create_table().expect("table should allocate");
2025
2026        lua.gc_collect();
2027        table.set("k", "v").expect("set should succeed");
2028        table.set(1_i64, "array").expect("array set should succeed");
2029        lua.gc_collect();
2030
2031        let value: String = table.get("k").expect("get should succeed");
2032        assert_eq!(value, "v");
2033        assert_eq!(table.len().expect("len should succeed"), 1);
2034    }
2035
2036    #[test]
2037    fn chunk_exec_eval_and_function_call_use_rooted_handles() {
2038        let lua = Lua::new();
2039        lua.load("function add(a, b) return a + b end")
2040            .set_name("test")
2041            .exec()
2042            .expect("chunk should execute");
2043
2044        let globals = lua.globals();
2045        let add: Function = globals.get("add").expect("function should exist");
2046        let result: i64 = add.call((20_i64, 22_i64)).expect("call should work");
2047        assert_eq!(result, 42);
2048
2049        let eval_result: i64 = lua
2050            .load("return add(1, 2)")
2051            .eval()
2052            .expect("eval should work");
2053        assert_eq!(eval_result, 3);
2054    }
2055
2056    #[test]
2057    fn rust_callback_captures_state_and_reenters_lua() {
2058        let lua = Lua::new();
2059        lua.load("function twice(v) return v * 2 end")
2060            .exec()
2061            .expect("chunk should execute");
2062
2063        let globals = lua.globals();
2064        let twice: Function = globals.get("twice").expect("function should exist");
2065        let calls = Rc::new(Cell::new(0));
2066        let calls_for_callback = calls.clone();
2067
2068        let callback = lua
2069            .create_function(move |_lua, value: i64| {
2070                calls_for_callback.set(calls_for_callback.get() + 1);
2071                let doubled: i64 = twice.call(value)?;
2072                Ok(doubled + 1)
2073            })
2074            .expect("callback should create");
2075        globals
2076            .set("from_rust", callback)
2077            .expect("callback should register");
2078
2079        let result: i64 = lua
2080            .load("return from_rust(20)")
2081            .eval()
2082            .expect("callback should run");
2083        assert_eq!(result, 41);
2084        assert_eq!(calls.get(), 1);
2085    }
2086
2087    #[test]
2088    fn rust_callback_accepts_and_returns_collectable_values() {
2089        let lua = Lua::new();
2090        let globals = lua.globals();
2091        let callback = lua
2092            .create_function(|lua, name: String| {
2093                let table = lua.create_table()?;
2094                table.set("name", name)?;
2095                Ok(table)
2096            })
2097            .expect("callback should create");
2098        globals
2099            .set("make_record", callback)
2100            .expect("callback should register");
2101
2102        let result: String = lua
2103            .load("return make_record('lua-rs').name")
2104            .eval()
2105            .expect("callback should return table");
2106        assert_eq!(result, "lua-rs");
2107    }
2108
2109    #[test]
2110    fn rust_callback_mut_tracks_state() {
2111        let lua = Lua::new();
2112        let globals = lua.globals();
2113        let mut next = 0_i64;
2114        let callback = lua
2115            .create_function_mut(move |_lua, delta: i64| {
2116                next += delta;
2117                Ok(next)
2118            })
2119            .expect("callback should create");
2120        globals
2121            .set("next", callback)
2122            .expect("callback should register");
2123
2124        let result: (i64, i64) = lua
2125            .load("return next(2), next(5)")
2126            .eval()
2127            .expect("callback should run");
2128        assert_eq!(result, (2, 7));
2129    }
2130
2131    #[test]
2132    fn dropped_rust_callback_releases_captured_handles_after_gc() {
2133        let lua = Lua::new();
2134        let table = lua.create_table().expect("table should allocate");
2135        table.set("value", 42_i64).expect("set should succeed");
2136        assert_eq!(external_root_count(&lua), 1);
2137
2138        let callback = {
2139            let captured = table.clone();
2140            lua.create_function(move |_lua, ()| captured.get::<_, i64>("value"))
2141                .expect("callback should create")
2142        };
2143        assert_eq!(external_root_count(&lua), 3);
2144
2145        drop(callback);
2146        lua.gc_collect();
2147        assert_eq!(external_root_count(&lua), 1);
2148        assert_eq!(table.get::<_, i64>("value").expect("table should live"), 42);
2149    }
2150
2151    #[test]
2152    fn userdata_methods_dispatch_and_track_borrows() {
2153        let lua = Lua::new();
2154        let globals = lua.globals();
2155        let counter = lua
2156            .create_userdata(Counter { value: 1 })
2157            .expect("userdata should create");
2158        globals
2159            .set("counter", &counter)
2160            .expect("userdata should register");
2161
2162        let result: i64 = lua
2163            .load("counter:inc(5); return counter:get()")
2164            .eval()
2165            .expect("methods should dispatch");
2166        assert_eq!(result, 6);
2167        assert_eq!(
2168            counter
2169                .with_borrow::<Counter, _>(|counter| counter.value)
2170                .expect("borrow should work"),
2171            6
2172        );
2173
2174        {
2175            let borrowed = counter
2176                .borrow::<Counter>()
2177                .expect("borrow guard should work");
2178            assert_eq!(borrowed.value, 6);
2179        }
2180
2181        {
2182            let mut borrowed = counter
2183                .borrow_mut::<Counter>()
2184                .expect("mutable borrow guard should work");
2185            borrowed.value = 9;
2186        }
2187
2188        assert_eq!(
2189            lua.load("return counter:get()")
2190                .eval::<i64>()
2191                .expect("method should see guard mutation"),
2192            9
2193        );
2194    }
2195
2196    #[test]
2197    fn userdata_payload_survives_gc_while_lua_holds_userdata() {
2198        let lua = Lua::new();
2199        let globals = lua.globals();
2200        let counter = lua
2201            .create_userdata(Counter { value: 10 })
2202            .expect("userdata should create");
2203        globals
2204            .set("counter", counter)
2205            .expect("userdata should register");
2206
2207        lua.gc_collect();
2208        let result: i64 = lua
2209            .load("counter:inc(2); collectgarbage('collect'); return counter:get()")
2210            .eval()
2211            .expect("userdata should survive collection");
2212        assert_eq!(result, 12);
2213    }
2214
2215    #[test]
2216    fn userdata_runtime_borrow_conflict_returns_lua_error() {
2217        let lua = Lua::new();
2218        let globals = lua.globals();
2219        let counter = lua
2220            .create_userdata(Counter { value: 1 })
2221            .expect("userdata should create");
2222        globals
2223            .set("counter", &counter)
2224            .expect("userdata should register");
2225
2226        let failed = counter
2227            .with_borrow::<Counter, _>(|_| lua.load("return counter:inc(1)").eval::<i64>().is_err())
2228            .expect("outer borrow should succeed");
2229        assert!(
2230            failed,
2231            "mutable method should fail while immutable borrow is held"
2232        );
2233        assert_eq!(
2234            counter
2235                .with_borrow::<Counter, _>(|counter| counter.value)
2236                .expect("borrow should work"),
2237            1
2238        );
2239    }
2240
2241    #[test]
2242    fn userdata_index_and_newindex_metamethods_dispatch() {
2243        let lua = Lua::new();
2244        let globals = lua.globals();
2245        let bag = lua
2246            .create_userdata(PropertyBag { value: 7 })
2247            .expect("userdata should create");
2248        globals.set("bag", &bag).expect("userdata should register");
2249
2250        let result: i64 = lua
2251            .load("bag.value = 42; return bag.value")
2252            .eval()
2253            .expect("metamethods should dispatch");
2254        assert_eq!(result, 42);
2255        assert_eq!(
2256            bag.with_borrow::<PropertyBag, _>(|bag| bag.value)
2257                .expect("borrow should work"),
2258            42
2259        );
2260    }
2261
2262    #[test]
2263    fn userdata_values_convert_directly_with_into_lua() {
2264        let lua = Lua::new();
2265        let globals = lua.globals();
2266        globals
2267            .set("counter", Counter { value: 3 })
2268            .expect("userdata should convert through IntoLua");
2269
2270        let result: i64 = lua
2271            .load("counter:inc(4); return counter:get()")
2272            .eval()
2273            .expect("converted userdata should dispatch methods");
2274        assert_eq!(result, 7);
2275    }
2276
2277    #[test]
2278    fn variadic_args_and_returns_convert_all_values() {
2279        let lua = Lua::new();
2280        let globals = lua.globals();
2281
2282        let sum = lua
2283            .create_function(|_lua, values: Variadic<i64>| Ok(values.iter().sum::<i64>()))
2284            .expect("variadic callback should create");
2285        globals.set("sum", sum).expect("callback should register");
2286        let result: i64 = lua
2287            .load("return sum(3, 2, 5)")
2288            .eval()
2289            .expect("variadic callback should run");
2290        assert_eq!(result, 10);
2291
2292        let echo = lua
2293            .create_function(|_lua, values: Variadic<Value>| Ok(values))
2294            .expect("variadic return callback should create");
2295        globals.set("echo", echo).expect("callback should register");
2296        let result: (i64, i64, i64) = lua
2297            .load("return echo(1, 2, 3)")
2298            .eval()
2299            .expect("variadic returns should stay separate");
2300        assert_eq!(result, (1, 2, 3));
2301
2302        let values: Variadic<i64> = lua
2303            .load("return 4, 5, 6")
2304            .eval()
2305            .expect("variadic eval should collect all returns");
2306        assert_eq!(values.into_vec(), vec![4, 5, 6]);
2307    }
2308
2309    #[test]
2310    fn vectors_maps_and_triple_returns_convert_through_tables() {
2311        let lua = Lua::new();
2312        let globals = lua.globals();
2313
2314        globals
2315            .set("list", vec![1_i64, 2, 3])
2316            .expect("vector should convert to table");
2317        let second: i64 = lua
2318            .load("return list[2]")
2319            .eval()
2320            .expect("table should be readable from Lua");
2321        assert_eq!(second, 2);
2322
2323        let list: Vec<i64> = lua
2324            .load("return {4, 5, 6}")
2325            .eval()
2326            .expect("table should convert to vector");
2327        assert_eq!(list, vec![4, 5, 6]);
2328
2329        let mut map = HashMap::new();
2330        map.insert("left".to_string(), 10_i64);
2331        map.insert("right".to_string(), 20_i64);
2332        globals
2333            .set("map", map)
2334            .expect("map should convert to table");
2335        let sum: i64 = lua
2336            .load("return map.left + map.right")
2337            .eval()
2338            .expect("map table should be readable from Lua");
2339        assert_eq!(sum, 30);
2340
2341        let map: HashMap<String, i64> = lua
2342            .load("return {alpha = 3, beta = 9}")
2343            .eval()
2344            .expect("table should convert to map");
2345        assert_eq!(map.get("alpha"), Some(&3));
2346        assert_eq!(map.get("beta"), Some(&9));
2347
2348        let triple: (i64, i64, i64) = lua
2349            .load("return 1, 2, 3")
2350            .eval()
2351            .expect("triple returns should convert");
2352        assert_eq!(triple, (1, 2, 3));
2353    }
2354}