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