1use std::any::{Any, TypeId};
60use std::cell::{Cell, Ref, RefCell, RefMut};
61use std::collections::HashMap;
62use std::ffi::c_void;
63use std::fmt;
64use std::hash::Hash;
65use std::ops::{Deref, DerefMut};
66use std::panic::{catch_unwind, AssertUnwindSafe};
67use std::rc::Rc;
68
69use lua_stdlib::auxlib::load_buffer;
70use lua_stdlib::init::open_libs;
71use lua_types::closure::{LuaCClosure as RawLuaCClosure, LuaClosure as RawLuaClosure, LuaLClosure};
72use lua_types::gc::GcRef;
73use lua_types::string::LuaString as RawLuaString;
74use lua_types::upval::UpVal;
75use lua_types::userdata::LuaUserData as RawLuaUserData;
76use lua_types::value::{LuaTable as RawLuaTable, LuaValue as RawLuaValue};
77use lua_vm::state::{
78 new_state, CpuClockHook, DynLibLoadHook, DynLibSymbolHook, DynLibUnloadHook, EntropyHook,
79 EnvHook, ExternalRootKey, FileLoaderHook, FileOpenHook, FileRemoveHook, FileRenameHook,
80 InputHook, LuaCallable, LuaRustFunction, LuaState, OsExecuteHook, OutputHook, PopenHook,
81 TempNameHook, UnixTimeHook,
82};
83
84pub use lua_types::{LuaError, LuaFileHandle};
85pub use lua_vm::state::{DynLibId, DynamicSymbol, OsExecuteReason, OsExecuteResult};
86
87#[cfg(feature = "derive")]
88pub use lua_rs_derive::{lua_methods, LuaUserData};
89
90pub type Error = LuaError;
91pub type Result<T> = std::result::Result<T, Error>;
92
93#[derive(Clone, Copy, Default)]
101pub struct HostHooks {
102 pub file_loader_hook: Option<FileLoaderHook>,
103 pub file_open_hook: Option<FileOpenHook>,
104 pub stdin_hook: Option<InputHook>,
105 pub stdout_hook: Option<OutputHook>,
106 pub stderr_hook: Option<OutputHook>,
107 pub env_hook: Option<EnvHook>,
108 pub unix_time_hook: Option<UnixTimeHook>,
109 pub cpu_clock_hook: Option<CpuClockHook>,
110 pub entropy_hook: Option<EntropyHook>,
111 pub temp_name_hook: Option<TempNameHook>,
112 pub popen_hook: Option<PopenHook>,
113 pub file_remove_hook: Option<FileRemoveHook>,
114 pub file_rename_hook: Option<FileRenameHook>,
115 pub os_execute_hook: Option<OsExecuteHook>,
116 pub dynlib_load_hook: Option<DynLibLoadHook>,
117 pub dynlib_symbol_hook: Option<DynLibSymbolHook>,
118 pub dynlib_unload_hook: Option<DynLibUnloadHook>,
119}
120
121impl HostHooks {
122 pub fn new() -> Self {
123 Self::default()
124 }
125
126 pub fn file_loader(mut self, hook: FileLoaderHook) -> Self {
127 self.file_loader_hook = Some(hook);
128 self
129 }
130
131 pub fn file_open(mut self, hook: FileOpenHook) -> Self {
132 self.file_open_hook = Some(hook);
133 self
134 }
135
136 pub fn stdin(mut self, hook: InputHook) -> Self {
137 self.stdin_hook = Some(hook);
138 self
139 }
140
141 pub fn stdout(mut self, hook: OutputHook) -> Self {
142 self.stdout_hook = Some(hook);
143 self
144 }
145
146 pub fn stderr(mut self, hook: OutputHook) -> Self {
147 self.stderr_hook = Some(hook);
148 self
149 }
150
151 pub fn env(mut self, hook: EnvHook) -> Self {
152 self.env_hook = Some(hook);
153 self
154 }
155
156 pub fn unix_time(mut self, hook: UnixTimeHook) -> Self {
157 self.unix_time_hook = Some(hook);
158 self
159 }
160
161 pub fn cpu_clock(mut self, hook: CpuClockHook) -> Self {
162 self.cpu_clock_hook = Some(hook);
163 self
164 }
165
166 pub fn entropy(mut self, hook: EntropyHook) -> Self {
167 self.entropy_hook = Some(hook);
168 self
169 }
170
171 pub fn temp_name(mut self, hook: TempNameHook) -> Self {
172 self.temp_name_hook = Some(hook);
173 self
174 }
175
176 pub fn popen(mut self, hook: PopenHook) -> Self {
177 self.popen_hook = Some(hook);
178 self
179 }
180
181 pub fn file_remove(mut self, hook: FileRemoveHook) -> Self {
182 self.file_remove_hook = Some(hook);
183 self
184 }
185
186 pub fn file_rename(mut self, hook: FileRenameHook) -> Self {
187 self.file_rename_hook = Some(hook);
188 self
189 }
190
191 pub fn os_execute(mut self, hook: OsExecuteHook) -> Self {
192 self.os_execute_hook = Some(hook);
193 self
194 }
195
196 pub fn dynlib_load(mut self, hook: DynLibLoadHook) -> Self {
197 self.dynlib_load_hook = Some(hook);
198 self
199 }
200
201 pub fn dynlib_symbol(mut self, hook: DynLibSymbolHook) -> Self {
202 self.dynlib_symbol_hook = Some(hook);
203 self
204 }
205
206 pub fn dynlib_unload(mut self, hook: DynLibUnloadHook) -> Self {
207 self.dynlib_unload_hook = Some(hook);
208 self
209 }
210
211 pub fn install(self, state: &mut LuaState) {
212 let global = &mut *state.global_mut();
213 global.file_loader_hook = self.file_loader_hook;
214 global.file_open_hook = self.file_open_hook;
215 global.stdin_hook = self.stdin_hook;
216 global.stdout_hook = self.stdout_hook;
217 global.stderr_hook = self.stderr_hook;
218 global.env_hook = self.env_hook;
219 global.unix_time_hook = self.unix_time_hook;
220 global.cpu_clock_hook = self.cpu_clock_hook;
221 global.entropy_hook = self.entropy_hook;
222 global.temp_name_hook = self.temp_name_hook;
223 global.popen_hook = self.popen_hook;
224 global.file_remove_hook = self.file_remove_hook;
225 global.file_rename_hook = self.file_rename_hook;
226 global.os_execute_hook = self.os_execute_hook;
227 global.dynlib_load_hook = self.dynlib_load_hook;
228 global.dynlib_symbol_hook = self.dynlib_symbol_hook;
229 global.dynlib_unload_hook = self.dynlib_unload_hook;
230 }
231}
232
233#[derive(Clone)]
240pub struct Lua {
241 inner: Rc<LuaInner>,
242}
243
244struct LuaInner {
245 state: RefCell<LuaState>,
246 active_state: Cell<*mut LuaState>,
247 pending_external_unroots: RefCell<Vec<ExternalRootKey>>,
248 userdata_metatables: RefCell<HashMap<TypeId, GcRef<RawLuaTable>>>,
253}
254
255struct UserDataCell<T> {
256 value: RefCell<T>,
257}
258
259struct RustCallbackCell {
260 function: LuaRustFunction,
261}
262
263struct ActiveStateGuard<'a> {
264 inner: &'a LuaInner,
265 previous: *mut LuaState,
266}
267
268impl Drop for ActiveStateGuard<'_> {
269 fn drop(&mut self) {
270 self.inner.active_state.set(self.previous);
271 }
272}
273
274impl LuaInner {
275 fn enter_active(&self, state: *mut LuaState) -> ActiveStateGuard<'_> {
276 let previous = self.active_state.replace(state);
277 ActiveStateGuard {
278 inner: self,
279 previous,
280 }
281 }
282
283 fn flush_pending_external_unroots(&self, state: &mut LuaState) {
284 let pending = self.pending_external_unroots.replace(Vec::new());
285 if pending.is_empty() {
286 return;
287 }
288
289 let mut still_pending = Vec::new();
290 for key in pending {
291 if state.try_external_unroot_value(key).is_err() {
292 still_pending.push(key);
293 }
294 }
295
296 if !still_pending.is_empty() {
297 self.pending_external_unroots
298 .borrow_mut()
299 .extend(still_pending);
300 }
301 }
302}
303
304impl fmt::Debug for Lua {
305 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
306 f.debug_struct("Lua").finish_non_exhaustive()
307 }
308}
309
310impl Lua {
311 pub fn new() -> Self {
313 Self::try_new().expect("Lua runtime should initialize")
314 }
315
316 pub fn try_new() -> Result<Self> {
318 Self::with_hooks(HostHooks::default())
319 }
320
321 pub fn with_hooks(hooks: HostHooks) -> Result<Self> {
323 let mut state = new_state().ok_or(LuaError::Memory)?;
324 install_parser_hook(&mut state);
325 hooks.install(&mut state);
326 open_libs(&mut state)?;
327 Ok(Self::from_initialized_state(state))
328 }
329
330 fn from_initialized_state(state: LuaState) -> Self {
331 Lua {
332 inner: Rc::new(LuaInner {
333 state: RefCell::new(state),
334 active_state: Cell::new(std::ptr::null_mut()),
335 pending_external_unroots: RefCell::new(Vec::new()),
336 userdata_metatables: RefCell::new(HashMap::new()),
337 }),
338 }
339 }
340
341 fn with_state<R>(&self, f: impl FnOnce(&mut LuaState) -> R) -> R {
342 if let Ok(mut state) = self.inner.state.try_borrow_mut() {
343 let _active = self.inner.enter_active(&mut *state);
344 self.inner.flush_pending_external_unroots(&mut state);
345 let result = f(&mut state);
346 self.inner.flush_pending_external_unroots(&mut state);
347 return result;
348 }
349
350 let state = self
351 .active_state_mut()
352 .expect("re-entrant Lua access without an active state");
353 let result = f(state);
354 self.inner.flush_pending_external_unroots(state);
355 result
356 }
357
358 fn active_state_mut(&self) -> Option<&mut LuaState> {
359 let state = self.inner.active_state.get();
360 if state.is_null() {
361 return None;
362 }
363
364 Some(unsafe { &mut *state })
370 }
371
372 fn unroot_external_key(&self, key: ExternalRootKey) {
373 let removed = if let Ok(mut state) = self.inner.state.try_borrow_mut() {
374 let _active = self.inner.enter_active(&mut *state);
375 self.inner.flush_pending_external_unroots(&mut state);
376 let removed = state.try_external_unroot_value(key).is_ok();
377 self.inner.flush_pending_external_unroots(&mut state);
378 removed
379 } else {
380 if let Some(state) = self.active_state_mut() {
381 let removed = state.try_external_unroot_value(key).is_ok();
382 self.inner.flush_pending_external_unroots(state);
383 removed
384 } else {
385 false
386 }
387 };
388
389 if !removed {
390 self.inner.pending_external_unroots.borrow_mut().push(key);
391 }
392 }
393
394 fn root_raw(&self, value: RawLuaValue) -> RootedValue {
395 let key = self.with_state(|state| state.external_root_value(value));
396 RootedValue {
397 lua: self.clone(),
398 key,
399 }
400 }
401
402 fn root_raw_in_state(&self, state: &mut LuaState, value: RawLuaValue) -> RootedValue {
403 let key = state.external_root_value(value);
404 RootedValue {
405 lua: self.clone(),
406 key,
407 }
408 }
409
410 fn userdata_cell<'a, T: 'static>(
411 &self,
412 userdata: &'a AnyUserData,
413 ) -> Result<&'a UserDataCell<T>> {
414 if !Rc::ptr_eq(&self.inner, &userdata.root.lua.inner) {
415 return Err(LuaError::runtime(format_args!(
416 "Lua userdata belongs to a different state"
417 )));
418 }
419 userdata.host_cell()
420 }
421
422 pub fn load(&self, source: impl AsRef<[u8]>) -> Chunk {
424 Chunk {
425 lua: self.clone(),
426 source: source.as_ref().to_vec(),
427 name: b"chunk".to_vec(),
428 }
429 }
430
431 pub fn globals(&self) -> Table {
433 let raw = self.with_state(|state| state.global().globals.clone());
434 Table {
435 root: self.root_raw(raw),
436 }
437 }
438
439 pub fn create_table(&self) -> Result<Table> {
441 let root = self.with_state(|state| {
442 let _heap_guard = heap_guard(state);
443 let table = state.new_table();
444 let raw = RawLuaValue::Table(table);
445 let key = state.external_root_value(raw);
446 state.gc().check_step();
447 RootedValue {
448 lua: self.clone(),
449 key,
450 }
451 });
452 Ok(Table { root })
453 }
454
455 pub fn create_string(&self, bytes: impl AsRef<[u8]>) -> Result<LuaString> {
457 let bytes = bytes.as_ref();
458 let root = self.with_state(|state| {
459 let _heap_guard = heap_guard(state);
460 let string = state.new_string(bytes)?;
461 let raw = RawLuaValue::Str(string);
462 let key = state.external_root_value(raw);
463 state.gc().check_step();
464 Ok::<_, LuaError>(RootedValue {
465 lua: self.clone(),
466 key,
467 })
468 })?;
469 Ok(LuaString { root })
470 }
471
472 pub fn create_function<A, R, F>(&self, func: F) -> Result<Function>
473 where
474 A: FromLuaMulti + 'static,
475 R: IntoLuaMulti + 'static,
476 F: Fn(&Lua, A) -> Result<R> + 'static,
477 {
478 let lua_weak = Rc::downgrade(&self.inner);
479 let callable: LuaRustFunction = Rc::new(move |state| {
480 let lua = match lua_weak.upgrade() {
481 Some(inner) => Lua { inner },
482 None => {
483 return Err(LuaError::runtime(format_args!(
484 "Lua callback fired after the state was dropped"
485 )))
486 }
487 };
488 match catch_unwind(AssertUnwindSafe(|| {
489 let args = callback_args(state, &lua)?;
490 let args = A::from_lua_multi(args, &lua)?;
491 let returns = func(&lua, args)?;
492 let returns = returns.into_lua_multi(&lua)?;
493 push_callback_returns(state, &lua, returns)
494 })) {
495 Ok(result) => result,
496 Err(_) => Err(LuaError::runtime(format_args!("Rust callback panicked"))),
497 }
498 });
499 self.create_registered_function(callable)
500 }
501
502 pub fn create_function_mut<A, R, F>(&self, func: F) -> Result<Function>
503 where
504 A: FromLuaMulti + 'static,
505 R: IntoLuaMulti + 'static,
506 F: FnMut(&Lua, A) -> Result<R> + 'static,
507 {
508 let func = RefCell::new(func);
509 self.create_function(move |lua, args| {
510 let mut func = func.try_borrow_mut().map_err(|_| {
511 LuaError::runtime(format_args!("mutable Rust callback is already borrowed"))
512 })?;
513 func(lua, args)
514 })
515 }
516
517 fn create_registered_function(&self, callable: LuaRustFunction) -> Result<Function> {
518 let root = self.with_state(|state| {
519 let trampoline = rust_callback_trampoline as lua_vm::state::LuaCFunction;
520 let idx = {
521 let mut global = state.global_mut();
522 match global.c_functions.iter().position(|existing| {
523 existing
524 .as_bare()
525 .is_some_and(|existing| std::ptr::fn_addr_eq(existing, trampoline))
526 }) {
527 Some(idx) => idx,
528 None => {
529 let idx = global.c_functions.len();
530 global.c_functions.push(LuaCallable::bare(trampoline));
531 idx
532 }
533 }
534 };
535 let raw = with_heap_guard(state, || {
536 let callback_payload = GcRef::new(RawLuaUserData {
537 data: Box::new([]),
538 uv: Vec::new(),
539 metatable: RefCell::new(None),
540 host_value: RefCell::new(Some(
541 Rc::new(RustCallbackCell { function: callable }) as Rc<dyn Any>,
542 )),
543 });
544 RawLuaValue::Function(RawLuaClosure::C(GcRef::new(RawLuaCClosure {
545 func: idx,
546 upvalues: vec![RawLuaValue::UserData(callback_payload)],
547 })))
548 });
549 let key = state.external_root_value(raw);
550 state.gc().check_step();
551 RootedValue {
552 lua: self.clone(),
553 key,
554 }
555 });
556 Ok(Function { root })
557 }
558
559 fn create_userdata_method<T, A, R, F>(&self, method: F) -> Result<Function>
560 where
561 T: UserData,
562 A: FromLuaMulti + 'static,
563 R: IntoLuaMulti + 'static,
564 F: Fn(&Lua, &T, A) -> Result<R> + 'static,
565 {
566 let lua_weak = Rc::downgrade(&self.inner);
567 let callable: LuaRustFunction = Rc::new(move |state| {
568 let lua = match lua_weak.upgrade() {
569 Some(inner) => Lua { inner },
570 None => {
571 return Err(LuaError::runtime(format_args!(
572 "Lua callback fired after the state was dropped"
573 )))
574 }
575 };
576 match catch_unwind(AssertUnwindSafe(|| {
577 let (userdata, args) = callback_userdata_args(state, &lua)?;
578 let args = A::from_lua_multi(args, &lua)?;
579 let cell = lua.userdata_cell::<T>(&userdata)?;
580 let value = cell.value.try_borrow().map_err(|_| {
581 LuaError::runtime(format_args!("userdata is already mutably borrowed"))
582 })?;
583 let returns = method(&lua, &value, args)?;
584 let returns = returns.into_lua_multi(&lua)?;
585 push_callback_returns(state, &lua, returns)
586 })) {
587 Ok(result) => result,
588 Err(_) => Err(LuaError::runtime(format_args!(
589 "Rust userdata method panicked"
590 ))),
591 }
592 });
593 self.create_registered_function(callable)
594 }
595
596 fn create_userdata_method_mut<T, A, R, F>(&self, method: F) -> Result<Function>
597 where
598 T: UserData,
599 A: FromLuaMulti + 'static,
600 R: IntoLuaMulti + 'static,
601 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
602 {
603 let lua_weak = Rc::downgrade(&self.inner);
604 let callable: LuaRustFunction = Rc::new(move |state| {
605 let lua = match lua_weak.upgrade() {
606 Some(inner) => Lua { inner },
607 None => {
608 return Err(LuaError::runtime(format_args!(
609 "Lua callback fired after the state was dropped"
610 )))
611 }
612 };
613 match catch_unwind(AssertUnwindSafe(|| {
614 let (userdata, args) = callback_userdata_args(state, &lua)?;
615 let args = A::from_lua_multi(args, &lua)?;
616 let cell = lua.userdata_cell::<T>(&userdata)?;
617 let mut value = cell
618 .value
619 .try_borrow_mut()
620 .map_err(|_| LuaError::runtime(format_args!("userdata is already borrowed")))?;
621 let returns = method(&lua, &mut value, args)?;
622 let returns = returns.into_lua_multi(&lua)?;
623 push_callback_returns(state, &lua, returns)
624 })) {
625 Ok(result) => result,
626 Err(_) => Err(LuaError::runtime(format_args!(
627 "Rust userdata method panicked"
628 ))),
629 }
630 });
631 self.create_registered_function(callable)
632 }
633
634 pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData>
635 where
636 T: UserData,
637 {
638 let type_id = TypeId::of::<T>();
639 let cached = self
640 .inner
641 .userdata_metatables
642 .borrow()
643 .get(&type_id)
644 .cloned();
645 let metatable = match cached {
646 Some(metatable) => metatable,
647 None => {
648 let mut methods = UserDataMethodRegistry::<T>::new(self);
649 T::add_methods(&mut methods);
650 T::add_meta_methods(&mut methods);
651 let metatable = methods.build_metatable()?;
652 self.inner
653 .userdata_metatables
654 .borrow_mut()
655 .insert(type_id, metatable.clone());
656 metatable
657 }
658 };
659 self.attach_userdata(data, metatable)
660 }
661
662 fn attach_userdata<T: UserData>(
666 &self,
667 data: T,
668 metatable: GcRef<RawLuaTable>,
669 ) -> Result<AnyUserData> {
670 let cell: Rc<dyn Any> = Rc::new(UserDataCell {
671 value: RefCell::new(data),
672 });
673 let host_value = cell.clone();
674 let root = self.with_state(|state| {
675 let userdata = with_heap_guard(state, || {
676 GcRef::new(RawLuaUserData {
677 data: Box::new([]),
678 uv: Vec::new(),
679 metatable: RefCell::new(None),
680 host_value: RefCell::new(None),
681 })
682 });
683 userdata.set_metatable(Some(metatable));
684 userdata.set_host_value(Some(cell));
685 let key = state.external_root_value(RawLuaValue::UserData(userdata));
686 RootedValue {
687 lua: self.clone(),
688 key,
689 }
690 });
691 Ok(AnyUserData {
692 root,
693 host_value: Some(host_value),
694 })
695 }
696
697 pub fn gc_collect(&self) {
699 self.with_state(|state| state.gc().full_collect());
700 }
701}
702
703pub struct Chunk {
704 lua: Lua,
705 source: Vec<u8>,
706 name: Vec<u8>,
707}
708
709impl Chunk {
710 pub fn set_name(mut self, name: impl AsRef<[u8]>) -> Self {
711 self.name = name.as_ref().to_vec();
712 self
713 }
714
715 pub fn exec(self) -> Result<()> {
716 self.lua
717 .with_state(|state| exec_state(state, &self.source, &self.name))
718 }
719
720 pub fn eval<T: FromLuaMulti>(self) -> Result<T> {
721 let raws = self.lua.with_state(|state| {
722 let saved_top = state.top_idx();
723 let status = load_buffer(state, &self.source, &self.name)?;
724 if status != 0 {
725 let err = state.pop();
726 state.set_top_idx(saved_top);
727 return Err(LuaError::from_value(err));
728 }
729 match lua_vm::api::pcall_k(state, 0, T::NRESULTS, 0, 0, None) {
730 Ok(_) => {
731 let nresults = if T::NRESULTS < 0 {
732 state.top_idx().0.saturating_sub(saved_top.0) as i32
733 } else {
734 T::NRESULTS
735 };
736 let mut values = Vec::with_capacity(nresults as usize);
737 for _ in 0..nresults {
738 values.push(state.pop());
739 }
740 values.reverse();
741 state.set_top_idx(saved_top);
742 Ok(values)
743 }
744 Err(err) => {
745 state.set_top_idx(saved_top);
746 Err(err)
747 }
748 }
749 })?;
750 let values = raws
751 .into_iter()
752 .map(|raw| Value::from_raw(&self.lua, raw))
753 .collect::<Result<Vec<_>>>()?;
754 T::from_lua_multi(values, &self.lua)
755 }
756}
757
758#[derive(Debug)]
759struct RootedValue {
760 lua: Lua,
761 key: ExternalRootKey,
762}
763
764impl RootedValue {
765 fn raw(&self) -> Result<RawLuaValue> {
766 self.lua
767 .with_state(|state| state.external_rooted_value(self.key))
768 .ok_or_else(stale_handle_error)
769 }
770
771 fn raw_for_lua(&self, lua: &Lua, state: &LuaState) -> Result<RawLuaValue> {
772 if !Rc::ptr_eq(&self.lua.inner, &lua.inner) {
773 return Err(LuaError::runtime(format_args!(
774 "Lua handle belongs to a different state"
775 )));
776 }
777 state
778 .external_rooted_value(self.key)
779 .ok_or_else(stale_handle_error)
780 }
781}
782
783impl Clone for RootedValue {
784 fn clone(&self) -> Self {
785 let raw = self.raw().expect("rooted Lua handle should not be stale");
786 self.lua.root_raw(raw)
787 }
788}
789
790impl Drop for RootedValue {
791 fn drop(&mut self) {
792 self.lua.unroot_external_key(self.key);
793 }
794}
795
796#[derive(Debug, Clone)]
798pub enum Value {
799 Nil,
800 Boolean(bool),
801 Integer(i64),
802 Number(f64),
803 String(LuaString),
804 Table(Table),
805 Function(Function),
806 UserData(AnyUserData),
807 LightUserData(*mut c_void),
808 Thread(Thread),
809}
810
811impl Value {
812 fn from_raw(lua: &Lua, raw: RawLuaValue) -> Result<Self> {
813 lua.with_state(|state| Self::from_raw_in_state(lua, state, raw))
814 }
815
816 fn from_raw_in_state(lua: &Lua, state: &mut LuaState, raw: RawLuaValue) -> Result<Self> {
817 Ok(match raw {
818 RawLuaValue::Nil => Value::Nil,
819 RawLuaValue::Bool(v) => Value::Boolean(v),
820 RawLuaValue::Int(v) => Value::Integer(v),
821 RawLuaValue::Float(v) => Value::Number(v),
822 RawLuaValue::Str(v) => Value::String(LuaString {
823 root: lua.root_raw_in_state(state, RawLuaValue::Str(v)),
824 }),
825 RawLuaValue::Table(v) => Value::Table(Table {
826 root: lua.root_raw_in_state(state, RawLuaValue::Table(v)),
827 }),
828 RawLuaValue::Function(v) => Value::Function(Function {
829 root: lua.root_raw_in_state(state, RawLuaValue::Function(v)),
830 }),
831 RawLuaValue::UserData(v) => {
832 let host_value = v.host_value();
833 Value::UserData(AnyUserData {
834 root: lua.root_raw_in_state(state, RawLuaValue::UserData(v)),
835 host_value,
836 })
837 }
838 RawLuaValue::LightUserData(v) => Value::LightUserData(v),
839 RawLuaValue::Thread(v) => Value::Thread(Thread {
840 root: lua.root_raw_in_state(state, RawLuaValue::Thread(v)),
841 }),
842 })
843 }
844
845 fn to_raw_for_lua(&self, lua: &Lua, state: &LuaState) -> Result<RawLuaValue> {
846 match self {
847 Value::Nil => Ok(RawLuaValue::Nil),
848 Value::Boolean(v) => Ok(RawLuaValue::Bool(*v)),
849 Value::Integer(v) => Ok(RawLuaValue::Int(*v)),
850 Value::Number(v) => Ok(RawLuaValue::Float(*v)),
851 Value::String(v) => v.root.raw_for_lua(lua, state),
852 Value::Table(v) => v.root.raw_for_lua(lua, state),
853 Value::Function(v) => v.root.raw_for_lua(lua, state),
854 Value::UserData(v) => v.root.raw_for_lua(lua, state),
855 Value::LightUserData(v) => Ok(RawLuaValue::LightUserData(*v)),
856 Value::Thread(v) => v.root.raw_for_lua(lua, state),
857 }
858 }
859}
860
861#[derive(Debug, Clone)]
862pub struct Table {
863 root: RootedValue,
864}
865
866impl Table {
867 fn raw_table(&self) -> Result<GcRef<RawLuaTable>> {
868 match self.root.raw()? {
869 RawLuaValue::Table(table) => Ok(table),
870 other => Err(type_error_raw(&other, "table")),
871 }
872 }
873
874 pub fn get<K, V>(&self, key: K) -> Result<V>
875 where
876 K: IntoLua,
877 V: FromLua,
878 {
879 let lua = self.root.lua.clone();
880 let key = key.into_lua(&lua)?;
881 let value_raw = lua.with_state(|state| {
882 let key_raw = key.to_raw_for_lua(&lua, state)?;
883 let table_raw = self.root.raw_for_lua(&lua, state)?;
884 state.table_get_with_tm(&table_raw, &key_raw)
885 })?;
886 let value = Value::from_raw(&lua, value_raw)?;
887 V::from_lua(value, &lua)
888 }
889
890 pub fn set<K, V>(&self, key: K, value: V) -> Result<()>
891 where
892 K: IntoLua,
893 V: IntoLua,
894 {
895 let lua = self.root.lua.clone();
896 let key = key.into_lua(&lua)?;
897 let value = value.into_lua(&lua)?;
898 lua.with_state(|state| {
899 let key_raw = key.to_raw_for_lua(&lua, state)?;
900 let value_raw = value.to_raw_for_lua(&lua, state)?;
901 let table_raw = self.root.raw_for_lua(&lua, state)?;
902 state.table_set_with_tm(&table_raw, key_raw, value_raw)
903 })
904 }
905
906 pub fn len(&self) -> Result<u64> {
907 Ok(self.raw_table()?.getn())
908 }
909}
910
911#[derive(Debug, Clone)]
912pub struct Function {
913 root: RootedValue,
914}
915
916impl Function {
917 pub fn call<A, R>(&self, args: A) -> Result<R>
918 where
919 A: IntoLuaMulti,
920 R: FromLuaMulti,
921 {
922 let lua = self.root.lua.clone();
923 let args = args.into_lua_multi(&lua)?;
924 let result_raws = lua.with_state(|state| {
925 let arg_raws = args
926 .iter()
927 .map(|value| value.to_raw_for_lua(&lua, state))
928 .collect::<Result<Vec<_>>>()?;
929 let function_raw = self.root.raw_for_lua(&lua, state)?;
930 let saved_top = state.top_idx();
931 state.push(function_raw);
932 for arg in &arg_raws {
933 state.push(*arg);
934 }
935 match lua_vm::api::pcall_k(state, arg_raws.len() as i32, R::NRESULTS, 0, 0, None) {
936 Ok(_) => {
937 let nresults = if R::NRESULTS < 0 {
938 state.top_idx().0.saturating_sub(saved_top.0) as i32
939 } else {
940 R::NRESULTS
941 };
942 let mut results = Vec::with_capacity(nresults as usize);
943 for _ in 0..nresults {
944 results.push(state.pop());
945 }
946 results.reverse();
947 state.set_top_idx(saved_top);
948 Ok(results)
949 }
950 Err(err) => {
951 state.set_top_idx(saved_top);
952 Err(err)
953 }
954 }
955 })?;
956 let values = result_raws
957 .into_iter()
958 .map(|raw| Value::from_raw(&lua, raw))
959 .collect::<Result<Vec<_>>>()?;
960 R::from_lua_multi(values, &lua)
961 }
962}
963
964#[derive(Debug, Clone)]
965pub struct LuaString {
966 root: RootedValue,
967}
968
969impl LuaString {
970 fn raw_string(&self) -> Result<GcRef<RawLuaString>> {
971 match self.root.raw()? {
972 RawLuaValue::Str(string) => Ok(string),
973 other => Err(type_error_raw(&other, "string")),
974 }
975 }
976
977 pub fn as_bytes(&self) -> Result<Vec<u8>> {
978 Ok(self.raw_string()?.as_bytes().to_vec())
979 }
980
981 pub fn to_str(&self) -> Result<String> {
982 let bytes = self.as_bytes()?;
983 String::from_utf8(bytes)
984 .map_err(|_| LuaError::runtime(format_args!("string is not valid UTF-8")))
985 }
986}
987
988#[derive(Clone)]
989pub struct AnyUserData {
990 root: RootedValue,
991 host_value: Option<Rc<dyn Any>>,
992}
993
994impl fmt::Debug for AnyUserData {
995 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
996 f.debug_struct("AnyUserData")
997 .field("root", &self.root)
998 .field("has_host_value", &self.host_value.is_some())
999 .finish()
1000 }
1001}
1002
1003impl AnyUserData {
1004 fn host_cell<T: 'static>(&self) -> Result<&UserDataCell<T>> {
1005 let host = self
1006 .host_value
1007 .as_deref()
1008 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust userdata payload")))?;
1009 host.downcast_ref::<UserDataCell<T>>()
1010 .ok_or_else(|| LuaError::runtime(format_args!("userdata type mismatch")))
1011 }
1012
1013 pub fn borrow<T>(&self) -> Result<Ref<'_, T>>
1014 where
1015 T: 'static,
1016 {
1017 self.host_cell::<T>()?
1018 .value
1019 .try_borrow()
1020 .map_err(|_| LuaError::runtime(format_args!("userdata is already mutably borrowed")))
1021 }
1022
1023 pub fn borrow_mut<T>(&self) -> Result<RefMut<'_, T>>
1024 where
1025 T: 'static,
1026 {
1027 self.host_cell::<T>()?
1028 .value
1029 .try_borrow_mut()
1030 .map_err(|_| LuaError::runtime(format_args!("userdata is already borrowed")))
1031 }
1032
1033 pub fn with_borrow<T, R>(&self, f: impl FnOnce(&T) -> R) -> Result<R>
1034 where
1035 T: 'static,
1036 {
1037 let value = self.borrow::<T>()?;
1038 Ok(f(&value))
1039 }
1040
1041 pub fn with_borrow_mut<T, R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R>
1042 where
1043 T: 'static,
1044 {
1045 let mut value = self.borrow_mut::<T>()?;
1046 Ok(f(&mut value))
1047 }
1048}
1049
1050#[derive(Debug, Clone)]
1051pub struct Thread {
1052 root: RootedValue,
1053}
1054
1055#[derive(Debug, Clone, Default, PartialEq, Eq)]
1061pub struct Variadic<T>(Vec<T>);
1062
1063impl<T> Variadic<T> {
1064 pub const fn new() -> Self {
1065 Self(Vec::new())
1066 }
1067
1068 pub fn with_capacity(capacity: usize) -> Self {
1069 Self(Vec::with_capacity(capacity))
1070 }
1071
1072 pub fn into_vec(self) -> Vec<T> {
1073 self.0
1074 }
1075}
1076
1077impl<T> Deref for Variadic<T> {
1078 type Target = Vec<T>;
1079
1080 fn deref(&self) -> &Self::Target {
1081 &self.0
1082 }
1083}
1084
1085impl<T> DerefMut for Variadic<T> {
1086 fn deref_mut(&mut self) -> &mut Self::Target {
1087 &mut self.0
1088 }
1089}
1090
1091impl<T> From<Vec<T>> for Variadic<T> {
1092 fn from(value: Vec<T>) -> Self {
1093 Self(value)
1094 }
1095}
1096
1097impl<T> From<Variadic<T>> for Vec<T> {
1098 fn from(value: Variadic<T>) -> Self {
1099 value.0
1100 }
1101}
1102
1103impl<T> FromIterator<T> for Variadic<T> {
1104 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
1105 Self(Vec::from_iter(iter))
1106 }
1107}
1108
1109impl<T> IntoIterator for Variadic<T> {
1110 type Item = T;
1111 type IntoIter = std::vec::IntoIter<T>;
1112
1113 fn into_iter(self) -> Self::IntoIter {
1114 self.0.into_iter()
1115 }
1116}
1117
1118pub trait UserData: 'static {
1119 fn add_methods<M: UserDataMethods<Self>>(_methods: &mut M)
1120 where
1121 Self: Sized,
1122 {
1123 }
1124
1125 fn add_meta_methods<M: UserDataMethods<Self>>(_methods: &mut M)
1126 where
1127 Self: Sized,
1128 {
1129 }
1130}
1131
1132pub trait UserDataMethods<T: UserData> {
1133 fn add_method<A, R, F>(&mut self, name: &str, method: F)
1134 where
1135 A: FromLuaMulti + 'static,
1136 R: IntoLuaMulti + 'static,
1137 F: Fn(&Lua, &T, A) -> Result<R> + 'static;
1138
1139 fn add_method_mut<A, R, F>(&mut self, name: &str, method: F)
1140 where
1141 A: FromLuaMulti + 'static,
1142 R: IntoLuaMulti + 'static,
1143 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static;
1144
1145 fn add_meta_method<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
1146 where
1147 A: FromLuaMulti + 'static,
1148 R: IntoLuaMulti + 'static,
1149 F: Fn(&Lua, &T, A) -> Result<R> + 'static;
1150
1151 fn add_meta_method_mut<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
1152 where
1153 A: FromLuaMulti + 'static,
1154 R: IntoLuaMulti + 'static,
1155 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static;
1156
1157 fn add_field_method_get<R, F>(&mut self, name: &str, getter: F)
1161 where
1162 R: IntoLuaMulti + 'static,
1163 F: Fn(&Lua, &T) -> Result<R> + 'static;
1164
1165 fn add_field_method_set<A, F>(&mut self, name: &str, setter: F)
1168 where
1169 A: FromLuaMulti + 'static,
1170 F: Fn(&Lua, &mut T, A) -> Result<()> + 'static;
1171}
1172
1173#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1174pub enum MetaMethod {
1175 Index,
1176 NewIndex,
1177 Add,
1178 Sub,
1179 Mul,
1180 Div,
1181 Mod,
1182 Pow,
1183 Unm,
1184 Len,
1185 Eq,
1186 Lt,
1187 Le,
1188 Concat,
1189 Call,
1190 ToString,
1191 Pairs,
1192}
1193
1194impl MetaMethod {
1195 fn name(self) -> &'static str {
1196 match self {
1197 MetaMethod::Index => "__index",
1198 MetaMethod::NewIndex => "__newindex",
1199 MetaMethod::Add => "__add",
1200 MetaMethod::Sub => "__sub",
1201 MetaMethod::Mul => "__mul",
1202 MetaMethod::Div => "__div",
1203 MetaMethod::Mod => "__mod",
1204 MetaMethod::Pow => "__pow",
1205 MetaMethod::Unm => "__unm",
1206 MetaMethod::Len => "__len",
1207 MetaMethod::Eq => "__eq",
1208 MetaMethod::Lt => "__lt",
1209 MetaMethod::Le => "__le",
1210 MetaMethod::Concat => "__concat",
1211 MetaMethod::Call => "__call",
1212 MetaMethod::ToString => "__tostring",
1213 MetaMethod::Pairs => "__pairs",
1214 }
1215 }
1216}
1217
1218fn root_for_state_lifetime(state: &mut LuaState, value: RawLuaValue) {
1229 let _ = state.external_root_value(value);
1230}
1231
1232struct UserDataMethodRegistry<'lua, T: UserData> {
1233 lua: &'lua Lua,
1234 methods: Vec<(String, Function)>,
1235 meta_methods: Vec<(MetaMethod, Function)>,
1236 fields_get: Vec<(String, Function)>,
1237 fields_set: Vec<(String, Function)>,
1238 error: Option<LuaError>,
1239 _marker: std::marker::PhantomData<T>,
1240}
1241
1242impl<'lua, T: UserData> UserDataMethodRegistry<'lua, T> {
1243 fn new(lua: &'lua Lua) -> Self {
1244 Self {
1245 lua,
1246 methods: Vec::new(),
1247 meta_methods: Vec::new(),
1248 fields_get: Vec::new(),
1249 fields_set: Vec::new(),
1250 error: None,
1251 _marker: std::marker::PhantomData,
1252 }
1253 }
1254
1255 fn record(&mut self, result: Result<Function>, insert: impl FnOnce(&mut Self, Function)) {
1256 if self.error.is_some() {
1257 return;
1258 }
1259 match result {
1260 Ok(function) => insert(self, function),
1261 Err(err) => self.error = Some(err),
1262 }
1263 }
1264
1265 fn build_metatable(mut self) -> Result<GcRef<RawLuaTable>> {
1269 if let Some(err) = self.error.take() {
1270 return Err(err);
1271 }
1272
1273 let lua = self.lua;
1274
1275 let method_table = lua.create_table()?;
1276 for (name, function) in &self.methods {
1277 method_table.set(name.as_str(), function)?;
1278 }
1279
1280 let field_getters = lua.create_table()?;
1281 for (name, function) in &self.fields_get {
1282 field_getters.set(name.as_str(), function)?;
1283 }
1284 let field_setters = lua.create_table()?;
1285 for (name, function) in &self.fields_set {
1286 field_setters.set(name.as_str(), function)?;
1287 }
1288
1289 let metatable = lua.create_table()?;
1292 let mut raw_index: Option<Function> = None;
1293 let mut raw_newindex: Option<Function> = None;
1294 for (metamethod, function) in &self.meta_methods {
1295 match metamethod {
1296 MetaMethod::Index => raw_index = Some(function.clone()),
1297 MetaMethod::NewIndex => raw_newindex = Some(function.clone()),
1298 other => {
1299 metatable.set(other.name(), function)?;
1300 }
1301 }
1302 }
1303
1304 let has_fields_get = !self.fields_get.is_empty();
1322 let has_methods = !self.methods.is_empty();
1323 let needs_index_composition = has_fields_get || (raw_index.is_some() && has_methods);
1324
1325 if needs_index_composition {
1326 let (getters_raw, methods_raw, raw_index_raw) = lua.with_state(|state| {
1327 let g = match field_getters.root.raw_for_lua(lua, state)? {
1328 RawLuaValue::Table(g) => g,
1329 v => return Err(type_error_raw(&v, "table")),
1330 };
1331 root_for_state_lifetime(state, RawLuaValue::Table(g.clone()));
1332 let m = match method_table.root.raw_for_lua(lua, state)? {
1333 RawLuaValue::Table(m) => m,
1334 v => return Err(type_error_raw(&v, "table")),
1335 };
1336 root_for_state_lifetime(state, RawLuaValue::Table(m.clone()));
1337 let r = match &raw_index {
1338 Some(f) => {
1339 let rv = f.root.raw_for_lua(lua, state)?;
1340 root_for_state_lifetime(state, rv.clone());
1341 Some(rv)
1342 }
1343 None => None,
1344 };
1345 Ok::<_, LuaError>((g, m, r))
1346 })?;
1347 let index_fn = lua.create_function(move |lua, (ud, key): (Value, Value)| {
1348 let getters = Table {
1349 root: lua.root_raw(RawLuaValue::Table(getters_raw.clone())),
1350 };
1351 let methods = Table {
1352 root: lua.root_raw(RawLuaValue::Table(methods_raw.clone())),
1353 };
1354 if let Value::Function(getter) = getters.get::<_, Value>(key.clone())? {
1355 return getter.call::<_, Value>(ud);
1356 }
1357 let method = methods.get::<_, Value>(key.clone())?;
1358 if !matches!(method, Value::Nil) {
1359 return Ok(method);
1360 }
1361 if let Some(raw_idx) = &raw_index_raw {
1362 let raw_fn = Function {
1363 root: lua.root_raw(raw_idx.clone()),
1364 };
1365 return raw_fn.call::<_, Value>((ud, key));
1366 }
1367 Ok(Value::Nil)
1368 })?;
1369 metatable.set(MetaMethod::Index.name(), &index_fn)?;
1370 } else if let Some(raw) = raw_index.as_ref() {
1371 metatable.set(MetaMethod::Index.name(), raw)?;
1372 } else {
1373 metatable.set(MetaMethod::Index.name(), &method_table)?;
1374 }
1375
1376 let has_fields_set = !self.fields_set.is_empty();
1379
1380 if has_fields_set {
1381 let (setters_raw, raw_newindex_raw) = lua.with_state(|state| {
1382 let s = match field_setters.root.raw_for_lua(lua, state)? {
1383 RawLuaValue::Table(s) => s,
1384 v => return Err(type_error_raw(&v, "table")),
1385 };
1386 root_for_state_lifetime(state, RawLuaValue::Table(s.clone()));
1387 let r = match &raw_newindex {
1388 Some(f) => {
1389 let rv = f.root.raw_for_lua(lua, state)?;
1390 root_for_state_lifetime(state, rv.clone());
1391 Some(rv)
1392 }
1393 None => None,
1394 };
1395 Ok::<_, LuaError>((s, r))
1396 })?;
1397 let newindex_fn =
1398 lua.create_function(move |lua, (ud, key, value): (Value, Value, Value)| {
1399 let setters = Table {
1400 root: lua.root_raw(RawLuaValue::Table(setters_raw.clone())),
1401 };
1402 if let Value::Function(setter) = setters.get::<_, Value>(key.clone())? {
1403 return setter.call::<_, Value>((ud, value));
1404 }
1405 if let Some(raw) = &raw_newindex_raw {
1406 let raw_fn = Function {
1407 root: lua.root_raw(raw.clone()),
1408 };
1409 return raw_fn.call::<_, Value>((ud, key, value));
1410 }
1411 Err(LuaError::runtime(format_args!(
1412 "cannot assign to unknown or read-only userdata field"
1413 )))
1414 })?;
1415 metatable.set(MetaMethod::NewIndex.name(), &newindex_fn)?;
1416 } else if let Some(raw) = raw_newindex.as_ref() {
1417 metatable.set(MetaMethod::NewIndex.name(), raw)?;
1418 }
1419
1420 self.lua.with_state(|state| {
1421 let metatable_raw = metatable.root.raw_for_lua(self.lua, state)?;
1422 let RawLuaValue::Table(metatable) = metatable_raw else {
1423 return Err(type_error_raw(&metatable_raw, "table"));
1424 };
1425 root_for_state_lifetime(state, RawLuaValue::Table(metatable.clone()));
1426 Ok(metatable)
1427 })
1428 }
1429}
1430
1431impl<T: UserData> UserDataMethods<T> for UserDataMethodRegistry<'_, T> {
1432 fn add_method<A, R, F>(&mut self, name: &str, method: F)
1433 where
1434 A: FromLuaMulti + 'static,
1435 R: IntoLuaMulti + 'static,
1436 F: Fn(&Lua, &T, A) -> Result<R> + 'static,
1437 {
1438 let name = name.to_string();
1439 let result = self.lua.create_userdata_method(method);
1440 self.record(result, move |this, function| {
1441 this.methods.push((name, function));
1442 });
1443 }
1444
1445 fn add_method_mut<A, R, F>(&mut self, name: &str, method: F)
1446 where
1447 A: FromLuaMulti + 'static,
1448 R: IntoLuaMulti + 'static,
1449 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
1450 {
1451 let name = name.to_string();
1452 let result = self.lua.create_userdata_method_mut(method);
1453 self.record(result, move |this, function| {
1454 this.methods.push((name, function));
1455 });
1456 }
1457
1458 fn add_meta_method<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
1459 where
1460 A: FromLuaMulti + 'static,
1461 R: IntoLuaMulti + 'static,
1462 F: Fn(&Lua, &T, A) -> Result<R> + 'static,
1463 {
1464 let result = self.lua.create_userdata_method(method);
1465 self.record(result, move |this, function| {
1466 this.meta_methods.push((metamethod, function));
1467 });
1468 }
1469
1470 fn add_meta_method_mut<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
1471 where
1472 A: FromLuaMulti + 'static,
1473 R: IntoLuaMulti + 'static,
1474 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
1475 {
1476 let result = self.lua.create_userdata_method_mut(method);
1477 self.record(result, move |this, function| {
1478 this.meta_methods.push((metamethod, function));
1479 });
1480 }
1481
1482 fn add_field_method_get<R, F>(&mut self, name: &str, getter: F)
1483 where
1484 R: IntoLuaMulti + 'static,
1485 F: Fn(&Lua, &T) -> Result<R> + 'static,
1486 {
1487 let name = name.to_string();
1488 let result = self
1489 .lua
1490 .create_userdata_method(move |lua, this, ()| getter(lua, this));
1491 self.record(result, move |this, function| {
1492 this.fields_get.push((name, function));
1493 });
1494 }
1495
1496 fn add_field_method_set<A, F>(&mut self, name: &str, setter: F)
1497 where
1498 A: FromLuaMulti + 'static,
1499 F: Fn(&Lua, &mut T, A) -> Result<()> + 'static,
1500 {
1501 let name = name.to_string();
1502 let result = self
1503 .lua
1504 .create_userdata_method_mut(move |lua, this, arg: A| setter(lua, this, arg));
1505 self.record(result, move |this, function| {
1506 this.fields_set.push((name, function));
1507 });
1508 }
1509}
1510
1511pub trait IntoLua {
1512 fn into_lua(self, lua: &Lua) -> Result<Value>;
1513}
1514
1515pub trait FromLua: Sized {
1516 fn from_lua(value: Value, lua: &Lua) -> Result<Self>;
1517}
1518
1519pub trait IntoLuaMulti {
1520 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>>;
1521}
1522
1523pub trait FromLuaMulti: Sized {
1524 const NRESULTS: i32;
1525
1526 fn from_lua_multi(values: Vec<Value>, lua: &Lua) -> Result<Self>;
1527}
1528
1529impl IntoLua for Value {
1530 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1531 Ok(self)
1532 }
1533}
1534
1535impl IntoLua for &Value {
1536 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1537 Ok(self.clone())
1538 }
1539}
1540
1541impl FromLua for Value {
1542 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1543 Ok(value)
1544 }
1545}
1546
1547impl IntoLua for bool {
1548 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1549 Ok(Value::Boolean(self))
1550 }
1551}
1552
1553impl FromLua for bool {
1554 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1555 match value {
1556 Value::Boolean(v) => Ok(v),
1557 other => Err(type_error_value(&other, "boolean")),
1558 }
1559 }
1560}
1561
1562impl IntoLua for i64 {
1563 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1564 Ok(Value::Integer(self))
1565 }
1566}
1567
1568impl FromLua for i64 {
1569 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1570 match value {
1571 Value::Integer(v) => Ok(v),
1572 Value::Number(v) if v.fract() == 0.0 && v.is_finite() => Ok(v as i64),
1573 other => Err(type_error_value(&other, "integer")),
1574 }
1575 }
1576}
1577
1578impl IntoLua for i32 {
1579 fn into_lua(self, lua: &Lua) -> Result<Value> {
1580 i64::from(self).into_lua(lua)
1581 }
1582}
1583
1584impl FromLua for i32 {
1585 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1586 let v = i64::from_lua(value, lua)?;
1587 i32::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
1588 }
1589}
1590
1591impl IntoLua for usize {
1592 fn into_lua(self, lua: &Lua) -> Result<Value> {
1593 let v = i64::try_from(self)
1594 .map_err(|_| LuaError::runtime(format_args!("integer out of range")))?;
1595 v.into_lua(lua)
1596 }
1597}
1598
1599impl FromLua for usize {
1600 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1601 let v = i64::from_lua(value, lua)?;
1602 usize::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
1603 }
1604}
1605
1606impl IntoLua for u64 {
1607 fn into_lua(self, lua: &Lua) -> Result<Value> {
1608 let v = i64::try_from(self)
1609 .map_err(|_| LuaError::runtime(format_args!("integer out of range")))?;
1610 v.into_lua(lua)
1611 }
1612}
1613
1614impl FromLua for u64 {
1615 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1616 let v = i64::from_lua(value, lua)?;
1617 u64::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
1618 }
1619}
1620
1621impl IntoLua for u32 {
1622 fn into_lua(self, lua: &Lua) -> Result<Value> {
1623 u64::from(self).into_lua(lua)
1624 }
1625}
1626
1627impl FromLua for u32 {
1628 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1629 let v = u64::from_lua(value, lua)?;
1630 u32::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
1631 }
1632}
1633
1634impl IntoLua for f64 {
1635 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1636 Ok(Value::Number(self))
1637 }
1638}
1639
1640impl FromLua for f64 {
1641 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1642 match value {
1643 Value::Integer(v) => Ok(v as f64),
1644 Value::Number(v) => Ok(v),
1645 other => Err(type_error_value(&other, "number")),
1646 }
1647 }
1648}
1649
1650impl IntoLua for &str {
1651 fn into_lua(self, lua: &Lua) -> Result<Value> {
1652 Ok(Value::String(lua.create_string(self.as_bytes())?))
1653 }
1654}
1655
1656impl IntoLua for String {
1657 fn into_lua(self, lua: &Lua) -> Result<Value> {
1658 Ok(Value::String(lua.create_string(self.into_bytes())?))
1659 }
1660}
1661
1662impl FromLua for String {
1663 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1664 match value {
1665 Value::String(s) => s.to_str(),
1666 other => Err(type_error_value(&other, "string")),
1667 }
1668 }
1669}
1670
1671impl IntoLua for &[u8] {
1672 fn into_lua(self, lua: &Lua) -> Result<Value> {
1673 Ok(Value::String(lua.create_string(self)?))
1674 }
1675}
1676
1677impl IntoLua for LuaString {
1678 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1679 Ok(Value::String(self))
1680 }
1681}
1682
1683impl IntoLua for &LuaString {
1684 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1685 Ok(Value::String(self.clone()))
1686 }
1687}
1688
1689impl FromLua for LuaString {
1690 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1691 match value {
1692 Value::String(v) => Ok(v),
1693 other => Err(type_error_value(&other, "string")),
1694 }
1695 }
1696}
1697
1698impl IntoLua for Table {
1699 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1700 Ok(Value::Table(self))
1701 }
1702}
1703
1704impl IntoLua for &Table {
1705 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1706 Ok(Value::Table(self.clone()))
1707 }
1708}
1709
1710impl FromLua for Table {
1711 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1712 match value {
1713 Value::Table(v) => Ok(v),
1714 other => Err(type_error_value(&other, "table")),
1715 }
1716 }
1717}
1718
1719impl IntoLua for Function {
1720 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1721 Ok(Value::Function(self))
1722 }
1723}
1724
1725impl IntoLua for &Function {
1726 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1727 Ok(Value::Function(self.clone()))
1728 }
1729}
1730
1731impl FromLua for Function {
1732 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1733 match value {
1734 Value::Function(v) => Ok(v),
1735 other => Err(type_error_value(&other, "function")),
1736 }
1737 }
1738}
1739
1740impl IntoLua for AnyUserData {
1741 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1742 Ok(Value::UserData(self))
1743 }
1744}
1745
1746impl IntoLua for &AnyUserData {
1747 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1748 Ok(Value::UserData(self.clone()))
1749 }
1750}
1751
1752impl FromLua for AnyUserData {
1753 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1754 match value {
1755 Value::UserData(v) => Ok(v),
1756 other => Err(type_error_value(&other, "userdata")),
1757 }
1758 }
1759}
1760
1761impl<T> IntoLua for T
1762where
1763 T: UserData,
1764{
1765 fn into_lua(self, lua: &Lua) -> Result<Value> {
1766 Ok(Value::UserData(lua.create_userdata(self)?))
1767 }
1768}
1769
1770impl<T> IntoLua for Option<T>
1771where
1772 T: IntoLua,
1773{
1774 fn into_lua(self, lua: &Lua) -> Result<Value> {
1775 match self {
1776 Some(value) => value.into_lua(lua),
1777 None => Ok(Value::Nil),
1778 }
1779 }
1780}
1781
1782impl<T> FromLua for Option<T>
1783where
1784 T: FromLua,
1785{
1786 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1787 match value {
1788 Value::Nil => Ok(None),
1789 other => T::from_lua(other, lua).map(Some),
1790 }
1791 }
1792}
1793
1794impl<T> IntoLua for Vec<T>
1795where
1796 T: IntoLua,
1797{
1798 fn into_lua(self, lua: &Lua) -> Result<Value> {
1799 let table = lua.create_table()?;
1800 for (idx, value) in self.into_iter().enumerate() {
1801 table.set((idx + 1) as i64, value)?;
1802 }
1803 Ok(Value::Table(table))
1804 }
1805}
1806
1807impl<T> FromLua for Vec<T>
1808where
1809 T: FromLua,
1810{
1811 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1812 let table = Table::from_lua(value, lua)?;
1813 let raw = table.raw_table()?;
1814 let len = raw.getn();
1815 let mut out = Vec::with_capacity(len as usize);
1816 for idx in 1..=len {
1817 let value = Value::from_raw(lua, raw.get_int(idx as i64))?;
1818 out.push(T::from_lua(value, lua)?);
1819 }
1820 Ok(out)
1821 }
1822}
1823
1824impl<K, V> IntoLua for HashMap<K, V>
1825where
1826 K: IntoLua,
1827 V: IntoLua,
1828{
1829 fn into_lua(self, lua: &Lua) -> Result<Value> {
1830 let table = lua.create_table()?;
1831 for (key, value) in self {
1832 table.set(key, value)?;
1833 }
1834 Ok(Value::Table(table))
1835 }
1836}
1837
1838impl<K, V> FromLua for HashMap<K, V>
1839where
1840 K: FromLua + Eq + Hash,
1841 V: FromLua,
1842{
1843 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1844 let table = Table::from_lua(value, lua)?;
1845 let raw = table.raw_table()?;
1846 let mut out = HashMap::new();
1847 let mut result = Ok(());
1848 raw.for_each_entry(|key, value| {
1849 if result.is_err() {
1850 return;
1851 }
1852 result = (|| {
1853 let key = Value::from_raw(lua, *key)?;
1854 let value = Value::from_raw(lua, *value)?;
1855 out.insert(K::from_lua(key, lua)?, V::from_lua(value, lua)?);
1856 Ok(())
1857 })();
1858 });
1859 result?;
1860 Ok(out)
1861 }
1862}
1863
1864impl<T> IntoLuaMulti for Variadic<T>
1865where
1866 T: IntoLua,
1867{
1868 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1869 self.into_iter().map(|value| value.into_lua(lua)).collect()
1870 }
1871}
1872
1873impl<T> FromLuaMulti for Variadic<T>
1874where
1875 T: FromLua,
1876{
1877 const NRESULTS: i32 = -1;
1878
1879 fn from_lua_multi(values: Vec<Value>, lua: &Lua) -> Result<Self> {
1880 values
1881 .into_iter()
1882 .map(|value| T::from_lua(value, lua))
1883 .collect()
1884 }
1885}
1886
1887impl IntoLuaMulti for () {
1888 fn into_lua_multi(self, _lua: &Lua) -> Result<Vec<Value>> {
1889 Ok(Vec::new())
1890 }
1891}
1892
1893impl<T> IntoLuaMulti for T
1894where
1895 T: IntoLua,
1896{
1897 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1898 Ok(vec![self.into_lua(lua)?])
1899 }
1900}
1901
1902impl<A, B> IntoLuaMulti for (A, B)
1903where
1904 A: IntoLua,
1905 B: IntoLua,
1906{
1907 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1908 Ok(vec![self.0.into_lua(lua)?, self.1.into_lua(lua)?])
1909 }
1910}
1911
1912impl<A, T> IntoLuaMulti for (A, Variadic<T>)
1913where
1914 A: IntoLua,
1915 T: IntoLua,
1916{
1917 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1918 let mut values = vec![self.0.into_lua(lua)?];
1919 values.extend(self.1.into_lua_multi(lua)?);
1920 Ok(values)
1921 }
1922}
1923
1924impl<A, B, C> IntoLuaMulti for (A, B, C)
1925where
1926 A: IntoLua,
1927 B: IntoLua,
1928 C: IntoLua,
1929{
1930 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1931 Ok(vec![
1932 self.0.into_lua(lua)?,
1933 self.1.into_lua(lua)?,
1934 self.2.into_lua(lua)?,
1935 ])
1936 }
1937}
1938
1939impl<A, B, T> IntoLuaMulti for (A, B, Variadic<T>)
1940where
1941 A: IntoLua,
1942 B: IntoLua,
1943 T: IntoLua,
1944{
1945 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1946 let mut values = vec![self.0.into_lua(lua)?, self.1.into_lua(lua)?];
1947 values.extend(self.2.into_lua_multi(lua)?);
1948 Ok(values)
1949 }
1950}
1951
1952impl FromLuaMulti for () {
1953 const NRESULTS: i32 = 0;
1954
1955 fn from_lua_multi(_values: Vec<Value>, _lua: &Lua) -> Result<Self> {
1956 Ok(())
1957 }
1958}
1959
1960impl<T> FromLuaMulti for T
1961where
1962 T: FromLua,
1963{
1964 const NRESULTS: i32 = 1;
1965
1966 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
1967 let value = if values.is_empty() {
1968 Value::Nil
1969 } else {
1970 values.remove(0)
1971 };
1972 T::from_lua(value, lua)
1973 }
1974}
1975
1976impl<A, B> FromLuaMulti for (A, B)
1977where
1978 A: FromLua,
1979 B: FromLua,
1980{
1981 const NRESULTS: i32 = 2;
1982
1983 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
1984 let first = if values.is_empty() {
1985 Value::Nil
1986 } else {
1987 values.remove(0)
1988 };
1989 let second = if values.is_empty() {
1990 Value::Nil
1991 } else {
1992 values.remove(0)
1993 };
1994 Ok((A::from_lua(first, lua)?, B::from_lua(second, lua)?))
1995 }
1996}
1997
1998impl<A, T> FromLuaMulti for (A, Variadic<T>)
1999where
2000 A: FromLua,
2001 T: FromLua,
2002{
2003 const NRESULTS: i32 = -1;
2004
2005 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
2006 let first = if values.is_empty() {
2007 Value::Nil
2008 } else {
2009 values.remove(0)
2010 };
2011 Ok((
2012 A::from_lua(first, lua)?,
2013 Variadic::from_lua_multi(values, lua)?,
2014 ))
2015 }
2016}
2017
2018impl<A, B, C> FromLuaMulti for (A, B, C)
2019where
2020 A: FromLua,
2021 B: FromLua,
2022 C: FromLua,
2023{
2024 const NRESULTS: i32 = 3;
2025
2026 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
2027 let first = if values.is_empty() {
2028 Value::Nil
2029 } else {
2030 values.remove(0)
2031 };
2032 let second = if values.is_empty() {
2033 Value::Nil
2034 } else {
2035 values.remove(0)
2036 };
2037 let third = if values.is_empty() {
2038 Value::Nil
2039 } else {
2040 values.remove(0)
2041 };
2042 Ok((
2043 A::from_lua(first, lua)?,
2044 B::from_lua(second, lua)?,
2045 C::from_lua(third, lua)?,
2046 ))
2047 }
2048}
2049
2050impl<A, B, T> FromLuaMulti for (A, B, Variadic<T>)
2051where
2052 A: FromLua,
2053 B: FromLua,
2054 T: FromLua,
2055{
2056 const NRESULTS: i32 = -1;
2057
2058 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
2059 let first = if values.is_empty() {
2060 Value::Nil
2061 } else {
2062 values.remove(0)
2063 };
2064 let second = if values.is_empty() {
2065 Value::Nil
2066 } else {
2067 values.remove(0)
2068 };
2069 Ok((
2070 A::from_lua(first, lua)?,
2071 B::from_lua(second, lua)?,
2072 Variadic::from_lua_multi(values, lua)?,
2073 ))
2074 }
2075}
2076
2077fn rust_callback_trampoline(state: &mut LuaState) -> Result<usize> {
2078 let func_idx = state.current_call_info().func;
2079 let callback = match state.get_at(func_idx) {
2080 RawLuaValue::Function(RawLuaClosure::C(closure)) => {
2081 let Some(RawLuaValue::UserData(userdata)) = closure.upvalues.first() else {
2082 return Err(LuaError::runtime(format_args!(
2083 "missing Rust callback payload"
2084 )));
2085 };
2086 let host = userdata
2087 .host_value()
2088 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust callback payload")))?;
2089 host.downcast::<RustCallbackCell>().map_err(|_| {
2090 LuaError::runtime(format_args!("Rust callback payload type mismatch"))
2091 })?
2092 }
2093 _ => {
2094 return Err(LuaError::runtime(format_args!(
2095 "Rust callback trampoline called without C closure"
2096 )));
2097 }
2098 };
2099 (callback.function)(state)
2100}
2101
2102fn with_heap_guard<R>(state: &LuaState, f: impl FnOnce() -> R) -> R {
2103 let _heap_guard = heap_guard(state);
2104 f()
2105}
2106
2107fn heap_guard(state: &LuaState) -> lua_gc::HeapGuard {
2108 let global = state.global();
2109 lua_gc::HeapGuard::push(&global.heap)
2110}
2111
2112fn callback_args(state: &mut LuaState, lua: &Lua) -> Result<Vec<Value>> {
2113 let func_idx = state.current_call_info().func;
2114 let nargs = state.top_idx().0.saturating_sub(func_idx.0 + 1);
2115 let mut args = Vec::with_capacity(nargs as usize);
2116 for i in 0..nargs {
2117 let raw = state.get_at(func_idx + 1 + i as i32);
2118 args.push(Value::from_raw_in_state(lua, state, raw)?);
2119 }
2120 Ok(args)
2121}
2122
2123fn callback_userdata_args(state: &mut LuaState, lua: &Lua) -> Result<(AnyUserData, Vec<Value>)> {
2124 let mut args = callback_args(state, lua)?;
2125 if args.is_empty() {
2126 return Err(LuaError::runtime(format_args!(
2127 "userdata method missing self argument"
2128 )));
2129 }
2130 let userdata = AnyUserData::from_lua(args.remove(0), lua)?;
2131 Ok((userdata, args))
2132}
2133
2134fn push_callback_returns(state: &mut LuaState, lua: &Lua, returns: Vec<Value>) -> Result<usize> {
2135 let mut count = 0usize;
2136 for value in returns {
2137 let raw = value.to_raw_for_lua(lua, state)?;
2138 state.push(raw);
2139 count += 1;
2140 }
2141 Ok(count)
2142}
2143
2144fn stale_handle_error() -> LuaError {
2145 LuaError::runtime(format_args!("stale Lua handle"))
2146}
2147
2148fn type_error_raw(value: &RawLuaValue, expected: &str) -> LuaError {
2149 LuaError::runtime(format_args!(
2150 "{} expected, got {}",
2151 expected,
2152 value.type_name()
2153 ))
2154}
2155
2156fn type_error_value(value: &Value, expected: &str) -> LuaError {
2157 let got = match value {
2158 Value::Nil => "nil",
2159 Value::Boolean(_) => "boolean",
2160 Value::Integer(_) | Value::Number(_) => "number",
2161 Value::String(_) => "string",
2162 Value::Table(_) => "table",
2163 Value::Function(_) => "function",
2164 Value::UserData(_) | Value::LightUserData(_) => "userdata",
2165 Value::Thread(_) => "thread",
2166 };
2167 LuaError::runtime(format_args!("{} expected, got {}", expected, got))
2168}
2169
2170pub struct LuaRuntime {
2172 state: LuaState,
2173}
2174
2175impl LuaRuntime {
2176 pub fn new() -> Result<Self> {
2182 Self::with_hooks(HostHooks::default())
2183 }
2184
2185 pub fn with_hooks(hooks: HostHooks) -> Result<Self> {
2187 let mut state = new_state().ok_or(LuaError::Memory)?;
2188 install_parser_hook(&mut state);
2189 hooks.install(&mut state);
2190 open_libs(&mut state)?;
2191 Ok(Self { state })
2192 }
2193
2194 pub fn state(&self) -> &LuaState {
2195 &self.state
2196 }
2197
2198 pub fn state_mut(&mut self) -> &mut LuaState {
2199 &mut self.state
2200 }
2201
2202 pub fn into_state(self) -> LuaState {
2203 self.state
2204 }
2205
2206 pub fn into_lua(self) -> Lua {
2207 Lua::from_initialized_state(self.state)
2208 }
2209
2210 pub fn exec(&mut self, source: &[u8], name: &[u8]) -> Result<()> {
2212 exec_state(&mut self.state, source, name)
2213 }
2214}
2215
2216fn exec_state(state: &mut LuaState, source: &[u8], name: &[u8]) -> Result<()> {
2217 let status = load_buffer(state, source, name)?;
2218 if status != 0 {
2219 let err = state.pop();
2220 return Err(LuaError::from_value(err));
2221 }
2222 lua_vm::api::pcall_k(state, 0, 0, 0, 0, None)?;
2223 Ok(())
2224}
2225
2226pub fn install_parser_hook(state: &mut LuaState) {
2227 state.global_mut().parser_hook = Some(parser_hook);
2228}
2229
2230fn parser_hook(
2231 state: &mut LuaState,
2232 source: &[u8],
2233 name: &[u8],
2234 firstchar: i32,
2235) -> Result<GcRef<LuaLClosure>> {
2236 let _heap_guard = heap_guard(state);
2237 let proto = lua_parse::parse(
2238 state,
2239 lua_parse::DynData::default(),
2240 source,
2241 name,
2242 firstchar,
2243 )?;
2244 let nupvals = proto.upvalues.len();
2245 let mut upvals = Vec::with_capacity(nupvals);
2246 for _ in 0..nupvals {
2247 upvals.push(std::cell::Cell::new(GcRef::new(UpVal::closed(
2248 RawLuaValue::Nil,
2249 ))));
2250 }
2251 Ok(GcRef::new(LuaLClosure {
2252 proto: GcRef::new(*proto),
2253 upvals,
2254 }))
2255}
2256
2257#[cfg(test)]
2258mod tests {
2259 use super::*;
2260 use std::cell::Cell;
2261
2262 fn external_root_count(lua: &Lua) -> usize {
2263 lua.with_state(|state| state.global().external_roots.len())
2264 }
2265
2266 struct Counter {
2267 value: i64,
2268 }
2269
2270 impl UserData for Counter {
2271 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
2272 methods.add_method("get", |_lua, this, ()| Ok(this.value));
2273 methods.add_method_mut("inc", |_lua, this, delta: i64| {
2274 this.value += delta;
2275 Ok(this.value)
2276 });
2277 }
2278 }
2279
2280 struct PropertyBag {
2281 value: i64,
2282 }
2283
2284 impl UserData for PropertyBag {
2285 fn add_meta_methods<M: UserDataMethods<Self>>(methods: &mut M) {
2286 methods.add_meta_method(MetaMethod::Index, |_lua, this, key: String| {
2287 if key == "value" {
2288 Ok(Value::Integer(this.value))
2289 } else {
2290 Ok(Value::Nil)
2291 }
2292 });
2293 methods.add_meta_method_mut(
2294 MetaMethod::NewIndex,
2295 |_lua, this, (key, value): (String, i64)| {
2296 if key != "value" {
2297 return Err(LuaError::runtime(format_args!("unknown property")));
2298 }
2299 this.value = value;
2300 Ok(())
2301 },
2302 );
2303 }
2304 }
2305
2306 #[test]
2307 fn rooted_table_clone_and_drop_manage_root_slots() {
2308 let lua = Lua::new();
2309 assert_eq!(external_root_count(&lua), 0);
2310
2311 let table = lua.create_table().expect("table should allocate");
2312 assert_eq!(external_root_count(&lua), 1);
2313
2314 let cloned = table.clone();
2315 assert_eq!(external_root_count(&lua), 2);
2316
2317 drop(table);
2318 assert_eq!(external_root_count(&lua), 1);
2319
2320 cloned.set("answer", 42_i64).expect("set should succeed");
2321 lua.gc_collect();
2322 assert_eq!(
2323 cloned.get::<_, i64>("answer").expect("get should succeed"),
2324 42
2325 );
2326
2327 drop(cloned);
2328 assert_eq!(external_root_count(&lua), 0);
2329 }
2330
2331 #[test]
2332 fn table_values_survive_forced_collection_between_operations() {
2333 let lua = Lua::new();
2334 let table = lua.create_table().expect("table should allocate");
2335
2336 lua.gc_collect();
2337 table.set("k", "v").expect("set should succeed");
2338 table.set(1_i64, "array").expect("array set should succeed");
2339 lua.gc_collect();
2340
2341 let value: String = table.get("k").expect("get should succeed");
2342 assert_eq!(value, "v");
2343 assert_eq!(table.len().expect("len should succeed"), 1);
2344 }
2345
2346 #[test]
2347 fn chunk_exec_eval_and_function_call_use_rooted_handles() {
2348 let lua = Lua::new();
2349 lua.load("function add(a, b) return a + b end")
2350 .set_name("test")
2351 .exec()
2352 .expect("chunk should execute");
2353
2354 let globals = lua.globals();
2355 let add: Function = globals.get("add").expect("function should exist");
2356 let result: i64 = add.call((20_i64, 22_i64)).expect("call should work");
2357 assert_eq!(result, 42);
2358
2359 let eval_result: i64 = lua
2360 .load("return add(1, 2)")
2361 .eval()
2362 .expect("eval should work");
2363 assert_eq!(eval_result, 3);
2364 }
2365
2366 #[test]
2367 fn rust_callback_captures_state_and_reenters_lua() {
2368 let lua = Lua::new();
2369 lua.load("function twice(v) return v * 2 end")
2370 .exec()
2371 .expect("chunk should execute");
2372
2373 let globals = lua.globals();
2374 let twice: Function = globals.get("twice").expect("function should exist");
2375 let calls = Rc::new(Cell::new(0));
2376 let calls_for_callback = calls.clone();
2377
2378 let callback = lua
2379 .create_function(move |_lua, value: i64| {
2380 calls_for_callback.set(calls_for_callback.get() + 1);
2381 let doubled: i64 = twice.call(value)?;
2382 Ok(doubled + 1)
2383 })
2384 .expect("callback should create");
2385 globals
2386 .set("from_rust", callback)
2387 .expect("callback should register");
2388
2389 let result: i64 = lua
2390 .load("return from_rust(20)")
2391 .eval()
2392 .expect("callback should run");
2393 assert_eq!(result, 41);
2394 assert_eq!(calls.get(), 1);
2395 }
2396
2397 #[test]
2398 fn rust_callback_accepts_and_returns_collectable_values() {
2399 let lua = Lua::new();
2400 let globals = lua.globals();
2401 let callback = lua
2402 .create_function(|lua, name: String| {
2403 let table = lua.create_table()?;
2404 table.set("name", name)?;
2405 Ok(table)
2406 })
2407 .expect("callback should create");
2408 globals
2409 .set("make_record", callback)
2410 .expect("callback should register");
2411
2412 let result: String = lua
2413 .load("return make_record('lua-rs').name")
2414 .eval()
2415 .expect("callback should return table");
2416 assert_eq!(result, "lua-rs");
2417 }
2418
2419 #[test]
2420 fn rust_callback_mut_tracks_state() {
2421 let lua = Lua::new();
2422 let globals = lua.globals();
2423 let mut next = 0_i64;
2424 let callback = lua
2425 .create_function_mut(move |_lua, delta: i64| {
2426 next += delta;
2427 Ok(next)
2428 })
2429 .expect("callback should create");
2430 globals
2431 .set("next", callback)
2432 .expect("callback should register");
2433
2434 let result: (i64, i64) = lua
2435 .load("return next(2), next(5)")
2436 .eval()
2437 .expect("callback should run");
2438 assert_eq!(result, (2, 7));
2439 }
2440
2441 #[test]
2442 fn dropped_rust_callback_releases_captured_handles_after_gc() {
2443 let lua = Lua::new();
2444 let table = lua.create_table().expect("table should allocate");
2445 table.set("value", 42_i64).expect("set should succeed");
2446 assert_eq!(external_root_count(&lua), 1);
2447
2448 let callback = {
2449 let captured = table.clone();
2450 lua.create_function(move |_lua, ()| captured.get::<_, i64>("value"))
2451 .expect("callback should create")
2452 };
2453 assert_eq!(external_root_count(&lua), 3);
2454
2455 drop(callback);
2456 lua.gc_collect();
2457 assert_eq!(external_root_count(&lua), 1);
2458 assert_eq!(table.get::<_, i64>("value").expect("table should live"), 42);
2459 }
2460
2461 #[test]
2462 fn metatable_is_built_once_per_type() {
2463 use std::sync::atomic::{AtomicUsize, Ordering};
2464 static BUILDS: AtomicUsize = AtomicUsize::new(0);
2465
2466 struct Widget {
2467 n: i64,
2468 }
2469 impl UserData for Widget {
2470 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
2471 BUILDS.fetch_add(1, Ordering::SeqCst);
2472 methods.add_method("n", |_lua, this, ()| Ok(this.n));
2473 }
2474 }
2475
2476 let lua = Lua::new();
2477 let a = lua.create_userdata(Widget { n: 1 }).expect("first");
2478 let b = lua.create_userdata(Widget { n: 2 }).expect("second");
2479 let c = lua.create_userdata(Widget { n: 3 }).expect("third");
2480
2481 assert_eq!(BUILDS.load(Ordering::SeqCst), 1);
2483
2484 let globals = lua.globals();
2486 globals.set("a", &a).unwrap();
2487 globals.set("b", &b).unwrap();
2488 globals.set("c", &c).unwrap();
2489 let sum: i64 = lua.load("return a:n() + b:n() + c:n()").eval().unwrap();
2490 assert_eq!(sum, 6);
2491 }
2492
2493 #[test]
2505 fn lua_state_frees_after_userdata_with_methods_is_dropped() {
2506 use std::rc::Rc;
2507
2508 let weak_inner = {
2509 let lua = Lua::new();
2510 let weak = Rc::downgrade(&lua.inner);
2511 let _ = lua
2515 .create_userdata(Counter { value: 1 })
2516 .expect("userdata should create");
2517 weak
2518 };
2519
2520 assert!(
2521 weak_inner.upgrade().is_none(),
2522 "LuaInner is still alive after every external Lua handle dropped: \
2523 internal callback closures hold a strong Rc<LuaInner>, leaking the state"
2524 );
2525 }
2526
2527 #[test]
2531 fn lua_state_frees_after_create_function_handle_drops() {
2532 use std::rc::Rc;
2533
2534 let weak_inner = {
2535 let lua = Lua::new();
2536 let weak = Rc::downgrade(&lua.inner);
2537 let _f = lua
2538 .create_function(|_, ()| Ok(()))
2539 .expect("create_function should succeed");
2540 weak
2541 };
2542
2543 assert!(
2544 weak_inner.upgrade().is_none(),
2545 "LuaInner is still alive after the only Lua handle dropped: \
2546 the create_function callback held a strong Rc<LuaInner>"
2547 );
2548 }
2549
2550 #[test]
2557 fn lua_state_frees_after_userdata_with_fields_drops() {
2558 use std::rc::Rc;
2559
2560 struct Point {
2561 x: f64,
2562 }
2563 impl UserData for Point {
2564 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
2565 m.add_field_method_get("x", |_, this| Ok(this.x));
2566 m.add_field_method_set("x", |_, this, v: f64| {
2567 this.x = v;
2568 Ok(())
2569 });
2570 }
2571 }
2572
2573 let weak_inner = {
2574 let lua = Lua::new();
2575 let weak = Rc::downgrade(&lua.inner);
2576 let _ = lua
2577 .create_userdata(Point { x: 1.0 })
2578 .expect("userdata should create");
2579 weak
2580 };
2581
2582 assert!(
2583 weak_inner.upgrade().is_none(),
2584 "LuaInner leaked via the composed __index/__newindex closures: \
2585 they capture Table/Function values whose RootedValue holds a \
2586 strong Rc<LuaInner>"
2587 );
2588 }
2589
2590 #[test]
2596 fn lua_state_frees_with_fields_methods_and_raw_meta() {
2597 use std::rc::Rc;
2598
2599 struct Mixed {
2600 x: f64,
2601 log: Vec<String>,
2602 }
2603 impl UserData for Mixed {
2604 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
2605 m.add_field_method_get("x", |_, this| Ok(this.x));
2606 m.add_field_method_set("x", |_, this, v: f64| {
2607 this.x = v;
2608 Ok(())
2609 });
2610 m.add_method("log_len", |_, this, ()| Ok(this.log.len() as i64));
2611 m.add_method_mut("push_log", |_, this, s: String| {
2612 this.log.push(s);
2613 Ok(())
2614 });
2615 m.add_meta_method(MetaMethod::Index, |_, _this, key: String| {
2616 Ok(::std::format!("dynamic:{key}"))
2617 });
2618 m.add_meta_method_mut(
2619 MetaMethod::NewIndex,
2620 |_, _this, (_k, _v): (String, Value)| Ok(()),
2621 );
2622 }
2623 }
2624
2625 let weak_inner = {
2626 let lua = Lua::new();
2627 let weak = Rc::downgrade(&lua.inner);
2628 let _ = lua
2629 .create_userdata(Mixed {
2630 x: 1.0,
2631 log: Vec::new(),
2632 })
2633 .expect("create");
2634 weak
2635 };
2636
2637 assert!(
2638 weak_inner.upgrade().is_none(),
2639 "maximal-composition userdata leaked LuaInner: \
2640 check the composed __index / __newindex captures"
2641 );
2642 }
2643
2644 #[test]
2650 fn composed_dispatch_does_not_accumulate_external_roots() {
2651 struct Probe {
2652 x: i64,
2653 }
2654 impl UserData for Probe {
2655 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
2656 m.add_field_method_get("x", |_, this| Ok(this.x));
2657 }
2658 }
2659
2660 let lua = Lua::new();
2661 lua.globals()
2662 .set("v", lua.create_userdata(Probe { x: 1 }).unwrap())
2663 .unwrap();
2664 let baseline = external_root_count(&lua);
2665
2666 for _ in 0..1000 {
2667 let _: i64 = lua.load("return v.x").eval().unwrap();
2668 }
2669 let after = external_root_count(&lua);
2672
2673 assert!(
2674 after <= baseline + 2,
2675 "external roots grew under composed __index churn: baseline={baseline} after={after}"
2676 );
2677 }
2678
2679 #[test]
2685 fn userdata_method_can_reenter_lua_from_callback() {
2686 struct Calc;
2687 impl UserData for Calc {
2688 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
2689 m.add_method("apply", |_lua, _this, f: Function| {
2690 let r: i64 = f.call(7_i64)?;
2691 Ok(r + 1)
2692 });
2693 }
2694 }
2695
2696 let lua = Lua::new();
2697 lua.globals()
2698 .set("c", lua.create_userdata(Calc).unwrap())
2699 .unwrap();
2700 let r: i64 = lua
2701 .load("return c:apply(function(n) return n * 2 end)")
2702 .eval()
2703 .unwrap();
2704 assert_eq!(r, 15);
2705 }
2706
2707 #[test]
2712 fn metatable_cache_is_per_lua_state() {
2713 use std::sync::atomic::{AtomicUsize, Ordering};
2714 static BUILDS: AtomicUsize = AtomicUsize::new(0);
2715
2716 struct Marker {
2717 v: i64,
2718 }
2719 impl UserData for Marker {
2720 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
2721 BUILDS.fetch_add(1, Ordering::SeqCst);
2722 m.add_method("v", |_, this, ()| Ok(this.v));
2723 }
2724 }
2725
2726 let start = BUILDS.load(Ordering::SeqCst);
2727
2728 let lua_a = Lua::new();
2729 let _a1 = lua_a.create_userdata(Marker { v: 1 }).unwrap();
2730 assert_eq!(BUILDS.load(Ordering::SeqCst) - start, 1, "state A first build");
2731 let _a2 = lua_a.create_userdata(Marker { v: 2 }).unwrap();
2732 assert_eq!(BUILDS.load(Ordering::SeqCst) - start, 1, "state A reuses cache");
2733
2734 let lua_b = Lua::new();
2735 let _b1 = lua_b.create_userdata(Marker { v: 3 }).unwrap();
2736 assert_eq!(BUILDS.load(Ordering::SeqCst) - start, 2, "state B is independent");
2737
2738 let _a3 = lua_a.create_userdata(Marker { v: 4 }).unwrap();
2739 assert_eq!(BUILDS.load(Ordering::SeqCst) - start, 2, "state A still cached");
2740 }
2741
2742 #[test]
2746 fn field_shadows_method_of_same_name() {
2747 struct Shadow {
2748 x: i64,
2749 }
2750 impl UserData for Shadow {
2751 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
2752 m.add_field_method_get("x", |_, this| Ok(this.x));
2753 m.add_method("x", |_, _this, ()| Ok(999_i64));
2754 }
2755 }
2756
2757 let lua = Lua::new();
2758 lua.globals()
2759 .set("v", lua.create_userdata(Shadow { x: 42 }).unwrap())
2760 .unwrap();
2761
2762 let r: i64 = lua.load("return v.x").eval().unwrap();
2763 assert_eq!(r, 42, "the field getter should beat the method of the same name");
2764 }
2765
2766 #[test]
2770 fn cached_metatable_is_shared_across_values_in_lua() {
2771 struct Twin;
2772 impl UserData for Twin {
2773 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
2774 m.add_method("ping", |_, _this, ()| Ok(1_i64));
2775 }
2776 }
2777
2778 let lua = Lua::new();
2779 lua.globals()
2780 .set("a", lua.create_userdata(Twin).unwrap())
2781 .unwrap();
2782 lua.globals()
2783 .set("b", lua.create_userdata(Twin).unwrap())
2784 .unwrap();
2785
2786 let same: bool = lua
2787 .load("return getmetatable(a) == getmetatable(b)")
2788 .eval()
2789 .unwrap();
2790 assert!(same, "cached metatable must be shared across values of the same type");
2791 }
2792
2793 #[test]
2794 fn fields_and_methods_coexist() {
2795 struct Vec2 {
2796 x: f64,
2797 y: f64,
2798 }
2799 impl UserData for Vec2 {
2800 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
2801 m.add_field_method_get("x", |_, this| Ok(this.x));
2802 m.add_field_method_get("y", |_, this| Ok(this.y));
2803 m.add_field_method_set("x", |_, this, v: f64| {
2804 this.x = v;
2805 Ok(())
2806 });
2807 m.add_field_method_set("y", |_, this, v: f64| {
2808 this.y = v;
2809 Ok(())
2810 });
2811 m.add_method("length", |_, this, ()| {
2812 Ok((this.x * this.x + this.y * this.y).sqrt())
2813 });
2814 m.add_method_mut("scale", |_, this, k: f64| {
2815 this.x *= k;
2816 this.y *= k;
2817 Ok(())
2818 });
2819 }
2820 }
2821
2822 let lua = Lua::new();
2823 let v = lua.create_userdata(Vec2 { x: 3.0, y: 4.0 }).unwrap();
2824 lua.globals().set("v", &v).unwrap();
2825
2826 assert_eq!(lua.load("return v:length()").eval::<f64>().unwrap(), 5.0);
2828 assert_eq!(lua.load("return v.x + v.y").eval::<f64>().unwrap(), 7.0);
2829
2830 lua.load("v.x = 6").exec().unwrap();
2832 assert_eq!(lua.load("return v.x").eval::<f64>().unwrap(), 6.0);
2833
2834 lua.load("v:scale(2)").exec().unwrap();
2836 assert_eq!(lua.load("return v.x").eval::<f64>().unwrap(), 12.0);
2837 assert_eq!(lua.load("return v.y").eval::<f64>().unwrap(), 8.0);
2838
2839 assert!(lua.load("v.z = 1").exec().is_err());
2841 }
2842
2843 #[test]
2844 fn userdata_methods_dispatch_and_track_borrows() {
2845 let lua = Lua::new();
2846 let globals = lua.globals();
2847 let counter = lua
2848 .create_userdata(Counter { value: 1 })
2849 .expect("userdata should create");
2850 globals
2851 .set("counter", &counter)
2852 .expect("userdata should register");
2853
2854 let result: i64 = lua
2855 .load("counter:inc(5); return counter:get()")
2856 .eval()
2857 .expect("methods should dispatch");
2858 assert_eq!(result, 6);
2859 assert_eq!(
2860 counter
2861 .with_borrow::<Counter, _>(|counter| counter.value)
2862 .expect("borrow should work"),
2863 6
2864 );
2865
2866 {
2867 let borrowed = counter
2868 .borrow::<Counter>()
2869 .expect("borrow guard should work");
2870 assert_eq!(borrowed.value, 6);
2871 }
2872
2873 {
2874 let mut borrowed = counter
2875 .borrow_mut::<Counter>()
2876 .expect("mutable borrow guard should work");
2877 borrowed.value = 9;
2878 }
2879
2880 assert_eq!(
2881 lua.load("return counter:get()")
2882 .eval::<i64>()
2883 .expect("method should see guard mutation"),
2884 9
2885 );
2886 }
2887
2888 #[test]
2889 fn userdata_payload_survives_gc_while_lua_holds_userdata() {
2890 let lua = Lua::new();
2891 let globals = lua.globals();
2892 let counter = lua
2893 .create_userdata(Counter { value: 10 })
2894 .expect("userdata should create");
2895 globals
2896 .set("counter", counter)
2897 .expect("userdata should register");
2898
2899 lua.gc_collect();
2900 let result: i64 = lua
2901 .load("counter:inc(2); collectgarbage('collect'); return counter:get()")
2902 .eval()
2903 .expect("userdata should survive collection");
2904 assert_eq!(result, 12);
2905 }
2906
2907 #[test]
2908 fn userdata_runtime_borrow_conflict_returns_lua_error() {
2909 let lua = Lua::new();
2910 let globals = lua.globals();
2911 let counter = lua
2912 .create_userdata(Counter { value: 1 })
2913 .expect("userdata should create");
2914 globals
2915 .set("counter", &counter)
2916 .expect("userdata should register");
2917
2918 let failed = counter
2919 .with_borrow::<Counter, _>(|_| lua.load("return counter:inc(1)").eval::<i64>().is_err())
2920 .expect("outer borrow should succeed");
2921 assert!(
2922 failed,
2923 "mutable method should fail while immutable borrow is held"
2924 );
2925 assert_eq!(
2926 counter
2927 .with_borrow::<Counter, _>(|counter| counter.value)
2928 .expect("borrow should work"),
2929 1
2930 );
2931 }
2932
2933 #[test]
2934 fn userdata_index_and_newindex_metamethods_dispatch() {
2935 let lua = Lua::new();
2936 let globals = lua.globals();
2937 let bag = lua
2938 .create_userdata(PropertyBag { value: 7 })
2939 .expect("userdata should create");
2940 globals.set("bag", &bag).expect("userdata should register");
2941
2942 let result: i64 = lua
2943 .load("bag.value = 42; return bag.value")
2944 .eval()
2945 .expect("metamethods should dispatch");
2946 assert_eq!(result, 42);
2947 assert_eq!(
2948 bag.with_borrow::<PropertyBag, _>(|bag| bag.value)
2949 .expect("borrow should work"),
2950 42
2951 );
2952 }
2953
2954 #[test]
2955 fn userdata_values_convert_directly_with_into_lua() {
2956 let lua = Lua::new();
2957 let globals = lua.globals();
2958 globals
2959 .set("counter", Counter { value: 3 })
2960 .expect("userdata should convert through IntoLua");
2961
2962 let result: i64 = lua
2963 .load("counter:inc(4); return counter:get()")
2964 .eval()
2965 .expect("converted userdata should dispatch methods");
2966 assert_eq!(result, 7);
2967 }
2968
2969 #[test]
2970 fn variadic_args_and_returns_convert_all_values() {
2971 let lua = Lua::new();
2972 let globals = lua.globals();
2973
2974 let sum = lua
2975 .create_function(|_lua, values: Variadic<i64>| Ok(values.iter().sum::<i64>()))
2976 .expect("variadic callback should create");
2977 globals.set("sum", sum).expect("callback should register");
2978 let result: i64 = lua
2979 .load("return sum(3, 2, 5)")
2980 .eval()
2981 .expect("variadic callback should run");
2982 assert_eq!(result, 10);
2983
2984 let echo = lua
2985 .create_function(|_lua, values: Variadic<Value>| Ok(values))
2986 .expect("variadic return callback should create");
2987 globals.set("echo", echo).expect("callback should register");
2988 let result: (i64, i64, i64) = lua
2989 .load("return echo(1, 2, 3)")
2990 .eval()
2991 .expect("variadic returns should stay separate");
2992 assert_eq!(result, (1, 2, 3));
2993
2994 let values: Variadic<i64> = lua
2995 .load("return 4, 5, 6")
2996 .eval()
2997 .expect("variadic eval should collect all returns");
2998 assert_eq!(values.into_vec(), vec![4, 5, 6]);
2999 }
3000
3001 #[test]
3002 fn vectors_maps_and_triple_returns_convert_through_tables() {
3003 let lua = Lua::new();
3004 let globals = lua.globals();
3005
3006 globals
3007 .set("list", vec![1_i64, 2, 3])
3008 .expect("vector should convert to table");
3009 let second: i64 = lua
3010 .load("return list[2]")
3011 .eval()
3012 .expect("table should be readable from Lua");
3013 assert_eq!(second, 2);
3014
3015 let list: Vec<i64> = lua
3016 .load("return {4, 5, 6}")
3017 .eval()
3018 .expect("table should convert to vector");
3019 assert_eq!(list, vec![4, 5, 6]);
3020
3021 let mut map = HashMap::new();
3022 map.insert("left".to_string(), 10_i64);
3023 map.insert("right".to_string(), 20_i64);
3024 globals
3025 .set("map", map)
3026 .expect("map should convert to table");
3027 let sum: i64 = lua
3028 .load("return map.left + map.right")
3029 .eval()
3030 .expect("map table should be readable from Lua");
3031 assert_eq!(sum, 30);
3032
3033 let map: HashMap<String, i64> = lua
3034 .load("return {alpha = 3, beta = 9}")
3035 .eval()
3036 .expect("table should convert to map");
3037 assert_eq!(map.get("alpha"), Some(&3));
3038 assert_eq!(map.get("beta"), Some(&9));
3039
3040 let triple: (i64, i64, i64) = lua
3041 .load("return 1, 2, 3")
3042 .eval()
3043 .expect("triple returns should convert");
3044 assert_eq!(triple, (1, 2, 3));
3045 }
3046}