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::ptr::NonNull;
68use std::rc::Rc;
69
70use lua_stdlib::auxlib::load_buffer;
71use lua_stdlib::init::open_libs;
72use lua_types::closure::{LuaCClosure as RawLuaCClosure, LuaClosure as RawLuaClosure, LuaLClosure};
73use lua_types::gc::GcRef;
74use lua_types::string::LuaString as RawLuaString;
75use lua_types::upval::UpVal;
76use lua_types::userdata::LuaUserData as RawLuaUserData;
77use lua_types::value::{LuaTable as RawLuaTable, LuaValue as RawLuaValue};
78use lua_vm::state::{
79 new_state, CpuClockHook, DynLibLoadHook, DynLibSymbolHook, DynLibUnloadHook, EntropyHook,
80 EnvHook, ExternalRootKey, FileLoaderHook, FileOpenHook, FileRemoveHook, FileRenameHook,
81 InputHook, LuaCallable, LuaRustFunction, LuaState, OsExecuteHook, OutputHook, PopenHook,
82 TempNameHook, UnixTimeHook,
83};
84
85pub use lua_types::{LuaError, LuaFileHandle};
86pub use lua_vm::state::{DynLibId, DynamicSymbol, OsExecuteReason, OsExecuteResult};
87
88#[cfg(feature = "derive")]
89pub use lua_rs_derive::{lua_methods, LuaUserData};
90
91pub type Error = LuaError;
92pub type Result<T> = std::result::Result<T, Error>;
93
94#[derive(Clone, Copy, Default)]
102pub struct HostHooks {
103 pub file_loader_hook: Option<FileLoaderHook>,
104 pub file_open_hook: Option<FileOpenHook>,
105 pub stdin_hook: Option<InputHook>,
106 pub stdout_hook: Option<OutputHook>,
107 pub stderr_hook: Option<OutputHook>,
108 pub env_hook: Option<EnvHook>,
109 pub unix_time_hook: Option<UnixTimeHook>,
110 pub cpu_clock_hook: Option<CpuClockHook>,
111 pub entropy_hook: Option<EntropyHook>,
112 pub temp_name_hook: Option<TempNameHook>,
113 pub popen_hook: Option<PopenHook>,
114 pub file_remove_hook: Option<FileRemoveHook>,
115 pub file_rename_hook: Option<FileRenameHook>,
116 pub os_execute_hook: Option<OsExecuteHook>,
117 pub dynlib_load_hook: Option<DynLibLoadHook>,
118 pub dynlib_symbol_hook: Option<DynLibSymbolHook>,
119 pub dynlib_unload_hook: Option<DynLibUnloadHook>,
120}
121
122impl HostHooks {
123 pub fn new() -> Self {
124 Self::default()
125 }
126
127 pub fn file_loader(mut self, hook: FileLoaderHook) -> Self {
128 self.file_loader_hook = Some(hook);
129 self
130 }
131
132 pub fn file_open(mut self, hook: FileOpenHook) -> Self {
133 self.file_open_hook = Some(hook);
134 self
135 }
136
137 pub fn stdin(mut self, hook: InputHook) -> Self {
138 self.stdin_hook = Some(hook);
139 self
140 }
141
142 pub fn stdout(mut self, hook: OutputHook) -> Self {
143 self.stdout_hook = Some(hook);
144 self
145 }
146
147 pub fn stderr(mut self, hook: OutputHook) -> Self {
148 self.stderr_hook = Some(hook);
149 self
150 }
151
152 pub fn env(mut self, hook: EnvHook) -> Self {
153 self.env_hook = Some(hook);
154 self
155 }
156
157 pub fn unix_time(mut self, hook: UnixTimeHook) -> Self {
158 self.unix_time_hook = Some(hook);
159 self
160 }
161
162 pub fn cpu_clock(mut self, hook: CpuClockHook) -> Self {
163 self.cpu_clock_hook = Some(hook);
164 self
165 }
166
167 pub fn entropy(mut self, hook: EntropyHook) -> Self {
168 self.entropy_hook = Some(hook);
169 self
170 }
171
172 pub fn temp_name(mut self, hook: TempNameHook) -> Self {
173 self.temp_name_hook = Some(hook);
174 self
175 }
176
177 pub fn popen(mut self, hook: PopenHook) -> Self {
178 self.popen_hook = Some(hook);
179 self
180 }
181
182 pub fn file_remove(mut self, hook: FileRemoveHook) -> Self {
183 self.file_remove_hook = Some(hook);
184 self
185 }
186
187 pub fn file_rename(mut self, hook: FileRenameHook) -> Self {
188 self.file_rename_hook = Some(hook);
189 self
190 }
191
192 pub fn os_execute(mut self, hook: OsExecuteHook) -> Self {
193 self.os_execute_hook = Some(hook);
194 self
195 }
196
197 pub fn dynlib_load(mut self, hook: DynLibLoadHook) -> Self {
198 self.dynlib_load_hook = Some(hook);
199 self
200 }
201
202 pub fn dynlib_symbol(mut self, hook: DynLibSymbolHook) -> Self {
203 self.dynlib_symbol_hook = Some(hook);
204 self
205 }
206
207 pub fn dynlib_unload(mut self, hook: DynLibUnloadHook) -> Self {
208 self.dynlib_unload_hook = Some(hook);
209 self
210 }
211
212 pub fn install(self, state: &mut LuaState) {
213 let global = &mut *state.global_mut();
214 global.file_loader_hook = self.file_loader_hook;
215 global.file_open_hook = self.file_open_hook;
216 global.stdin_hook = self.stdin_hook;
217 global.stdout_hook = self.stdout_hook;
218 global.stderr_hook = self.stderr_hook;
219 global.env_hook = self.env_hook;
220 global.unix_time_hook = self.unix_time_hook;
221 global.cpu_clock_hook = self.cpu_clock_hook;
222 global.entropy_hook = self.entropy_hook;
223 global.temp_name_hook = self.temp_name_hook;
224 global.popen_hook = self.popen_hook;
225 global.file_remove_hook = self.file_remove_hook;
226 global.file_rename_hook = self.file_rename_hook;
227 global.os_execute_hook = self.os_execute_hook;
228 global.dynlib_load_hook = self.dynlib_load_hook;
229 global.dynlib_symbol_hook = self.dynlib_symbol_hook;
230 global.dynlib_unload_hook = self.dynlib_unload_hook;
231 }
232}
233
234#[derive(Clone)]
241pub struct Lua {
242 inner: Rc<LuaInner>,
243}
244
245struct LuaInner {
246 state: RefCell<LuaState>,
247 active_state: Cell<*mut LuaState>,
248 pending_external_unroots: RefCell<Vec<ExternalRootKey>>,
249 userdata_metatables: RefCell<HashMap<TypeId, GcRef<RawLuaTable>>>,
254 userdata_scoped_metatables: RefCell<HashMap<TypeId, GcRef<RawLuaTable>>>,
259}
260
261struct UserDataCell<T> {
262 value: RefCell<T>,
263}
264
265struct ScopedCell<T: 'static> {
292 ptr: Cell<Option<NonNull<T>>>,
293 borrow: Cell<isize>,
296}
297
298impl<T: 'static> ScopedCell<T> {
299 fn new(data: &mut T) -> Self {
300 Self {
301 ptr: Cell::new(Some(NonNull::from(data))),
302 borrow: Cell::new(0),
303 }
304 }
305
306 fn try_borrow(&self) -> Result<ScopedRef<'_, T>> {
307 let b = self.borrow.get();
308 if b < 0 {
309 return Err(LuaError::runtime(format_args!(
310 "scoped userdata is already mutably borrowed"
311 )));
312 }
313 let ptr = self.ptr.get().ok_or_else(|| {
314 LuaError::runtime(format_args!(
315 "scoped userdata is no longer valid (its scope has ended)"
316 ))
317 })?;
318 self.borrow.set(b + 1);
319 Ok(ScopedRef { cell: self, ptr })
320 }
321
322 fn try_borrow_mut(&self) -> Result<ScopedRefMut<'_, T>> {
323 let b = self.borrow.get();
324 if b != 0 {
325 return Err(LuaError::runtime(format_args!(
326 "scoped userdata is already borrowed"
327 )));
328 }
329 let ptr = self.ptr.get().ok_or_else(|| {
330 LuaError::runtime(format_args!(
331 "scoped userdata is no longer valid (its scope has ended)"
332 ))
333 })?;
334 self.borrow.set(-1);
335 Ok(ScopedRefMut { cell: self, ptr })
336 }
337}
338
339trait ScopeInvalidate {
342 fn invalidate(&self);
343}
344
345impl<T: 'static> ScopeInvalidate for ScopedCell<T> {
346 fn invalidate(&self) {
347 self.ptr.set(None);
353 }
354}
355
356struct ScopedRef<'a, T: 'static> {
357 cell: &'a ScopedCell<T>,
358 ptr: NonNull<T>,
359}
360
361impl<'a, T: 'static> Drop for ScopedRef<'a, T> {
362 fn drop(&mut self) {
363 self.cell.borrow.set(self.cell.borrow.get() - 1);
364 }
365}
366
367impl<'a, T: 'static> Deref for ScopedRef<'a, T> {
368 type Target = T;
369 fn deref(&self) -> &T {
370 unsafe { self.ptr.as_ref() }
376 }
377}
378
379struct ScopedRefMut<'a, T: 'static> {
380 cell: &'a ScopedCell<T>,
381 ptr: NonNull<T>,
382}
383
384impl<'a, T: 'static> Drop for ScopedRefMut<'a, T> {
385 fn drop(&mut self) {
386 self.cell.borrow.set(0);
387 }
388}
389
390impl<'a, T: 'static> Deref for ScopedRefMut<'a, T> {
391 type Target = T;
392 fn deref(&self) -> &T {
393 unsafe { self.ptr.as_ref() }
395 }
396}
397
398impl<'a, T: 'static> DerefMut for ScopedRefMut<'a, T> {
399 fn deref_mut(&mut self) -> &mut T {
400 unsafe { self.ptr.as_mut() }
403 }
404}
405
406pub struct Scope<'scope> {
413 invalidators: RefCell<Vec<Rc<dyn ScopeInvalidate>>>,
414 _phantom: std::marker::PhantomData<&'scope mut ()>,
415}
416
417impl<'scope> Scope<'scope> {
418 fn new() -> Self {
419 Self {
420 invalidators: RefCell::new(Vec::new()),
421 _phantom: std::marker::PhantomData,
422 }
423 }
424
425 pub fn create_userdata_ref_mut<T>(&self, lua: &Lua, data: &'scope mut T) -> Result<AnyUserData>
436 where
437 T: UserData,
438 {
439 let cell = Rc::new(ScopedCell::<T>::new(data));
440 self.invalidators
441 .borrow_mut()
442 .push(cell.clone() as Rc<dyn ScopeInvalidate>);
443 lua.create_scoped_userdata::<T>(cell)
444 }
445
446 pub fn create_function<A, R, F>(&self, lua: &Lua, func: F) -> Result<Function>
456 where
457 A: FromLuaMulti + 'static,
458 R: IntoLuaMulti + 'static,
459 F: Fn(&Lua, A) -> Result<R> + 'scope,
460 {
461 let adapter: Box<dyn Fn(&Lua, Vec<Value>) -> Result<Vec<Value>> + 'scope> =
462 Box::new(move |lua, args| {
463 let args = A::from_lua_multi(args, lua)?;
464 let returns = func(lua, args)?;
465 returns.into_lua_multi(lua)
466 });
467 self.install_function(lua, adapter)
468 }
469
470 pub fn create_function_mut<A, R, F>(&self, lua: &Lua, func: F) -> Result<Function>
475 where
476 A: FromLuaMulti + 'static,
477 R: IntoLuaMulti + 'static,
478 F: FnMut(&Lua, A) -> Result<R> + 'scope,
479 {
480 let func = RefCell::new(func);
481 self.create_function(lua, move |lua, args| {
482 let mut func = func.try_borrow_mut().map_err(|_| {
483 LuaError::runtime(format_args!("mutable Rust callback is already borrowed"))
484 })?;
485 func(lua, args)
486 })
487 }
488
489 fn install_function(
494 &self,
495 lua: &Lua,
496 adapter: Box<dyn Fn(&Lua, Vec<Value>) -> Result<Vec<Value>> + 'scope>,
497 ) -> Result<Function> {
498 let adapter_static: Box<dyn Fn(&Lua, Vec<Value>) -> Result<Vec<Value>>> =
508 unsafe { std::mem::transmute(adapter) };
509 let cell = Rc::new(ScopedFnCell {
510 boxed: RefCell::new(Some(adapter_static)),
511 });
512 self.invalidators
513 .borrow_mut()
514 .push(cell.clone() as Rc<dyn ScopeInvalidate>);
515 lua.create_scoped_function(cell)
516 }
517}
518
519impl<'scope> Drop for Scope<'scope> {
520 fn drop(&mut self) {
521 for inv in self.invalidators.borrow().iter() {
522 inv.invalidate();
523 }
524 }
525}
526
527struct ScopedFnCell {
532 boxed: RefCell<Option<Box<dyn Fn(&Lua, Vec<Value>) -> Result<Vec<Value>>>>>,
533}
534
535impl ScopedFnCell {
536 fn try_call(&self, lua: &Lua, args: Vec<Value>) -> Result<Vec<Value>> {
539 let guard = self.boxed.borrow();
540 let func = guard.as_deref().ok_or_else(|| {
541 LuaError::runtime(format_args!(
542 "scoped function is no longer valid (its scope has ended)"
543 ))
544 })?;
545 func(lua, args)
546 }
547}
548
549impl ScopeInvalidate for ScopedFnCell {
550 fn invalidate(&self) {
551 *self.boxed.borrow_mut() = None;
552 }
553}
554
555struct DelegatedCell<S: 'static> {
580 enter_mut: RefCell<Option<Box<dyn Fn(&mut dyn FnMut(&mut S)) -> Result<()>>>>,
581}
582
583impl<S: 'static> DelegatedCell<S> {
584 fn enter_mut(&self, f: &mut dyn FnMut(&mut S)) -> Result<()> {
585 let guard = self.enter_mut.borrow();
586 let inner = guard.as_ref().ok_or_else(|| {
587 LuaError::runtime(format_args!(
588 "scoped userdata is no longer valid (its scope has ended)"
589 ))
590 })?;
591 inner(f)
592 }
593}
594
595impl<S: 'static> ScopeInvalidate for DelegatedCell<S> {
596 fn invalidate(&self) {
597 *self.enter_mut.borrow_mut() = None;
598 }
599}
600
601struct RustCallbackCell {
604 function: LuaRustFunction,
605}
606
607struct ActiveStateGuard<'a> {
608 inner: &'a LuaInner,
609 previous: *mut LuaState,
610}
611
612impl Drop for ActiveStateGuard<'_> {
613 fn drop(&mut self) {
614 self.inner.active_state.set(self.previous);
615 }
616}
617
618impl LuaInner {
619 fn enter_active(&self, state: *mut LuaState) -> ActiveStateGuard<'_> {
620 let previous = self.active_state.replace(state);
621 ActiveStateGuard {
622 inner: self,
623 previous,
624 }
625 }
626
627 fn flush_pending_external_unroots(&self, state: &mut LuaState) {
628 let pending = self.pending_external_unroots.replace(Vec::new());
629 if pending.is_empty() {
630 return;
631 }
632
633 let mut still_pending = Vec::new();
634 for key in pending {
635 if state.try_external_unroot_value(key).is_err() {
636 still_pending.push(key);
637 }
638 }
639
640 if !still_pending.is_empty() {
641 self.pending_external_unroots
642 .borrow_mut()
643 .extend(still_pending);
644 }
645 }
646}
647
648impl fmt::Debug for Lua {
649 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
650 f.debug_struct("Lua").finish_non_exhaustive()
651 }
652}
653
654impl Lua {
655 pub fn new() -> Self {
657 Self::try_new().expect("Lua runtime should initialize")
658 }
659
660 pub fn try_new() -> Result<Self> {
662 Self::with_hooks(HostHooks::default())
663 }
664
665 pub fn with_hooks(hooks: HostHooks) -> Result<Self> {
667 let mut state = new_state().ok_or(LuaError::Memory)?;
668 install_parser_hook(&mut state);
669 hooks.install(&mut state);
670 open_libs(&mut state)?;
671 Ok(Self::from_initialized_state(state))
672 }
673
674 fn from_initialized_state(state: LuaState) -> Self {
675 Lua {
676 inner: Rc::new(LuaInner {
677 state: RefCell::new(state),
678 active_state: Cell::new(std::ptr::null_mut()),
679 pending_external_unroots: RefCell::new(Vec::new()),
680 userdata_metatables: RefCell::new(HashMap::new()),
681 userdata_scoped_metatables: RefCell::new(HashMap::new()),
682 }),
683 }
684 }
685
686 fn with_state<R>(&self, f: impl FnOnce(&mut LuaState) -> R) -> R {
687 if let Ok(mut state) = self.inner.state.try_borrow_mut() {
688 let _active = self.inner.enter_active(&mut *state);
689 self.inner.flush_pending_external_unroots(&mut state);
690 let result = f(&mut state);
691 self.inner.flush_pending_external_unroots(&mut state);
692 return result;
693 }
694
695 let state = self
696 .active_state_mut()
697 .expect("re-entrant Lua access without an active state");
698 let result = f(state);
699 self.inner.flush_pending_external_unroots(state);
700 result
701 }
702
703 fn active_state_mut(&self) -> Option<&mut LuaState> {
704 let state = self.inner.active_state.get();
705 if state.is_null() {
706 return None;
707 }
708
709 Some(unsafe { &mut *state })
715 }
716
717 fn unroot_external_key(&self, key: ExternalRootKey) {
718 let removed = if let Ok(mut state) = self.inner.state.try_borrow_mut() {
719 let _active = self.inner.enter_active(&mut *state);
720 self.inner.flush_pending_external_unroots(&mut state);
721 let removed = state.try_external_unroot_value(key).is_ok();
722 self.inner.flush_pending_external_unroots(&mut state);
723 removed
724 } else {
725 if let Some(state) = self.active_state_mut() {
726 let removed = state.try_external_unroot_value(key).is_ok();
727 self.inner.flush_pending_external_unroots(state);
728 removed
729 } else {
730 false
731 }
732 };
733
734 if !removed {
735 self.inner.pending_external_unroots.borrow_mut().push(key);
736 }
737 }
738
739 fn root_raw(&self, value: RawLuaValue) -> RootedValue {
740 let key = self.with_state(|state| state.external_root_value(value));
741 RootedValue {
742 lua: self.clone(),
743 key,
744 }
745 }
746
747 fn root_raw_in_state(&self, state: &mut LuaState, value: RawLuaValue) -> RootedValue {
748 let key = state.external_root_value(value);
749 RootedValue {
750 lua: self.clone(),
751 key,
752 }
753 }
754
755 fn userdata_cell<'a, T: 'static>(
756 &self,
757 userdata: &'a AnyUserData,
758 ) -> Result<&'a UserDataCell<T>> {
759 if !Rc::ptr_eq(&self.inner, &userdata.root.lua.inner) {
760 return Err(LuaError::runtime(format_args!(
761 "Lua userdata belongs to a different state"
762 )));
763 }
764 userdata.host_cell()
765 }
766
767 pub fn load(&self, source: impl AsRef<[u8]>) -> Chunk {
769 Chunk {
770 lua: self.clone(),
771 source: source.as_ref().to_vec(),
772 name: b"chunk".to_vec(),
773 }
774 }
775
776 pub fn globals(&self) -> Table {
778 let raw = self.with_state(|state| state.global().globals.clone());
779 Table {
780 root: self.root_raw(raw),
781 }
782 }
783
784 pub fn create_table(&self) -> Result<Table> {
786 let root = self.with_state(|state| {
787 let _heap_guard = heap_guard(state);
788 let table = state.new_table();
789 let raw = RawLuaValue::Table(table);
790 let key = state.external_root_value(raw);
791 state.gc().check_step();
792 RootedValue {
793 lua: self.clone(),
794 key,
795 }
796 });
797 Ok(Table { root })
798 }
799
800 pub fn create_string(&self, bytes: impl AsRef<[u8]>) -> Result<LuaString> {
802 let bytes = bytes.as_ref();
803 let root = self.with_state(|state| {
804 let _heap_guard = heap_guard(state);
805 let string = state.new_string(bytes)?;
806 let raw = RawLuaValue::Str(string);
807 let key = state.external_root_value(raw);
808 state.gc().check_step();
809 Ok::<_, LuaError>(RootedValue {
810 lua: self.clone(),
811 key,
812 })
813 })?;
814 Ok(LuaString { root })
815 }
816
817 pub fn create_function<A, R, F>(&self, func: F) -> Result<Function>
818 where
819 A: FromLuaMulti + 'static,
820 R: IntoLuaMulti + 'static,
821 F: Fn(&Lua, A) -> Result<R> + 'static,
822 {
823 let lua_weak = Rc::downgrade(&self.inner);
824 let callable: LuaRustFunction = Rc::new(move |state| {
825 let lua = match lua_weak.upgrade() {
826 Some(inner) => Lua { inner },
827 None => {
828 return Err(LuaError::runtime(format_args!(
829 "Lua callback fired after the state was dropped"
830 )))
831 }
832 };
833 match catch_unwind(AssertUnwindSafe(|| {
834 let args = callback_args(state, &lua)?;
835 let args = A::from_lua_multi(args, &lua)?;
836 let returns = func(&lua, args)?;
837 let returns = returns.into_lua_multi(&lua)?;
838 push_callback_returns(state, &lua, returns)
839 })) {
840 Ok(result) => result,
841 Err(_) => Err(LuaError::runtime(format_args!("Rust callback panicked"))),
842 }
843 });
844 self.create_registered_function(callable)
845 }
846
847 pub fn create_function_mut<A, R, F>(&self, func: F) -> Result<Function>
848 where
849 A: FromLuaMulti + 'static,
850 R: IntoLuaMulti + 'static,
851 F: FnMut(&Lua, A) -> Result<R> + 'static,
852 {
853 let func = RefCell::new(func);
854 self.create_function(move |lua, args| {
855 let mut func = func.try_borrow_mut().map_err(|_| {
856 LuaError::runtime(format_args!("mutable Rust callback is already borrowed"))
857 })?;
858 func(lua, args)
859 })
860 }
861
862 fn create_registered_function(&self, callable: LuaRustFunction) -> Result<Function> {
863 let root = self.with_state(|state| {
864 let trampoline = rust_callback_trampoline as lua_vm::state::LuaCFunction;
865 let idx = {
866 let mut global = state.global_mut();
867 match global.c_functions.iter().position(|existing| {
868 existing
869 .as_bare()
870 .is_some_and(|existing| std::ptr::fn_addr_eq(existing, trampoline))
871 }) {
872 Some(idx) => idx,
873 None => {
874 let idx = global.c_functions.len();
875 global.c_functions.push(LuaCallable::bare(trampoline));
876 idx
877 }
878 }
879 };
880 let raw = with_heap_guard(state, || {
881 let callback_payload = GcRef::new(RawLuaUserData {
882 data: Box::new([]),
883 uv: Vec::new(),
884 metatable: RefCell::new(None),
885 host_value: RefCell::new(Some(
886 Rc::new(RustCallbackCell { function: callable }) as Rc<dyn Any>,
887 )),
888 });
889 RawLuaValue::Function(RawLuaClosure::C(GcRef::new(RawLuaCClosure {
890 func: idx,
891 upvalues: vec![RawLuaValue::UserData(callback_payload)],
892 })))
893 });
894 let key = state.external_root_value(raw);
895 state.gc().check_step();
896 RootedValue {
897 lua: self.clone(),
898 key,
899 }
900 });
901 Ok(Function { root })
902 }
903
904 fn create_userdata_method<T, A, R, F>(&self, method: F) -> Result<Function>
905 where
906 T: UserData,
907 A: FromLuaMulti + 'static,
908 R: IntoLuaMulti + 'static,
909 F: Fn(&Lua, &T, A) -> Result<R> + 'static,
910 {
911 let lua_weak = Rc::downgrade(&self.inner);
912 let callable: LuaRustFunction = Rc::new(move |state| {
913 let lua = match lua_weak.upgrade() {
914 Some(inner) => Lua { inner },
915 None => {
916 return Err(LuaError::runtime(format_args!(
917 "Lua callback fired after the state was dropped"
918 )))
919 }
920 };
921 match catch_unwind(AssertUnwindSafe(|| {
922 let (userdata, args) = callback_userdata_args(state, &lua)?;
923 let args = A::from_lua_multi(args, &lua)?;
924 let cell = lua.userdata_cell::<T>(&userdata)?;
925 let value = cell.value.try_borrow().map_err(|_| {
926 LuaError::runtime(format_args!("userdata is already mutably borrowed"))
927 })?;
928 let returns = method(&lua, &value, args)?;
929 let returns = returns.into_lua_multi(&lua)?;
930 push_callback_returns(state, &lua, returns)
931 })) {
932 Ok(result) => result,
933 Err(_) => Err(LuaError::runtime(format_args!(
934 "Rust userdata method panicked"
935 ))),
936 }
937 });
938 self.create_registered_function(callable)
939 }
940
941 fn create_userdata_method_mut<T, A, R, F>(&self, method: F) -> Result<Function>
942 where
943 T: UserData,
944 A: FromLuaMulti + 'static,
945 R: IntoLuaMulti + 'static,
946 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
947 {
948 let lua_weak = Rc::downgrade(&self.inner);
949 let callable: LuaRustFunction = Rc::new(move |state| {
950 let lua = match lua_weak.upgrade() {
951 Some(inner) => Lua { inner },
952 None => {
953 return Err(LuaError::runtime(format_args!(
954 "Lua callback fired after the state was dropped"
955 )))
956 }
957 };
958 match catch_unwind(AssertUnwindSafe(|| {
959 let (userdata, args) = callback_userdata_args(state, &lua)?;
960 let args = A::from_lua_multi(args, &lua)?;
961 let cell = lua.userdata_cell::<T>(&userdata)?;
962 let mut value = cell
963 .value
964 .try_borrow_mut()
965 .map_err(|_| LuaError::runtime(format_args!("userdata is already borrowed")))?;
966 let returns = method(&lua, &mut value, args)?;
967 let returns = returns.into_lua_multi(&lua)?;
968 push_callback_returns(state, &lua, returns)
969 })) {
970 Ok(result) => result,
971 Err(_) => Err(LuaError::runtime(format_args!(
972 "Rust userdata method panicked"
973 ))),
974 }
975 });
976 self.create_registered_function(callable)
977 }
978
979 pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData>
980 where
981 T: UserData,
982 {
983 let type_id = TypeId::of::<T>();
984 let cached = self
985 .inner
986 .userdata_metatables
987 .borrow()
988 .get(&type_id)
989 .cloned();
990 let metatable = match cached {
991 Some(metatable) => metatable,
992 None => {
993 let mut methods = UserDataMethodRegistry::<T>::new(self);
994 T::add_methods(&mut methods);
995 T::add_meta_methods(&mut methods);
996 let metatable = methods.build_metatable()?;
997 self.inner
998 .userdata_metatables
999 .borrow_mut()
1000 .insert(type_id, metatable.clone());
1001 metatable
1002 }
1003 };
1004 self.attach_userdata(data, metatable)
1005 }
1006
1007 fn attach_userdata<T: UserData>(
1011 &self,
1012 data: T,
1013 metatable: GcRef<RawLuaTable>,
1014 ) -> Result<AnyUserData> {
1015 let cell: Rc<dyn Any> = Rc::new(UserDataCell {
1016 value: RefCell::new(data),
1017 });
1018 let host_value = cell.clone();
1019 let root = self.with_state(|state| {
1020 let userdata = with_heap_guard(state, || {
1021 GcRef::new(RawLuaUserData {
1022 data: Box::new([]),
1023 uv: Vec::new(),
1024 metatable: RefCell::new(None),
1025 host_value: RefCell::new(None),
1026 })
1027 });
1028 userdata.set_metatable(Some(metatable));
1029 userdata.set_host_value(Some(cell));
1030 let key = state.external_root_value(RawLuaValue::UserData(userdata));
1031 RootedValue {
1032 lua: self.clone(),
1033 key,
1034 }
1035 });
1036 Ok(AnyUserData {
1037 root,
1038 host_value: Some(host_value),
1039 })
1040 }
1041
1042 pub fn scope<F, R>(&self, f: F) -> Result<R>
1081 where
1082 F: for<'scope> FnOnce(&Scope<'scope>) -> Result<R>,
1083 {
1084 let scope = Scope::new();
1085 let result = f(&scope);
1086 drop(scope);
1090 result
1091 }
1092
1093 fn scoped_metatable_for<T>(&self) -> Result<GcRef<RawLuaTable>>
1099 where
1100 T: UserData,
1101 {
1102 let type_id = TypeId::of::<T>();
1103 let cached = self
1104 .inner
1105 .userdata_scoped_metatables
1106 .borrow()
1107 .get(&type_id)
1108 .cloned();
1109 if let Some(mt) = cached {
1110 return Ok(mt);
1111 }
1112 let mut methods = UserDataMethodRegistry::<T>::new_scoped(self);
1113 T::add_methods(&mut methods);
1114 T::add_meta_methods(&mut methods);
1115 let mt = methods.build_metatable()?;
1116 self.inner
1117 .userdata_scoped_metatables
1118 .borrow_mut()
1119 .insert(type_id, mt.clone());
1120 Ok(mt)
1121 }
1122
1123 fn create_scoped_userdata<T>(&self, cell: Rc<ScopedCell<T>>) -> Result<AnyUserData>
1126 where
1127 T: UserData,
1128 {
1129 let metatable = self.scoped_metatable_for::<T>()?;
1130 self.attach_scoped_userdata::<T>(cell, metatable)
1131 }
1132
1133 fn create_delegated_userdata<S>(&self, cell: Rc<DelegatedCell<S>>) -> Result<AnyUserData>
1137 where
1138 S: UserData,
1139 {
1140 let metatable = self.scoped_metatable_for::<S>()?;
1141 let host_value: Rc<dyn Any> = cell;
1142 let root = self.with_state(|state| {
1143 let userdata = with_heap_guard(state, || {
1144 GcRef::new(RawLuaUserData {
1145 data: Box::new([]),
1146 uv: Vec::new(),
1147 metatable: RefCell::new(None),
1148 host_value: RefCell::new(None),
1149 })
1150 });
1151 userdata.set_metatable(Some(metatable));
1152 userdata.set_host_value(Some(host_value.clone()));
1153 let key = state.external_root_value(RawLuaValue::UserData(userdata));
1154 RootedValue {
1155 lua: self.clone(),
1156 key,
1157 }
1158 });
1159 Ok(AnyUserData {
1160 root,
1161 host_value: Some(host_value),
1162 })
1163 }
1164
1165 fn attach_scoped_userdata<T>(
1168 &self,
1169 cell: Rc<ScopedCell<T>>,
1170 metatable: GcRef<RawLuaTable>,
1171 ) -> Result<AnyUserData>
1172 where
1173 T: UserData,
1174 {
1175 let host_value: Rc<dyn Any> = cell;
1176 let root = self.with_state(|state| {
1177 let userdata = with_heap_guard(state, || {
1178 GcRef::new(RawLuaUserData {
1179 data: Box::new([]),
1180 uv: Vec::new(),
1181 metatable: RefCell::new(None),
1182 host_value: RefCell::new(None),
1183 })
1184 });
1185 userdata.set_metatable(Some(metatable));
1186 userdata.set_host_value(Some(host_value.clone()));
1187 let key = state.external_root_value(RawLuaValue::UserData(userdata));
1188 RootedValue {
1189 lua: self.clone(),
1190 key,
1191 }
1192 });
1193 Ok(AnyUserData {
1194 root,
1195 host_value: Some(host_value),
1196 })
1197 }
1198
1199 fn dispatch_scoped_borrow<T, F, R>(
1210 &self,
1211 userdata: &AnyUserData,
1212 f: F,
1213 ) -> Result<R>
1214 where
1215 T: 'static,
1216 F: FnOnce(&T) -> Result<R>,
1217 {
1218 let host = userdata
1219 .host_value
1220 .as_ref()
1221 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust userdata payload")))?;
1222
1223 if let Ok(scoped) = Rc::clone(host).downcast::<ScopedCell<T>>() {
1224 let borrow = scoped.try_borrow()?;
1225 return f(&*borrow);
1226 }
1227
1228 if let Ok(delegated) = Rc::clone(host).downcast::<DelegatedCell<T>>() {
1229 let mut slot: Option<Result<R>> = None;
1230 let mut f_slot = Some(f);
1231 delegated.enter_mut(&mut |t| {
1232 if let Some(f) = f_slot.take() {
1233 slot = Some(f(&*t));
1234 }
1235 })?;
1236 return slot.expect("delegated enter_mut must invoke its callback");
1237 }
1238
1239 Err(LuaError::runtime(format_args!(
1240 "scoped userdata type mismatch"
1241 )))
1242 }
1243
1244 fn dispatch_scoped_borrow_mut<T, F, R>(
1245 &self,
1246 userdata: &AnyUserData,
1247 f: F,
1248 ) -> Result<R>
1249 where
1250 T: 'static,
1251 F: FnOnce(&mut T) -> Result<R>,
1252 {
1253 let host = userdata
1254 .host_value
1255 .as_ref()
1256 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust userdata payload")))?;
1257
1258 if let Ok(scoped) = Rc::clone(host).downcast::<ScopedCell<T>>() {
1259 let mut borrow = scoped.try_borrow_mut()?;
1260 return f(&mut *borrow);
1261 }
1262
1263 if let Ok(delegated) = Rc::clone(host).downcast::<DelegatedCell<T>>() {
1264 let mut slot: Option<Result<R>> = None;
1265 let mut f_slot = Some(f);
1266 delegated.enter_mut(&mut |t| {
1267 if let Some(f) = f_slot.take() {
1268 slot = Some(f(t));
1269 }
1270 })?;
1271 return slot.expect("delegated enter_mut must invoke its callback");
1272 }
1273
1274 Err(LuaError::runtime(format_args!(
1275 "scoped userdata type mismatch"
1276 )))
1277 }
1278
1279 fn create_scoped_userdata_method<T, A, R, F>(&self, method: F) -> Result<Function>
1284 where
1285 T: UserData,
1286 A: FromLuaMulti + 'static,
1287 R: IntoLuaMulti + 'static,
1288 F: Fn(&Lua, &T, A) -> Result<R> + 'static,
1289 {
1290 let lua_weak = Rc::downgrade(&self.inner);
1291 let callable: LuaRustFunction = Rc::new(move |state| {
1292 let lua = match lua_weak.upgrade() {
1293 Some(inner) => Lua { inner },
1294 None => {
1295 return Err(LuaError::runtime(format_args!(
1296 "Lua callback fired after the state was dropped"
1297 )))
1298 }
1299 };
1300 match catch_unwind(AssertUnwindSafe(|| {
1301 let (userdata, args) = callback_userdata_args(state, &lua)?;
1302 let args = A::from_lua_multi(args, &lua)?;
1303 let returns = lua.dispatch_scoped_borrow::<T, _, _>(&userdata, |t| {
1304 method(&lua, t, args)
1305 })?;
1306 let returns = returns.into_lua_multi(&lua)?;
1307 push_callback_returns(state, &lua, returns)
1308 })) {
1309 Ok(result) => result,
1310 Err(_) => Err(LuaError::runtime(format_args!(
1311 "Rust userdata method panicked"
1312 ))),
1313 }
1314 });
1315 self.create_registered_function(callable)
1316 }
1317
1318 fn create_scoped_userdata_method_mut<T, A, R, F>(&self, method: F) -> Result<Function>
1319 where
1320 T: UserData,
1321 A: FromLuaMulti + 'static,
1322 R: IntoLuaMulti + 'static,
1323 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
1324 {
1325 let lua_weak = Rc::downgrade(&self.inner);
1326 let callable: LuaRustFunction = Rc::new(move |state| {
1327 let lua = match lua_weak.upgrade() {
1328 Some(inner) => Lua { inner },
1329 None => {
1330 return Err(LuaError::runtime(format_args!(
1331 "Lua callback fired after the state was dropped"
1332 )))
1333 }
1334 };
1335 match catch_unwind(AssertUnwindSafe(|| {
1336 let (userdata, args) = callback_userdata_args(state, &lua)?;
1337 let args = A::from_lua_multi(args, &lua)?;
1338 let returns = lua.dispatch_scoped_borrow_mut::<T, _, _>(&userdata, |t| {
1339 method(&lua, t, args)
1340 })?;
1341 let returns = returns.into_lua_multi(&lua)?;
1342 push_callback_returns(state, &lua, returns)
1343 })) {
1344 Ok(result) => result,
1345 Err(_) => Err(LuaError::runtime(format_args!(
1346 "Rust userdata method panicked"
1347 ))),
1348 }
1349 });
1350 self.create_registered_function(callable)
1351 }
1352
1353 fn create_scoped_function(&self, cell: Rc<ScopedFnCell>) -> Result<Function> {
1359 let lua_weak = Rc::downgrade(&self.inner);
1360 let callable: LuaRustFunction = Rc::new(move |state| {
1361 let lua = match lua_weak.upgrade() {
1362 Some(inner) => Lua { inner },
1363 None => {
1364 return Err(LuaError::runtime(format_args!(
1365 "Lua callback fired after the state was dropped"
1366 )))
1367 }
1368 };
1369 match catch_unwind(AssertUnwindSafe(|| {
1370 let args = callback_args(state, &lua)?;
1371 let returns = cell.try_call(&lua, args)?;
1372 push_callback_returns(state, &lua, returns)
1373 })) {
1374 Ok(result) => result,
1375 Err(_) => Err(LuaError::runtime(format_args!(
1376 "scoped Rust callback panicked"
1377 ))),
1378 }
1379 });
1380 self.create_registered_function(callable)
1381 }
1382
1383 pub fn gc_collect(&self) {
1385 self.with_state(|state| state.gc().full_collect());
1386 }
1387}
1388
1389pub struct Chunk {
1390 lua: Lua,
1391 source: Vec<u8>,
1392 name: Vec<u8>,
1393}
1394
1395impl Chunk {
1396 pub fn set_name(mut self, name: impl AsRef<[u8]>) -> Self {
1397 self.name = name.as_ref().to_vec();
1398 self
1399 }
1400
1401 pub fn exec(self) -> Result<()> {
1402 self.lua
1403 .with_state(|state| exec_state(state, &self.source, &self.name))
1404 }
1405
1406 pub fn eval<T: FromLuaMulti>(self) -> Result<T> {
1407 let raws = self.lua.with_state(|state| {
1408 let saved_top = state.top_idx();
1409 let status = load_buffer(state, &self.source, &self.name)?;
1410 if status != 0 {
1411 let err = state.pop();
1412 state.set_top_idx(saved_top);
1413 return Err(LuaError::from_value(err));
1414 }
1415 match lua_vm::api::pcall_k(state, 0, T::NRESULTS, 0, 0, None) {
1416 Ok(_) => {
1417 let nresults = if T::NRESULTS < 0 {
1418 state.top_idx().0.saturating_sub(saved_top.0) as i32
1419 } else {
1420 T::NRESULTS
1421 };
1422 let mut values = Vec::with_capacity(nresults as usize);
1423 for _ in 0..nresults {
1424 values.push(state.pop());
1425 }
1426 values.reverse();
1427 state.set_top_idx(saved_top);
1428 Ok(values)
1429 }
1430 Err(err) => {
1431 state.set_top_idx(saved_top);
1432 Err(err)
1433 }
1434 }
1435 })?;
1436 let values = raws
1437 .into_iter()
1438 .map(|raw| Value::from_raw(&self.lua, raw))
1439 .collect::<Result<Vec<_>>>()?;
1440 T::from_lua_multi(values, &self.lua)
1441 }
1442}
1443
1444#[derive(Debug)]
1445struct RootedValue {
1446 lua: Lua,
1447 key: ExternalRootKey,
1448}
1449
1450impl RootedValue {
1451 fn raw(&self) -> Result<RawLuaValue> {
1452 self.lua
1453 .with_state(|state| state.external_rooted_value(self.key))
1454 .ok_or_else(stale_handle_error)
1455 }
1456
1457 fn raw_for_lua(&self, lua: &Lua, state: &LuaState) -> Result<RawLuaValue> {
1458 if !Rc::ptr_eq(&self.lua.inner, &lua.inner) {
1459 return Err(LuaError::runtime(format_args!(
1460 "Lua handle belongs to a different state"
1461 )));
1462 }
1463 state
1464 .external_rooted_value(self.key)
1465 .ok_or_else(stale_handle_error)
1466 }
1467}
1468
1469impl Clone for RootedValue {
1470 fn clone(&self) -> Self {
1471 let raw = self.raw().expect("rooted Lua handle should not be stale");
1472 self.lua.root_raw(raw)
1473 }
1474}
1475
1476impl Drop for RootedValue {
1477 fn drop(&mut self) {
1478 self.lua.unroot_external_key(self.key);
1479 }
1480}
1481
1482#[derive(Debug, Clone)]
1484pub enum Value {
1485 Nil,
1486 Boolean(bool),
1487 Integer(i64),
1488 Number(f64),
1489 String(LuaString),
1490 Table(Table),
1491 Function(Function),
1492 UserData(AnyUserData),
1493 LightUserData(*mut c_void),
1494 Thread(Thread),
1495}
1496
1497impl Value {
1498 fn from_raw(lua: &Lua, raw: RawLuaValue) -> Result<Self> {
1499 lua.with_state(|state| Self::from_raw_in_state(lua, state, raw))
1500 }
1501
1502 fn from_raw_in_state(lua: &Lua, state: &mut LuaState, raw: RawLuaValue) -> Result<Self> {
1503 Ok(match raw {
1504 RawLuaValue::Nil => Value::Nil,
1505 RawLuaValue::Bool(v) => Value::Boolean(v),
1506 RawLuaValue::Int(v) => Value::Integer(v),
1507 RawLuaValue::Float(v) => Value::Number(v),
1508 RawLuaValue::Str(v) => Value::String(LuaString {
1509 root: lua.root_raw_in_state(state, RawLuaValue::Str(v)),
1510 }),
1511 RawLuaValue::Table(v) => Value::Table(Table {
1512 root: lua.root_raw_in_state(state, RawLuaValue::Table(v)),
1513 }),
1514 RawLuaValue::Function(v) => Value::Function(Function {
1515 root: lua.root_raw_in_state(state, RawLuaValue::Function(v)),
1516 }),
1517 RawLuaValue::UserData(v) => {
1518 let host_value = v.host_value();
1519 Value::UserData(AnyUserData {
1520 root: lua.root_raw_in_state(state, RawLuaValue::UserData(v)),
1521 host_value,
1522 })
1523 }
1524 RawLuaValue::LightUserData(v) => Value::LightUserData(v),
1525 RawLuaValue::Thread(v) => Value::Thread(Thread {
1526 root: lua.root_raw_in_state(state, RawLuaValue::Thread(v)),
1527 }),
1528 })
1529 }
1530
1531 fn to_raw_for_lua(&self, lua: &Lua, state: &LuaState) -> Result<RawLuaValue> {
1532 match self {
1533 Value::Nil => Ok(RawLuaValue::Nil),
1534 Value::Boolean(v) => Ok(RawLuaValue::Bool(*v)),
1535 Value::Integer(v) => Ok(RawLuaValue::Int(*v)),
1536 Value::Number(v) => Ok(RawLuaValue::Float(*v)),
1537 Value::String(v) => v.root.raw_for_lua(lua, state),
1538 Value::Table(v) => v.root.raw_for_lua(lua, state),
1539 Value::Function(v) => v.root.raw_for_lua(lua, state),
1540 Value::UserData(v) => v.root.raw_for_lua(lua, state),
1541 Value::LightUserData(v) => Ok(RawLuaValue::LightUserData(*v)),
1542 Value::Thread(v) => v.root.raw_for_lua(lua, state),
1543 }
1544 }
1545}
1546
1547#[derive(Debug, Clone)]
1548pub struct Table {
1549 root: RootedValue,
1550}
1551
1552impl Table {
1553 fn raw_table(&self) -> Result<GcRef<RawLuaTable>> {
1554 match self.root.raw()? {
1555 RawLuaValue::Table(table) => Ok(table),
1556 other => Err(type_error_raw(&other, "table")),
1557 }
1558 }
1559
1560 pub fn get<K, V>(&self, key: K) -> Result<V>
1561 where
1562 K: IntoLua,
1563 V: FromLua,
1564 {
1565 let lua = self.root.lua.clone();
1566 let key = key.into_lua(&lua)?;
1567 let value_raw = lua.with_state(|state| {
1568 let key_raw = key.to_raw_for_lua(&lua, state)?;
1569 let table_raw = self.root.raw_for_lua(&lua, state)?;
1570 state.table_get_with_tm(&table_raw, &key_raw)
1571 })?;
1572 let value = Value::from_raw(&lua, value_raw)?;
1573 V::from_lua(value, &lua)
1574 }
1575
1576 pub fn set<K, V>(&self, key: K, value: V) -> Result<()>
1577 where
1578 K: IntoLua,
1579 V: IntoLua,
1580 {
1581 let lua = self.root.lua.clone();
1582 let key = key.into_lua(&lua)?;
1583 let value = value.into_lua(&lua)?;
1584 lua.with_state(|state| {
1585 let key_raw = key.to_raw_for_lua(&lua, state)?;
1586 let value_raw = value.to_raw_for_lua(&lua, state)?;
1587 let table_raw = self.root.raw_for_lua(&lua, state)?;
1588 state.table_set_with_tm(&table_raw, key_raw, value_raw)
1589 })
1590 }
1591
1592 pub fn len(&self) -> Result<u64> {
1593 Ok(self.raw_table()?.getn())
1594 }
1595}
1596
1597#[derive(Debug, Clone)]
1598pub struct Function {
1599 root: RootedValue,
1600}
1601
1602impl Function {
1603 pub fn call<A, R>(&self, args: A) -> Result<R>
1604 where
1605 A: IntoLuaMulti,
1606 R: FromLuaMulti,
1607 {
1608 let lua = self.root.lua.clone();
1609 let args = args.into_lua_multi(&lua)?;
1610 let result_raws = lua.with_state(|state| {
1611 let arg_raws = args
1612 .iter()
1613 .map(|value| value.to_raw_for_lua(&lua, state))
1614 .collect::<Result<Vec<_>>>()?;
1615 let function_raw = self.root.raw_for_lua(&lua, state)?;
1616 let saved_top = state.top_idx();
1617 state.push(function_raw);
1618 for arg in &arg_raws {
1619 state.push(*arg);
1620 }
1621 match lua_vm::api::pcall_k(state, arg_raws.len() as i32, R::NRESULTS, 0, 0, None) {
1622 Ok(_) => {
1623 let nresults = if R::NRESULTS < 0 {
1624 state.top_idx().0.saturating_sub(saved_top.0) as i32
1625 } else {
1626 R::NRESULTS
1627 };
1628 let mut results = Vec::with_capacity(nresults as usize);
1629 for _ in 0..nresults {
1630 results.push(state.pop());
1631 }
1632 results.reverse();
1633 state.set_top_idx(saved_top);
1634 Ok(results)
1635 }
1636 Err(err) => {
1637 state.set_top_idx(saved_top);
1638 Err(err)
1639 }
1640 }
1641 })?;
1642 let values = result_raws
1643 .into_iter()
1644 .map(|raw| Value::from_raw(&lua, raw))
1645 .collect::<Result<Vec<_>>>()?;
1646 R::from_lua_multi(values, &lua)
1647 }
1648}
1649
1650#[derive(Debug, Clone)]
1651pub struct LuaString {
1652 root: RootedValue,
1653}
1654
1655impl LuaString {
1656 fn raw_string(&self) -> Result<GcRef<RawLuaString>> {
1657 match self.root.raw()? {
1658 RawLuaValue::Str(string) => Ok(string),
1659 other => Err(type_error_raw(&other, "string")),
1660 }
1661 }
1662
1663 pub fn as_bytes(&self) -> Result<Vec<u8>> {
1664 Ok(self.raw_string()?.as_bytes().to_vec())
1665 }
1666
1667 pub fn to_str(&self) -> Result<String> {
1668 let bytes = self.as_bytes()?;
1669 String::from_utf8(bytes)
1670 .map_err(|_| LuaError::runtime(format_args!("string is not valid UTF-8")))
1671 }
1672}
1673
1674#[derive(Clone)]
1675pub struct AnyUserData {
1676 root: RootedValue,
1677 host_value: Option<Rc<dyn Any>>,
1678}
1679
1680impl fmt::Debug for AnyUserData {
1681 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1682 f.debug_struct("AnyUserData")
1683 .field("root", &self.root)
1684 .field("has_host_value", &self.host_value.is_some())
1685 .finish()
1686 }
1687}
1688
1689impl AnyUserData {
1690 fn host_cell<T: 'static>(&self) -> Result<&UserDataCell<T>> {
1691 let host = self
1692 .host_value
1693 .as_deref()
1694 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust userdata payload")))?;
1695 host.downcast_ref::<UserDataCell<T>>()
1696 .ok_or_else(|| LuaError::runtime(format_args!("userdata type mismatch")))
1697 }
1698
1699 pub fn borrow<T>(&self) -> Result<Ref<'_, T>>
1700 where
1701 T: 'static,
1702 {
1703 self.host_cell::<T>()?
1704 .value
1705 .try_borrow()
1706 .map_err(|_| LuaError::runtime(format_args!("userdata is already mutably borrowed")))
1707 }
1708
1709 pub fn borrow_mut<T>(&self) -> Result<RefMut<'_, T>>
1710 where
1711 T: 'static,
1712 {
1713 self.host_cell::<T>()?
1714 .value
1715 .try_borrow_mut()
1716 .map_err(|_| LuaError::runtime(format_args!("userdata is already borrowed")))
1717 }
1718
1719 pub fn with_borrow<T, R>(&self, f: impl FnOnce(&T) -> R) -> Result<R>
1720 where
1721 T: 'static,
1722 {
1723 let value = self.borrow::<T>()?;
1724 Ok(f(&value))
1725 }
1726
1727 pub fn with_borrow_mut<T, R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R>
1728 where
1729 T: 'static,
1730 {
1731 let mut value = self.borrow_mut::<T>()?;
1732 Ok(f(&mut value))
1733 }
1734
1735 fn host_scoped_cell<T: 'static>(&self) -> Result<&ScopedCell<T>> {
1738 let host = self
1739 .host_value
1740 .as_deref()
1741 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust userdata payload")))?;
1742 host.downcast_ref::<ScopedCell<T>>()
1743 .ok_or_else(|| LuaError::runtime(format_args!("scoped userdata type mismatch")))
1744 }
1745
1746 pub fn scoped_borrow<T, R>(&self, f: impl FnOnce(&T) -> R) -> Result<R>
1751 where
1752 T: 'static,
1753 {
1754 let cell = self.host_scoped_cell::<T>()?;
1755 let guard = cell.try_borrow()?;
1756 Ok(f(&*guard))
1757 }
1758
1759 pub fn scoped_borrow_mut<T, R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R>
1762 where
1763 T: 'static,
1764 {
1765 let cell = self.host_scoped_cell::<T>()?;
1766 let mut guard = cell.try_borrow_mut()?;
1767 Ok(f(&mut *guard))
1768 }
1769
1770 pub fn delegate<P, S, F>(&self, lua: &Lua, accessor: F) -> Result<AnyUserData>
1785 where
1786 P: UserData,
1787 S: UserData,
1788 F: Fn(&mut P) -> &mut S + 'static,
1789 {
1790 let host = self
1791 .host_value
1792 .as_ref()
1793 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust userdata payload")))?;
1794
1795 if let Ok(parent_cell) = Rc::clone(host).downcast::<ScopedCell<P>>() {
1799 let parent_for_closure = Rc::clone(&parent_cell);
1800 let enter: Box<dyn Fn(&mut dyn FnMut(&mut S)) -> Result<()>> =
1801 Box::new(move |f| {
1802 let mut guard = parent_for_closure.try_borrow_mut()?;
1803 f(accessor(&mut *guard));
1804 Ok(())
1805 });
1806 let cell = Rc::new(DelegatedCell::<S> {
1807 enter_mut: RefCell::new(Some(enter)),
1808 });
1809 return lua.create_delegated_userdata::<S>(cell);
1810 }
1811
1812 if let Ok(parent_cell) = Rc::clone(host).downcast::<DelegatedCell<P>>() {
1813 let parent_for_closure = Rc::clone(&parent_cell);
1814 let enter: Box<dyn Fn(&mut dyn FnMut(&mut S)) -> Result<()>> =
1815 Box::new(move |f| {
1816 parent_for_closure.enter_mut(&mut |p| {
1817 f(accessor(p));
1818 })
1819 });
1820 let cell = Rc::new(DelegatedCell::<S> {
1821 enter_mut: RefCell::new(Some(enter)),
1822 });
1823 return lua.create_delegated_userdata::<S>(cell);
1824 }
1825
1826 Err(LuaError::runtime(format_args!(
1827 "delegate: receiver is not a scoped userdata of the expected type"
1828 )))
1829 }
1830}
1831
1832#[derive(Debug, Clone)]
1833pub struct Thread {
1834 root: RootedValue,
1835}
1836
1837#[derive(Debug, Clone, Default, PartialEq, Eq)]
1843pub struct Variadic<T>(Vec<T>);
1844
1845impl<T> Variadic<T> {
1846 pub const fn new() -> Self {
1847 Self(Vec::new())
1848 }
1849
1850 pub fn with_capacity(capacity: usize) -> Self {
1851 Self(Vec::with_capacity(capacity))
1852 }
1853
1854 pub fn into_vec(self) -> Vec<T> {
1855 self.0
1856 }
1857}
1858
1859impl<T> Deref for Variadic<T> {
1860 type Target = Vec<T>;
1861
1862 fn deref(&self) -> &Self::Target {
1863 &self.0
1864 }
1865}
1866
1867impl<T> DerefMut for Variadic<T> {
1868 fn deref_mut(&mut self) -> &mut Self::Target {
1869 &mut self.0
1870 }
1871}
1872
1873impl<T> From<Vec<T>> for Variadic<T> {
1874 fn from(value: Vec<T>) -> Self {
1875 Self(value)
1876 }
1877}
1878
1879impl<T> From<Variadic<T>> for Vec<T> {
1880 fn from(value: Variadic<T>) -> Self {
1881 value.0
1882 }
1883}
1884
1885impl<T> FromIterator<T> for Variadic<T> {
1886 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
1887 Self(Vec::from_iter(iter))
1888 }
1889}
1890
1891impl<T> IntoIterator for Variadic<T> {
1892 type Item = T;
1893 type IntoIter = std::vec::IntoIter<T>;
1894
1895 fn into_iter(self) -> Self::IntoIter {
1896 self.0.into_iter()
1897 }
1898}
1899
1900pub trait UserData: 'static {
1901 fn add_methods<M: UserDataMethods<Self>>(_methods: &mut M)
1902 where
1903 Self: Sized,
1904 {
1905 }
1906
1907 fn add_meta_methods<M: UserDataMethods<Self>>(_methods: &mut M)
1908 where
1909 Self: Sized,
1910 {
1911 }
1912}
1913
1914pub trait UserDataMethods<T: UserData> {
1915 fn add_method<A, R, F>(&mut self, name: &str, method: F)
1916 where
1917 A: FromLuaMulti + 'static,
1918 R: IntoLuaMulti + 'static,
1919 F: Fn(&Lua, &T, A) -> Result<R> + 'static;
1920
1921 fn add_method_mut<A, R, F>(&mut self, name: &str, method: F)
1922 where
1923 A: FromLuaMulti + 'static,
1924 R: IntoLuaMulti + 'static,
1925 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static;
1926
1927 fn add_meta_method<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
1928 where
1929 A: FromLuaMulti + 'static,
1930 R: IntoLuaMulti + 'static,
1931 F: Fn(&Lua, &T, A) -> Result<R> + 'static;
1932
1933 fn add_meta_method_mut<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
1934 where
1935 A: FromLuaMulti + 'static,
1936 R: IntoLuaMulti + 'static,
1937 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static;
1938
1939 fn add_field_method_get<R, F>(&mut self, name: &str, getter: F)
1943 where
1944 R: IntoLuaMulti + 'static,
1945 F: Fn(&Lua, &T) -> Result<R> + 'static;
1946
1947 fn add_field_method_set<A, F>(&mut self, name: &str, setter: F)
1950 where
1951 A: FromLuaMulti + 'static,
1952 F: Fn(&Lua, &mut T, A) -> Result<()> + 'static;
1953
1954 fn add_function<A, R, F>(&mut self, name: &str, function: F)
1964 where
1965 A: FromLuaMulti + 'static,
1966 R: IntoLuaMulti + 'static,
1967 F: Fn(&Lua, A) -> Result<R> + 'static;
1968
1969 fn add_function_mut<A, R, F>(&mut self, name: &str, function: F)
1972 where
1973 A: FromLuaMulti + 'static,
1974 R: IntoLuaMulti + 'static,
1975 F: FnMut(&Lua, A) -> Result<R> + 'static;
1976}
1977
1978#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1979pub enum MetaMethod {
1980 Index,
1981 NewIndex,
1982 Add,
1983 Sub,
1984 Mul,
1985 Div,
1986 Mod,
1987 Pow,
1988 Unm,
1989 Len,
1990 Eq,
1991 Lt,
1992 Le,
1993 Concat,
1994 Call,
1995 ToString,
1996 Pairs,
1997}
1998
1999impl MetaMethod {
2000 fn name(self) -> &'static str {
2001 match self {
2002 MetaMethod::Index => "__index",
2003 MetaMethod::NewIndex => "__newindex",
2004 MetaMethod::Add => "__add",
2005 MetaMethod::Sub => "__sub",
2006 MetaMethod::Mul => "__mul",
2007 MetaMethod::Div => "__div",
2008 MetaMethod::Mod => "__mod",
2009 MetaMethod::Pow => "__pow",
2010 MetaMethod::Unm => "__unm",
2011 MetaMethod::Len => "__len",
2012 MetaMethod::Eq => "__eq",
2013 MetaMethod::Lt => "__lt",
2014 MetaMethod::Le => "__le",
2015 MetaMethod::Concat => "__concat",
2016 MetaMethod::Call => "__call",
2017 MetaMethod::ToString => "__tostring",
2018 MetaMethod::Pairs => "__pairs",
2019 }
2020 }
2021}
2022
2023fn root_for_state_lifetime(state: &mut LuaState, value: RawLuaValue) {
2034 let _ = state.external_root_value(value);
2035}
2036
2037#[derive(Clone, Copy)]
2042enum RegistryMode {
2043 Owned,
2044 Scoped,
2045}
2046
2047struct UserDataMethodRegistry<'lua, T: UserData> {
2048 lua: &'lua Lua,
2049 mode: RegistryMode,
2050 methods: Vec<(String, Function)>,
2051 meta_methods: Vec<(MetaMethod, Function)>,
2052 fields_get: Vec<(String, Function)>,
2053 fields_set: Vec<(String, Function)>,
2054 error: Option<LuaError>,
2055 _marker: std::marker::PhantomData<T>,
2056}
2057
2058impl<'lua, T: UserData> UserDataMethodRegistry<'lua, T> {
2059 fn new(lua: &'lua Lua) -> Self {
2060 Self::with_mode(lua, RegistryMode::Owned)
2061 }
2062
2063 fn new_scoped(lua: &'lua Lua) -> Self {
2064 Self::with_mode(lua, RegistryMode::Scoped)
2065 }
2066
2067 fn with_mode(lua: &'lua Lua, mode: RegistryMode) -> Self {
2068 Self {
2069 lua,
2070 mode,
2071 methods: Vec::new(),
2072 meta_methods: Vec::new(),
2073 fields_get: Vec::new(),
2074 fields_set: Vec::new(),
2075 error: None,
2076 _marker: std::marker::PhantomData,
2077 }
2078 }
2079
2080 fn record(&mut self, result: Result<Function>, insert: impl FnOnce(&mut Self, Function)) {
2081 if self.error.is_some() {
2082 return;
2083 }
2084 match result {
2085 Ok(function) => insert(self, function),
2086 Err(err) => self.error = Some(err),
2087 }
2088 }
2089
2090 fn build_metatable(mut self) -> Result<GcRef<RawLuaTable>> {
2094 if let Some(err) = self.error.take() {
2095 return Err(err);
2096 }
2097
2098 let lua = self.lua;
2099
2100 let method_table = lua.create_table()?;
2101 for (name, function) in &self.methods {
2102 method_table.set(name.as_str(), function)?;
2103 }
2104
2105 let field_getters = lua.create_table()?;
2106 for (name, function) in &self.fields_get {
2107 field_getters.set(name.as_str(), function)?;
2108 }
2109 let field_setters = lua.create_table()?;
2110 for (name, function) in &self.fields_set {
2111 field_setters.set(name.as_str(), function)?;
2112 }
2113
2114 let metatable = lua.create_table()?;
2117 let mut raw_index: Option<Function> = None;
2118 let mut raw_newindex: Option<Function> = None;
2119 for (metamethod, function) in &self.meta_methods {
2120 match metamethod {
2121 MetaMethod::Index => raw_index = Some(function.clone()),
2122 MetaMethod::NewIndex => raw_newindex = Some(function.clone()),
2123 other => {
2124 metatable.set(other.name(), function)?;
2125 }
2126 }
2127 }
2128
2129 let has_fields_get = !self.fields_get.is_empty();
2147 let has_methods = !self.methods.is_empty();
2148 let needs_index_composition = has_fields_get || (raw_index.is_some() && has_methods);
2149
2150 if needs_index_composition {
2151 let (getters_raw, methods_raw, raw_index_raw) = lua.with_state(|state| {
2152 let g = match field_getters.root.raw_for_lua(lua, state)? {
2153 RawLuaValue::Table(g) => g,
2154 v => return Err(type_error_raw(&v, "table")),
2155 };
2156 root_for_state_lifetime(state, RawLuaValue::Table(g.clone()));
2157 let m = match method_table.root.raw_for_lua(lua, state)? {
2158 RawLuaValue::Table(m) => m,
2159 v => return Err(type_error_raw(&v, "table")),
2160 };
2161 root_for_state_lifetime(state, RawLuaValue::Table(m.clone()));
2162 let r = match &raw_index {
2163 Some(f) => {
2164 let rv = f.root.raw_for_lua(lua, state)?;
2165 root_for_state_lifetime(state, rv.clone());
2166 Some(rv)
2167 }
2168 None => None,
2169 };
2170 Ok::<_, LuaError>((g, m, r))
2171 })?;
2172 let index_fn = lua.create_function(move |lua, (ud, key): (Value, Value)| {
2173 let getters = Table {
2174 root: lua.root_raw(RawLuaValue::Table(getters_raw.clone())),
2175 };
2176 let methods = Table {
2177 root: lua.root_raw(RawLuaValue::Table(methods_raw.clone())),
2178 };
2179 if let Value::Function(getter) = getters.get::<_, Value>(key.clone())? {
2180 return getter.call::<_, Value>(ud);
2181 }
2182 let method = methods.get::<_, Value>(key.clone())?;
2183 if !matches!(method, Value::Nil) {
2184 return Ok(method);
2185 }
2186 if let Some(raw_idx) = &raw_index_raw {
2187 let raw_fn = Function {
2188 root: lua.root_raw(raw_idx.clone()),
2189 };
2190 return raw_fn.call::<_, Value>((ud, key));
2191 }
2192 Ok(Value::Nil)
2193 })?;
2194 metatable.set(MetaMethod::Index.name(), &index_fn)?;
2195 } else if let Some(raw) = raw_index.as_ref() {
2196 metatable.set(MetaMethod::Index.name(), raw)?;
2197 } else {
2198 metatable.set(MetaMethod::Index.name(), &method_table)?;
2199 }
2200
2201 let has_fields_set = !self.fields_set.is_empty();
2204
2205 if has_fields_set {
2206 let (setters_raw, raw_newindex_raw) = lua.with_state(|state| {
2207 let s = match field_setters.root.raw_for_lua(lua, state)? {
2208 RawLuaValue::Table(s) => s,
2209 v => return Err(type_error_raw(&v, "table")),
2210 };
2211 root_for_state_lifetime(state, RawLuaValue::Table(s.clone()));
2212 let r = match &raw_newindex {
2213 Some(f) => {
2214 let rv = f.root.raw_for_lua(lua, state)?;
2215 root_for_state_lifetime(state, rv.clone());
2216 Some(rv)
2217 }
2218 None => None,
2219 };
2220 Ok::<_, LuaError>((s, r))
2221 })?;
2222 let newindex_fn =
2223 lua.create_function(move |lua, (ud, key, value): (Value, Value, Value)| {
2224 let setters = Table {
2225 root: lua.root_raw(RawLuaValue::Table(setters_raw.clone())),
2226 };
2227 if let Value::Function(setter) = setters.get::<_, Value>(key.clone())? {
2228 return setter.call::<_, Value>((ud, value));
2229 }
2230 if let Some(raw) = &raw_newindex_raw {
2231 let raw_fn = Function {
2232 root: lua.root_raw(raw.clone()),
2233 };
2234 return raw_fn.call::<_, Value>((ud, key, value));
2235 }
2236 Err(LuaError::runtime(format_args!(
2237 "cannot assign to unknown or read-only userdata field"
2238 )))
2239 })?;
2240 metatable.set(MetaMethod::NewIndex.name(), &newindex_fn)?;
2241 } else if let Some(raw) = raw_newindex.as_ref() {
2242 metatable.set(MetaMethod::NewIndex.name(), raw)?;
2243 }
2244
2245 self.lua.with_state(|state| {
2246 let metatable_raw = metatable.root.raw_for_lua(self.lua, state)?;
2247 let RawLuaValue::Table(metatable) = metatable_raw else {
2248 return Err(type_error_raw(&metatable_raw, "table"));
2249 };
2250 root_for_state_lifetime(state, RawLuaValue::Table(metatable.clone()));
2251 Ok(metatable)
2252 })
2253 }
2254}
2255
2256impl<T: UserData> UserDataMethods<T> for UserDataMethodRegistry<'_, T> {
2257 fn add_method<A, R, F>(&mut self, name: &str, method: F)
2258 where
2259 A: FromLuaMulti + 'static,
2260 R: IntoLuaMulti + 'static,
2261 F: Fn(&Lua, &T, A) -> Result<R> + 'static,
2262 {
2263 let name = name.to_string();
2264 let result = match self.mode {
2265 RegistryMode::Owned => self.lua.create_userdata_method(method),
2266 RegistryMode::Scoped => self.lua.create_scoped_userdata_method(method),
2267 };
2268 self.record(result, move |this, function| {
2269 this.methods.push((name, function));
2270 });
2271 }
2272
2273 fn add_method_mut<A, R, F>(&mut self, name: &str, method: F)
2274 where
2275 A: FromLuaMulti + 'static,
2276 R: IntoLuaMulti + 'static,
2277 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
2278 {
2279 let name = name.to_string();
2280 let result = match self.mode {
2281 RegistryMode::Owned => self.lua.create_userdata_method_mut(method),
2282 RegistryMode::Scoped => self.lua.create_scoped_userdata_method_mut(method),
2283 };
2284 self.record(result, move |this, function| {
2285 this.methods.push((name, function));
2286 });
2287 }
2288
2289 fn add_meta_method<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
2290 where
2291 A: FromLuaMulti + 'static,
2292 R: IntoLuaMulti + 'static,
2293 F: Fn(&Lua, &T, A) -> Result<R> + 'static,
2294 {
2295 let result = match self.mode {
2296 RegistryMode::Owned => self.lua.create_userdata_method(method),
2297 RegistryMode::Scoped => self.lua.create_scoped_userdata_method(method),
2298 };
2299 self.record(result, move |this, function| {
2300 this.meta_methods.push((metamethod, function));
2301 });
2302 }
2303
2304 fn add_meta_method_mut<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
2305 where
2306 A: FromLuaMulti + 'static,
2307 R: IntoLuaMulti + 'static,
2308 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
2309 {
2310 let result = match self.mode {
2311 RegistryMode::Owned => self.lua.create_userdata_method_mut(method),
2312 RegistryMode::Scoped => self.lua.create_scoped_userdata_method_mut(method),
2313 };
2314 self.record(result, move |this, function| {
2315 this.meta_methods.push((metamethod, function));
2316 });
2317 }
2318
2319 fn add_field_method_get<R, F>(&mut self, name: &str, getter: F)
2320 where
2321 R: IntoLuaMulti + 'static,
2322 F: Fn(&Lua, &T) -> Result<R> + 'static,
2323 {
2324 let name = name.to_string();
2325 let wrapped = move |lua: &Lua, this: &T, ()| getter(lua, this);
2326 let result = match self.mode {
2327 RegistryMode::Owned => self.lua.create_userdata_method(wrapped),
2328 RegistryMode::Scoped => self.lua.create_scoped_userdata_method(wrapped),
2329 };
2330 self.record(result, move |this, function| {
2331 this.fields_get.push((name, function));
2332 });
2333 }
2334
2335 fn add_field_method_set<A, F>(&mut self, name: &str, setter: F)
2336 where
2337 A: FromLuaMulti + 'static,
2338 F: Fn(&Lua, &mut T, A) -> Result<()> + 'static,
2339 {
2340 let name = name.to_string();
2341 let wrapped = move |lua: &Lua, this: &mut T, arg: A| setter(lua, this, arg);
2342 let result = match self.mode {
2343 RegistryMode::Owned => self.lua.create_userdata_method_mut(wrapped),
2344 RegistryMode::Scoped => self.lua.create_scoped_userdata_method_mut(wrapped),
2345 };
2346 self.record(result, move |this, function| {
2347 this.fields_set.push((name, function));
2348 });
2349 }
2350
2351 fn add_function<A, R, F>(&mut self, name: &str, function: F)
2352 where
2353 A: FromLuaMulti + 'static,
2354 R: IntoLuaMulti + 'static,
2355 F: Fn(&Lua, A) -> Result<R> + 'static,
2356 {
2357 let name = name.to_string();
2358 let result = self.lua.create_function(function);
2362 self.record(result, move |this, function| {
2363 this.methods.push((name, function));
2364 });
2365 }
2366
2367 fn add_function_mut<A, R, F>(&mut self, name: &str, function: F)
2368 where
2369 A: FromLuaMulti + 'static,
2370 R: IntoLuaMulti + 'static,
2371 F: FnMut(&Lua, A) -> Result<R> + 'static,
2372 {
2373 let name = name.to_string();
2374 let result = self.lua.create_function_mut(function);
2375 self.record(result, move |this, function| {
2376 this.methods.push((name, function));
2377 });
2378 }
2379}
2380
2381pub trait IntoLua {
2382 fn into_lua(self, lua: &Lua) -> Result<Value>;
2383}
2384
2385pub trait FromLua: Sized {
2386 fn from_lua(value: Value, lua: &Lua) -> Result<Self>;
2387}
2388
2389pub trait IntoLuaMulti {
2390 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>>;
2391}
2392
2393pub trait FromLuaMulti: Sized {
2394 const NRESULTS: i32;
2395
2396 fn from_lua_multi(values: Vec<Value>, lua: &Lua) -> Result<Self>;
2397}
2398
2399impl IntoLua for Value {
2400 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2401 Ok(self)
2402 }
2403}
2404
2405impl IntoLua for &Value {
2406 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2407 Ok(self.clone())
2408 }
2409}
2410
2411impl FromLua for Value {
2412 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2413 Ok(value)
2414 }
2415}
2416
2417impl IntoLua for bool {
2418 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2419 Ok(Value::Boolean(self))
2420 }
2421}
2422
2423impl FromLua for bool {
2424 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2425 match value {
2426 Value::Boolean(v) => Ok(v),
2427 other => Err(type_error_value(&other, "boolean")),
2428 }
2429 }
2430}
2431
2432impl IntoLua for i64 {
2433 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2434 Ok(Value::Integer(self))
2435 }
2436}
2437
2438impl FromLua for i64 {
2439 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2440 match value {
2441 Value::Integer(v) => Ok(v),
2442 Value::Number(v) if v.fract() == 0.0 && v.is_finite() => Ok(v as i64),
2443 other => Err(type_error_value(&other, "integer")),
2444 }
2445 }
2446}
2447
2448impl IntoLua for i32 {
2449 fn into_lua(self, lua: &Lua) -> Result<Value> {
2450 i64::from(self).into_lua(lua)
2451 }
2452}
2453
2454impl FromLua for i32 {
2455 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
2456 let v = i64::from_lua(value, lua)?;
2457 i32::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
2458 }
2459}
2460
2461impl IntoLua for usize {
2462 fn into_lua(self, lua: &Lua) -> Result<Value> {
2463 let v = i64::try_from(self)
2464 .map_err(|_| LuaError::runtime(format_args!("integer out of range")))?;
2465 v.into_lua(lua)
2466 }
2467}
2468
2469impl FromLua for usize {
2470 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
2471 let v = i64::from_lua(value, lua)?;
2472 usize::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
2473 }
2474}
2475
2476impl IntoLua for u64 {
2477 fn into_lua(self, lua: &Lua) -> Result<Value> {
2478 let v = i64::try_from(self)
2479 .map_err(|_| LuaError::runtime(format_args!("integer out of range")))?;
2480 v.into_lua(lua)
2481 }
2482}
2483
2484impl FromLua for u64 {
2485 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
2486 let v = i64::from_lua(value, lua)?;
2487 u64::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
2488 }
2489}
2490
2491impl IntoLua for u32 {
2492 fn into_lua(self, lua: &Lua) -> Result<Value> {
2493 u64::from(self).into_lua(lua)
2494 }
2495}
2496
2497impl FromLua for u32 {
2498 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
2499 let v = u64::from_lua(value, lua)?;
2500 u32::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
2501 }
2502}
2503
2504impl IntoLua for f64 {
2505 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2506 Ok(Value::Number(self))
2507 }
2508}
2509
2510impl FromLua for f64 {
2511 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2512 match value {
2513 Value::Integer(v) => Ok(v as f64),
2514 Value::Number(v) => Ok(v),
2515 other => Err(type_error_value(&other, "number")),
2516 }
2517 }
2518}
2519
2520impl IntoLua for &str {
2521 fn into_lua(self, lua: &Lua) -> Result<Value> {
2522 Ok(Value::String(lua.create_string(self.as_bytes())?))
2523 }
2524}
2525
2526impl IntoLua for String {
2527 fn into_lua(self, lua: &Lua) -> Result<Value> {
2528 Ok(Value::String(lua.create_string(self.into_bytes())?))
2529 }
2530}
2531
2532impl FromLua for String {
2533 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2534 match value {
2535 Value::String(s) => s.to_str(),
2536 other => Err(type_error_value(&other, "string")),
2537 }
2538 }
2539}
2540
2541impl IntoLua for &[u8] {
2542 fn into_lua(self, lua: &Lua) -> Result<Value> {
2543 Ok(Value::String(lua.create_string(self)?))
2544 }
2545}
2546
2547impl IntoLua for LuaString {
2548 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2549 Ok(Value::String(self))
2550 }
2551}
2552
2553impl IntoLua for &LuaString {
2554 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2555 Ok(Value::String(self.clone()))
2556 }
2557}
2558
2559impl FromLua for LuaString {
2560 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2561 match value {
2562 Value::String(v) => Ok(v),
2563 other => Err(type_error_value(&other, "string")),
2564 }
2565 }
2566}
2567
2568impl IntoLua for Table {
2569 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2570 Ok(Value::Table(self))
2571 }
2572}
2573
2574impl IntoLua for &Table {
2575 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2576 Ok(Value::Table(self.clone()))
2577 }
2578}
2579
2580impl FromLua for Table {
2581 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2582 match value {
2583 Value::Table(v) => Ok(v),
2584 other => Err(type_error_value(&other, "table")),
2585 }
2586 }
2587}
2588
2589impl IntoLua for Function {
2590 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2591 Ok(Value::Function(self))
2592 }
2593}
2594
2595impl IntoLua for &Function {
2596 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2597 Ok(Value::Function(self.clone()))
2598 }
2599}
2600
2601impl FromLua for Function {
2602 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2603 match value {
2604 Value::Function(v) => Ok(v),
2605 other => Err(type_error_value(&other, "function")),
2606 }
2607 }
2608}
2609
2610impl IntoLua for AnyUserData {
2611 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2612 Ok(Value::UserData(self))
2613 }
2614}
2615
2616impl IntoLua for &AnyUserData {
2617 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2618 Ok(Value::UserData(self.clone()))
2619 }
2620}
2621
2622impl FromLua for AnyUserData {
2623 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2624 match value {
2625 Value::UserData(v) => Ok(v),
2626 other => Err(type_error_value(&other, "userdata")),
2627 }
2628 }
2629}
2630
2631impl<T> IntoLua for T
2632where
2633 T: UserData,
2634{
2635 fn into_lua(self, lua: &Lua) -> Result<Value> {
2636 Ok(Value::UserData(lua.create_userdata(self)?))
2637 }
2638}
2639
2640impl<T> IntoLua for Option<T>
2641where
2642 T: IntoLua,
2643{
2644 fn into_lua(self, lua: &Lua) -> Result<Value> {
2645 match self {
2646 Some(value) => value.into_lua(lua),
2647 None => Ok(Value::Nil),
2648 }
2649 }
2650}
2651
2652impl<T> FromLua for Option<T>
2653where
2654 T: FromLua,
2655{
2656 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
2657 match value {
2658 Value::Nil => Ok(None),
2659 other => T::from_lua(other, lua).map(Some),
2660 }
2661 }
2662}
2663
2664impl<T> IntoLua for Vec<T>
2665where
2666 T: IntoLua,
2667{
2668 fn into_lua(self, lua: &Lua) -> Result<Value> {
2669 let table = lua.create_table()?;
2670 for (idx, value) in self.into_iter().enumerate() {
2671 table.set((idx + 1) as i64, value)?;
2672 }
2673 Ok(Value::Table(table))
2674 }
2675}
2676
2677impl<T> FromLua for Vec<T>
2678where
2679 T: FromLua,
2680{
2681 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
2682 let table = Table::from_lua(value, lua)?;
2683 let raw = table.raw_table()?;
2684 let len = raw.getn();
2685 let mut out = Vec::with_capacity(len as usize);
2686 for idx in 1..=len {
2687 let value = Value::from_raw(lua, raw.get_int(idx as i64))?;
2688 out.push(T::from_lua(value, lua)?);
2689 }
2690 Ok(out)
2691 }
2692}
2693
2694impl<K, V> IntoLua for HashMap<K, V>
2695where
2696 K: IntoLua,
2697 V: IntoLua,
2698{
2699 fn into_lua(self, lua: &Lua) -> Result<Value> {
2700 let table = lua.create_table()?;
2701 for (key, value) in self {
2702 table.set(key, value)?;
2703 }
2704 Ok(Value::Table(table))
2705 }
2706}
2707
2708impl<K, V> FromLua for HashMap<K, V>
2709where
2710 K: FromLua + Eq + Hash,
2711 V: FromLua,
2712{
2713 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
2714 let table = Table::from_lua(value, lua)?;
2715 let raw = table.raw_table()?;
2716 let mut out = HashMap::new();
2717 let mut result = Ok(());
2718 raw.for_each_entry(|key, value| {
2719 if result.is_err() {
2720 return;
2721 }
2722 result = (|| {
2723 let key = Value::from_raw(lua, *key)?;
2724 let value = Value::from_raw(lua, *value)?;
2725 out.insert(K::from_lua(key, lua)?, V::from_lua(value, lua)?);
2726 Ok(())
2727 })();
2728 });
2729 result?;
2730 Ok(out)
2731 }
2732}
2733
2734impl<T> IntoLuaMulti for Variadic<T>
2735where
2736 T: IntoLua,
2737{
2738 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
2739 self.into_iter().map(|value| value.into_lua(lua)).collect()
2740 }
2741}
2742
2743impl<T> FromLuaMulti for Variadic<T>
2744where
2745 T: FromLua,
2746{
2747 const NRESULTS: i32 = -1;
2748
2749 fn from_lua_multi(values: Vec<Value>, lua: &Lua) -> Result<Self> {
2750 values
2751 .into_iter()
2752 .map(|value| T::from_lua(value, lua))
2753 .collect()
2754 }
2755}
2756
2757impl IntoLuaMulti for () {
2758 fn into_lua_multi(self, _lua: &Lua) -> Result<Vec<Value>> {
2759 Ok(Vec::new())
2760 }
2761}
2762
2763impl<T> IntoLuaMulti for T
2764where
2765 T: IntoLua,
2766{
2767 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
2768 Ok(vec![self.into_lua(lua)?])
2769 }
2770}
2771
2772impl<A, B> IntoLuaMulti for (A, B)
2773where
2774 A: IntoLua,
2775 B: IntoLua,
2776{
2777 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
2778 Ok(vec![self.0.into_lua(lua)?, self.1.into_lua(lua)?])
2779 }
2780}
2781
2782impl<A, T> IntoLuaMulti for (A, Variadic<T>)
2783where
2784 A: IntoLua,
2785 T: IntoLua,
2786{
2787 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
2788 let mut values = vec![self.0.into_lua(lua)?];
2789 values.extend(self.1.into_lua_multi(lua)?);
2790 Ok(values)
2791 }
2792}
2793
2794impl<A, B, C> IntoLuaMulti for (A, B, C)
2795where
2796 A: IntoLua,
2797 B: IntoLua,
2798 C: IntoLua,
2799{
2800 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
2801 Ok(vec![
2802 self.0.into_lua(lua)?,
2803 self.1.into_lua(lua)?,
2804 self.2.into_lua(lua)?,
2805 ])
2806 }
2807}
2808
2809impl<A, B, T> IntoLuaMulti for (A, B, Variadic<T>)
2810where
2811 A: IntoLua,
2812 B: IntoLua,
2813 T: IntoLua,
2814{
2815 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
2816 let mut values = vec![self.0.into_lua(lua)?, self.1.into_lua(lua)?];
2817 values.extend(self.2.into_lua_multi(lua)?);
2818 Ok(values)
2819 }
2820}
2821
2822impl FromLuaMulti for () {
2823 const NRESULTS: i32 = 0;
2824
2825 fn from_lua_multi(_values: Vec<Value>, _lua: &Lua) -> Result<Self> {
2826 Ok(())
2827 }
2828}
2829
2830impl<T> FromLuaMulti for T
2831where
2832 T: FromLua,
2833{
2834 const NRESULTS: i32 = 1;
2835
2836 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
2837 let value = if values.is_empty() {
2838 Value::Nil
2839 } else {
2840 values.remove(0)
2841 };
2842 T::from_lua(value, lua)
2843 }
2844}
2845
2846impl<A, B> FromLuaMulti for (A, B)
2847where
2848 A: FromLua,
2849 B: FromLua,
2850{
2851 const NRESULTS: i32 = 2;
2852
2853 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
2854 let first = if values.is_empty() {
2855 Value::Nil
2856 } else {
2857 values.remove(0)
2858 };
2859 let second = if values.is_empty() {
2860 Value::Nil
2861 } else {
2862 values.remove(0)
2863 };
2864 Ok((A::from_lua(first, lua)?, B::from_lua(second, lua)?))
2865 }
2866}
2867
2868impl<A, T> FromLuaMulti for (A, Variadic<T>)
2869where
2870 A: FromLua,
2871 T: FromLua,
2872{
2873 const NRESULTS: i32 = -1;
2874
2875 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
2876 let first = if values.is_empty() {
2877 Value::Nil
2878 } else {
2879 values.remove(0)
2880 };
2881 Ok((
2882 A::from_lua(first, lua)?,
2883 Variadic::from_lua_multi(values, lua)?,
2884 ))
2885 }
2886}
2887
2888impl<A, B, C> FromLuaMulti for (A, B, C)
2889where
2890 A: FromLua,
2891 B: FromLua,
2892 C: FromLua,
2893{
2894 const NRESULTS: i32 = 3;
2895
2896 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
2897 let first = if values.is_empty() {
2898 Value::Nil
2899 } else {
2900 values.remove(0)
2901 };
2902 let second = if values.is_empty() {
2903 Value::Nil
2904 } else {
2905 values.remove(0)
2906 };
2907 let third = if values.is_empty() {
2908 Value::Nil
2909 } else {
2910 values.remove(0)
2911 };
2912 Ok((
2913 A::from_lua(first, lua)?,
2914 B::from_lua(second, lua)?,
2915 C::from_lua(third, lua)?,
2916 ))
2917 }
2918}
2919
2920impl<A, B, T> FromLuaMulti for (A, B, Variadic<T>)
2921where
2922 A: FromLua,
2923 B: FromLua,
2924 T: FromLua,
2925{
2926 const NRESULTS: i32 = -1;
2927
2928 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
2929 let first = if values.is_empty() {
2930 Value::Nil
2931 } else {
2932 values.remove(0)
2933 };
2934 let second = if values.is_empty() {
2935 Value::Nil
2936 } else {
2937 values.remove(0)
2938 };
2939 Ok((
2940 A::from_lua(first, lua)?,
2941 B::from_lua(second, lua)?,
2942 Variadic::from_lua_multi(values, lua)?,
2943 ))
2944 }
2945}
2946
2947fn rust_callback_trampoline(state: &mut LuaState) -> Result<usize> {
2948 let func_idx = state.current_call_info().func;
2949 let callback = match state.get_at(func_idx) {
2950 RawLuaValue::Function(RawLuaClosure::C(closure)) => {
2951 let Some(RawLuaValue::UserData(userdata)) = closure.upvalues.first() else {
2952 return Err(LuaError::runtime(format_args!(
2953 "missing Rust callback payload"
2954 )));
2955 };
2956 let host = userdata
2957 .host_value()
2958 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust callback payload")))?;
2959 host.downcast::<RustCallbackCell>().map_err(|_| {
2960 LuaError::runtime(format_args!("Rust callback payload type mismatch"))
2961 })?
2962 }
2963 _ => {
2964 return Err(LuaError::runtime(format_args!(
2965 "Rust callback trampoline called without C closure"
2966 )));
2967 }
2968 };
2969 (callback.function)(state)
2970}
2971
2972fn with_heap_guard<R>(state: &LuaState, f: impl FnOnce() -> R) -> R {
2973 let _heap_guard = heap_guard(state);
2974 f()
2975}
2976
2977fn heap_guard(state: &LuaState) -> lua_gc::HeapGuard {
2978 let global = state.global();
2979 lua_gc::HeapGuard::push(&global.heap)
2980}
2981
2982fn callback_args(state: &mut LuaState, lua: &Lua) -> Result<Vec<Value>> {
2983 let func_idx = state.current_call_info().func;
2984 let nargs = state.top_idx().0.saturating_sub(func_idx.0 + 1);
2985 let mut args = Vec::with_capacity(nargs as usize);
2986 for i in 0..nargs {
2987 let raw = state.get_at(func_idx + 1 + i as i32);
2988 args.push(Value::from_raw_in_state(lua, state, raw)?);
2989 }
2990 Ok(args)
2991}
2992
2993fn callback_userdata_args(state: &mut LuaState, lua: &Lua) -> Result<(AnyUserData, Vec<Value>)> {
2994 let mut args = callback_args(state, lua)?;
2995 if args.is_empty() {
2996 return Err(LuaError::runtime(format_args!(
2997 "userdata method missing self argument"
2998 )));
2999 }
3000 let userdata = AnyUserData::from_lua(args.remove(0), lua)?;
3001 Ok((userdata, args))
3002}
3003
3004fn push_callback_returns(state: &mut LuaState, lua: &Lua, returns: Vec<Value>) -> Result<usize> {
3005 let mut count = 0usize;
3006 for value in returns {
3007 let raw = value.to_raw_for_lua(lua, state)?;
3008 state.push(raw);
3009 count += 1;
3010 }
3011 Ok(count)
3012}
3013
3014fn stale_handle_error() -> LuaError {
3015 LuaError::runtime(format_args!("stale Lua handle"))
3016}
3017
3018fn type_error_raw(value: &RawLuaValue, expected: &str) -> LuaError {
3019 LuaError::runtime(format_args!(
3020 "{} expected, got {}",
3021 expected,
3022 value.type_name()
3023 ))
3024}
3025
3026fn type_error_value(value: &Value, expected: &str) -> LuaError {
3027 let got = match value {
3028 Value::Nil => "nil",
3029 Value::Boolean(_) => "boolean",
3030 Value::Integer(_) | Value::Number(_) => "number",
3031 Value::String(_) => "string",
3032 Value::Table(_) => "table",
3033 Value::Function(_) => "function",
3034 Value::UserData(_) | Value::LightUserData(_) => "userdata",
3035 Value::Thread(_) => "thread",
3036 };
3037 LuaError::runtime(format_args!("{} expected, got {}", expected, got))
3038}
3039
3040pub struct LuaRuntime {
3042 state: LuaState,
3043}
3044
3045impl LuaRuntime {
3046 pub fn new() -> Result<Self> {
3052 Self::with_hooks(HostHooks::default())
3053 }
3054
3055 pub fn with_hooks(hooks: HostHooks) -> Result<Self> {
3057 let mut state = new_state().ok_or(LuaError::Memory)?;
3058 install_parser_hook(&mut state);
3059 hooks.install(&mut state);
3060 open_libs(&mut state)?;
3061 Ok(Self { state })
3062 }
3063
3064 pub fn state(&self) -> &LuaState {
3065 &self.state
3066 }
3067
3068 pub fn state_mut(&mut self) -> &mut LuaState {
3069 &mut self.state
3070 }
3071
3072 pub fn into_state(self) -> LuaState {
3073 self.state
3074 }
3075
3076 pub fn into_lua(self) -> Lua {
3077 Lua::from_initialized_state(self.state)
3078 }
3079
3080 pub fn exec(&mut self, source: &[u8], name: &[u8]) -> Result<()> {
3082 exec_state(&mut self.state, source, name)
3083 }
3084}
3085
3086fn exec_state(state: &mut LuaState, source: &[u8], name: &[u8]) -> Result<()> {
3087 let status = load_buffer(state, source, name)?;
3088 if status != 0 {
3089 let err = state.pop();
3090 return Err(LuaError::from_value(err));
3091 }
3092 lua_vm::api::pcall_k(state, 0, 0, 0, 0, None)?;
3093 Ok(())
3094}
3095
3096pub fn install_parser_hook(state: &mut LuaState) {
3097 state.global_mut().parser_hook = Some(parser_hook);
3098}
3099
3100fn parser_hook(
3101 state: &mut LuaState,
3102 source: &[u8],
3103 name: &[u8],
3104 firstchar: i32,
3105) -> Result<GcRef<LuaLClosure>> {
3106 let _heap_guard = heap_guard(state);
3107 let proto = lua_parse::parse(
3108 state,
3109 lua_parse::DynData::default(),
3110 source,
3111 name,
3112 firstchar,
3113 )?;
3114 let nupvals = proto.upvalues.len();
3115 let mut upvals = Vec::with_capacity(nupvals);
3116 for _ in 0..nupvals {
3117 upvals.push(std::cell::Cell::new(GcRef::new(UpVal::closed(
3118 RawLuaValue::Nil,
3119 ))));
3120 }
3121 Ok(GcRef::new(LuaLClosure {
3122 proto: GcRef::new(*proto),
3123 upvals,
3124 }))
3125}
3126
3127#[cfg(test)]
3128mod tests {
3129 use super::*;
3130 use std::cell::Cell;
3131
3132 fn external_root_count(lua: &Lua) -> usize {
3133 lua.with_state(|state| state.global().external_roots.len())
3134 }
3135
3136 struct Counter {
3137 value: i64,
3138 }
3139
3140 impl UserData for Counter {
3141 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
3142 methods.add_method("get", |_lua, this, ()| Ok(this.value));
3143 methods.add_method_mut("inc", |_lua, this, delta: i64| {
3144 this.value += delta;
3145 Ok(this.value)
3146 });
3147 }
3148 }
3149
3150 struct PropertyBag {
3151 value: i64,
3152 }
3153
3154 impl UserData for PropertyBag {
3155 fn add_meta_methods<M: UserDataMethods<Self>>(methods: &mut M) {
3156 methods.add_meta_method(MetaMethod::Index, |_lua, this, key: String| {
3157 if key == "value" {
3158 Ok(Value::Integer(this.value))
3159 } else {
3160 Ok(Value::Nil)
3161 }
3162 });
3163 methods.add_meta_method_mut(
3164 MetaMethod::NewIndex,
3165 |_lua, this, (key, value): (String, i64)| {
3166 if key != "value" {
3167 return Err(LuaError::runtime(format_args!("unknown property")));
3168 }
3169 this.value = value;
3170 Ok(())
3171 },
3172 );
3173 }
3174 }
3175
3176 #[test]
3177 fn rooted_table_clone_and_drop_manage_root_slots() {
3178 let lua = Lua::new();
3179 assert_eq!(external_root_count(&lua), 0);
3180
3181 let table = lua.create_table().expect("table should allocate");
3182 assert_eq!(external_root_count(&lua), 1);
3183
3184 let cloned = table.clone();
3185 assert_eq!(external_root_count(&lua), 2);
3186
3187 drop(table);
3188 assert_eq!(external_root_count(&lua), 1);
3189
3190 cloned.set("answer", 42_i64).expect("set should succeed");
3191 lua.gc_collect();
3192 assert_eq!(
3193 cloned.get::<_, i64>("answer").expect("get should succeed"),
3194 42
3195 );
3196
3197 drop(cloned);
3198 assert_eq!(external_root_count(&lua), 0);
3199 }
3200
3201 #[test]
3202 fn table_values_survive_forced_collection_between_operations() {
3203 let lua = Lua::new();
3204 let table = lua.create_table().expect("table should allocate");
3205
3206 lua.gc_collect();
3207 table.set("k", "v").expect("set should succeed");
3208 table.set(1_i64, "array").expect("array set should succeed");
3209 lua.gc_collect();
3210
3211 let value: String = table.get("k").expect("get should succeed");
3212 assert_eq!(value, "v");
3213 assert_eq!(table.len().expect("len should succeed"), 1);
3214 }
3215
3216 #[test]
3217 fn chunk_exec_eval_and_function_call_use_rooted_handles() {
3218 let lua = Lua::new();
3219 lua.load("function add(a, b) return a + b end")
3220 .set_name("test")
3221 .exec()
3222 .expect("chunk should execute");
3223
3224 let globals = lua.globals();
3225 let add: Function = globals.get("add").expect("function should exist");
3226 let result: i64 = add.call((20_i64, 22_i64)).expect("call should work");
3227 assert_eq!(result, 42);
3228
3229 let eval_result: i64 = lua
3230 .load("return add(1, 2)")
3231 .eval()
3232 .expect("eval should work");
3233 assert_eq!(eval_result, 3);
3234 }
3235
3236 #[test]
3237 fn rust_callback_captures_state_and_reenters_lua() {
3238 let lua = Lua::new();
3239 lua.load("function twice(v) return v * 2 end")
3240 .exec()
3241 .expect("chunk should execute");
3242
3243 let globals = lua.globals();
3244 let twice: Function = globals.get("twice").expect("function should exist");
3245 let calls = Rc::new(Cell::new(0));
3246 let calls_for_callback = calls.clone();
3247
3248 let callback = lua
3249 .create_function(move |_lua, value: i64| {
3250 calls_for_callback.set(calls_for_callback.get() + 1);
3251 let doubled: i64 = twice.call(value)?;
3252 Ok(doubled + 1)
3253 })
3254 .expect("callback should create");
3255 globals
3256 .set("from_rust", callback)
3257 .expect("callback should register");
3258
3259 let result: i64 = lua
3260 .load("return from_rust(20)")
3261 .eval()
3262 .expect("callback should run");
3263 assert_eq!(result, 41);
3264 assert_eq!(calls.get(), 1);
3265 }
3266
3267 #[test]
3268 fn rust_callback_accepts_and_returns_collectable_values() {
3269 let lua = Lua::new();
3270 let globals = lua.globals();
3271 let callback = lua
3272 .create_function(|lua, name: String| {
3273 let table = lua.create_table()?;
3274 table.set("name", name)?;
3275 Ok(table)
3276 })
3277 .expect("callback should create");
3278 globals
3279 .set("make_record", callback)
3280 .expect("callback should register");
3281
3282 let result: String = lua
3283 .load("return make_record('lua-rs').name")
3284 .eval()
3285 .expect("callback should return table");
3286 assert_eq!(result, "lua-rs");
3287 }
3288
3289 #[test]
3290 fn rust_callback_mut_tracks_state() {
3291 let lua = Lua::new();
3292 let globals = lua.globals();
3293 let mut next = 0_i64;
3294 let callback = lua
3295 .create_function_mut(move |_lua, delta: i64| {
3296 next += delta;
3297 Ok(next)
3298 })
3299 .expect("callback should create");
3300 globals
3301 .set("next", callback)
3302 .expect("callback should register");
3303
3304 let result: (i64, i64) = lua
3305 .load("return next(2), next(5)")
3306 .eval()
3307 .expect("callback should run");
3308 assert_eq!(result, (2, 7));
3309 }
3310
3311 #[test]
3312 fn dropped_rust_callback_releases_captured_handles_after_gc() {
3313 let lua = Lua::new();
3314 let table = lua.create_table().expect("table should allocate");
3315 table.set("value", 42_i64).expect("set should succeed");
3316 assert_eq!(external_root_count(&lua), 1);
3317
3318 let callback = {
3319 let captured = table.clone();
3320 lua.create_function(move |_lua, ()| captured.get::<_, i64>("value"))
3321 .expect("callback should create")
3322 };
3323 assert_eq!(external_root_count(&lua), 3);
3324
3325 drop(callback);
3326 lua.gc_collect();
3327 assert_eq!(external_root_count(&lua), 1);
3328 assert_eq!(table.get::<_, i64>("value").expect("table should live"), 42);
3329 }
3330
3331 #[test]
3332 fn metatable_is_built_once_per_type() {
3333 use std::sync::atomic::{AtomicUsize, Ordering};
3334 static BUILDS: AtomicUsize = AtomicUsize::new(0);
3335
3336 struct Widget {
3337 n: i64,
3338 }
3339 impl UserData for Widget {
3340 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
3341 BUILDS.fetch_add(1, Ordering::SeqCst);
3342 methods.add_method("n", |_lua, this, ()| Ok(this.n));
3343 }
3344 }
3345
3346 let lua = Lua::new();
3347 let a = lua.create_userdata(Widget { n: 1 }).expect("first");
3348 let b = lua.create_userdata(Widget { n: 2 }).expect("second");
3349 let c = lua.create_userdata(Widget { n: 3 }).expect("third");
3350
3351 assert_eq!(BUILDS.load(Ordering::SeqCst), 1);
3353
3354 let globals = lua.globals();
3356 globals.set("a", &a).unwrap();
3357 globals.set("b", &b).unwrap();
3358 globals.set("c", &c).unwrap();
3359 let sum: i64 = lua.load("return a:n() + b:n() + c:n()").eval().unwrap();
3360 assert_eq!(sum, 6);
3361 }
3362
3363 #[test]
3375 fn lua_state_frees_after_userdata_with_methods_is_dropped() {
3376 use std::rc::Rc;
3377
3378 let weak_inner = {
3379 let lua = Lua::new();
3380 let weak = Rc::downgrade(&lua.inner);
3381 let _ = lua
3385 .create_userdata(Counter { value: 1 })
3386 .expect("userdata should create");
3387 weak
3388 };
3389
3390 assert!(
3391 weak_inner.upgrade().is_none(),
3392 "LuaInner is still alive after every external Lua handle dropped: \
3393 internal callback closures hold a strong Rc<LuaInner>, leaking the state"
3394 );
3395 }
3396
3397 #[test]
3401 fn lua_state_frees_after_create_function_handle_drops() {
3402 use std::rc::Rc;
3403
3404 let weak_inner = {
3405 let lua = Lua::new();
3406 let weak = Rc::downgrade(&lua.inner);
3407 let _f = lua
3408 .create_function(|_, ()| Ok(()))
3409 .expect("create_function should succeed");
3410 weak
3411 };
3412
3413 assert!(
3414 weak_inner.upgrade().is_none(),
3415 "LuaInner is still alive after the only Lua handle dropped: \
3416 the create_function callback held a strong Rc<LuaInner>"
3417 );
3418 }
3419
3420 #[test]
3427 fn lua_state_frees_after_userdata_with_fields_drops() {
3428 use std::rc::Rc;
3429
3430 struct Point {
3431 x: f64,
3432 }
3433 impl UserData for Point {
3434 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
3435 m.add_field_method_get("x", |_, this| Ok(this.x));
3436 m.add_field_method_set("x", |_, this, v: f64| {
3437 this.x = v;
3438 Ok(())
3439 });
3440 }
3441 }
3442
3443 let weak_inner = {
3444 let lua = Lua::new();
3445 let weak = Rc::downgrade(&lua.inner);
3446 let _ = lua
3447 .create_userdata(Point { x: 1.0 })
3448 .expect("userdata should create");
3449 weak
3450 };
3451
3452 assert!(
3453 weak_inner.upgrade().is_none(),
3454 "LuaInner leaked via the composed __index/__newindex closures: \
3455 they capture Table/Function values whose RootedValue holds a \
3456 strong Rc<LuaInner>"
3457 );
3458 }
3459
3460 #[test]
3466 fn lua_state_frees_with_fields_methods_and_raw_meta() {
3467 use std::rc::Rc;
3468
3469 struct Mixed {
3470 x: f64,
3471 log: Vec<String>,
3472 }
3473 impl UserData for Mixed {
3474 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
3475 m.add_field_method_get("x", |_, this| Ok(this.x));
3476 m.add_field_method_set("x", |_, this, v: f64| {
3477 this.x = v;
3478 Ok(())
3479 });
3480 m.add_method("log_len", |_, this, ()| Ok(this.log.len() as i64));
3481 m.add_method_mut("push_log", |_, this, s: String| {
3482 this.log.push(s);
3483 Ok(())
3484 });
3485 m.add_meta_method(MetaMethod::Index, |_, _this, key: String| {
3486 Ok(::std::format!("dynamic:{key}"))
3487 });
3488 m.add_meta_method_mut(
3489 MetaMethod::NewIndex,
3490 |_, _this, (_k, _v): (String, Value)| Ok(()),
3491 );
3492 }
3493 }
3494
3495 let weak_inner = {
3496 let lua = Lua::new();
3497 let weak = Rc::downgrade(&lua.inner);
3498 let _ = lua
3499 .create_userdata(Mixed {
3500 x: 1.0,
3501 log: Vec::new(),
3502 })
3503 .expect("create");
3504 weak
3505 };
3506
3507 assert!(
3508 weak_inner.upgrade().is_none(),
3509 "maximal-composition userdata leaked LuaInner: \
3510 check the composed __index / __newindex captures"
3511 );
3512 }
3513
3514 #[test]
3520 fn composed_dispatch_does_not_accumulate_external_roots() {
3521 struct Probe {
3522 x: i64,
3523 }
3524 impl UserData for Probe {
3525 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
3526 m.add_field_method_get("x", |_, this| Ok(this.x));
3527 }
3528 }
3529
3530 let lua = Lua::new();
3531 lua.globals()
3532 .set("v", lua.create_userdata(Probe { x: 1 }).unwrap())
3533 .unwrap();
3534 let baseline = external_root_count(&lua);
3535
3536 for _ in 0..1000 {
3537 let _: i64 = lua.load("return v.x").eval().unwrap();
3538 }
3539 let after = external_root_count(&lua);
3542
3543 assert!(
3544 after <= baseline + 2,
3545 "external roots grew under composed __index churn: baseline={baseline} after={after}"
3546 );
3547 }
3548
3549 #[test]
3555 fn userdata_method_can_reenter_lua_from_callback() {
3556 struct Calc;
3557 impl UserData for Calc {
3558 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
3559 m.add_method("apply", |_lua, _this, f: Function| {
3560 let r: i64 = f.call(7_i64)?;
3561 Ok(r + 1)
3562 });
3563 }
3564 }
3565
3566 let lua = Lua::new();
3567 lua.globals()
3568 .set("c", lua.create_userdata(Calc).unwrap())
3569 .unwrap();
3570 let r: i64 = lua
3571 .load("return c:apply(function(n) return n * 2 end)")
3572 .eval()
3573 .unwrap();
3574 assert_eq!(r, 15);
3575 }
3576
3577 #[test]
3582 fn metatable_cache_is_per_lua_state() {
3583 use std::sync::atomic::{AtomicUsize, Ordering};
3584 static BUILDS: AtomicUsize = AtomicUsize::new(0);
3585
3586 struct Marker {
3587 v: i64,
3588 }
3589 impl UserData for Marker {
3590 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
3591 BUILDS.fetch_add(1, Ordering::SeqCst);
3592 m.add_method("v", |_, this, ()| Ok(this.v));
3593 }
3594 }
3595
3596 let start = BUILDS.load(Ordering::SeqCst);
3597
3598 let lua_a = Lua::new();
3599 let _a1 = lua_a.create_userdata(Marker { v: 1 }).unwrap();
3600 assert_eq!(BUILDS.load(Ordering::SeqCst) - start, 1, "state A first build");
3601 let _a2 = lua_a.create_userdata(Marker { v: 2 }).unwrap();
3602 assert_eq!(BUILDS.load(Ordering::SeqCst) - start, 1, "state A reuses cache");
3603
3604 let lua_b = Lua::new();
3605 let _b1 = lua_b.create_userdata(Marker { v: 3 }).unwrap();
3606 assert_eq!(BUILDS.load(Ordering::SeqCst) - start, 2, "state B is independent");
3607
3608 let _a3 = lua_a.create_userdata(Marker { v: 4 }).unwrap();
3609 assert_eq!(BUILDS.load(Ordering::SeqCst) - start, 2, "state A still cached");
3610 }
3611
3612 #[test]
3616 fn field_shadows_method_of_same_name() {
3617 struct Shadow {
3618 x: i64,
3619 }
3620 impl UserData for Shadow {
3621 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
3622 m.add_field_method_get("x", |_, this| Ok(this.x));
3623 m.add_method("x", |_, _this, ()| Ok(999_i64));
3624 }
3625 }
3626
3627 let lua = Lua::new();
3628 lua.globals()
3629 .set("v", lua.create_userdata(Shadow { x: 42 }).unwrap())
3630 .unwrap();
3631
3632 let r: i64 = lua.load("return v.x").eval().unwrap();
3633 assert_eq!(r, 42, "the field getter should beat the method of the same name");
3634 }
3635
3636 #[test]
3640 fn cached_metatable_is_shared_across_values_in_lua() {
3641 struct Twin;
3642 impl UserData for Twin {
3643 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
3644 m.add_method("ping", |_, _this, ()| Ok(1_i64));
3645 }
3646 }
3647
3648 let lua = Lua::new();
3649 lua.globals()
3650 .set("a", lua.create_userdata(Twin).unwrap())
3651 .unwrap();
3652 lua.globals()
3653 .set("b", lua.create_userdata(Twin).unwrap())
3654 .unwrap();
3655
3656 let same: bool = lua
3657 .load("return getmetatable(a) == getmetatable(b)")
3658 .eval()
3659 .unwrap();
3660 assert!(same, "cached metatable must be shared across values of the same type");
3661 }
3662
3663 #[test]
3664 fn fields_and_methods_coexist() {
3665 struct Vec2 {
3666 x: f64,
3667 y: f64,
3668 }
3669 impl UserData for Vec2 {
3670 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
3671 m.add_field_method_get("x", |_, this| Ok(this.x));
3672 m.add_field_method_get("y", |_, this| Ok(this.y));
3673 m.add_field_method_set("x", |_, this, v: f64| {
3674 this.x = v;
3675 Ok(())
3676 });
3677 m.add_field_method_set("y", |_, this, v: f64| {
3678 this.y = v;
3679 Ok(())
3680 });
3681 m.add_method("length", |_, this, ()| {
3682 Ok((this.x * this.x + this.y * this.y).sqrt())
3683 });
3684 m.add_method_mut("scale", |_, this, k: f64| {
3685 this.x *= k;
3686 this.y *= k;
3687 Ok(())
3688 });
3689 }
3690 }
3691
3692 let lua = Lua::new();
3693 let v = lua.create_userdata(Vec2 { x: 3.0, y: 4.0 }).unwrap();
3694 lua.globals().set("v", &v).unwrap();
3695
3696 assert_eq!(lua.load("return v:length()").eval::<f64>().unwrap(), 5.0);
3698 assert_eq!(lua.load("return v.x + v.y").eval::<f64>().unwrap(), 7.0);
3699
3700 lua.load("v.x = 6").exec().unwrap();
3702 assert_eq!(lua.load("return v.x").eval::<f64>().unwrap(), 6.0);
3703
3704 lua.load("v:scale(2)").exec().unwrap();
3706 assert_eq!(lua.load("return v.x").eval::<f64>().unwrap(), 12.0);
3707 assert_eq!(lua.load("return v.y").eval::<f64>().unwrap(), 8.0);
3708
3709 assert!(lua.load("v.z = 1").exec().is_err());
3711 }
3712
3713 #[test]
3714 fn userdata_methods_dispatch_and_track_borrows() {
3715 let lua = Lua::new();
3716 let globals = lua.globals();
3717 let counter = lua
3718 .create_userdata(Counter { value: 1 })
3719 .expect("userdata should create");
3720 globals
3721 .set("counter", &counter)
3722 .expect("userdata should register");
3723
3724 let result: i64 = lua
3725 .load("counter:inc(5); return counter:get()")
3726 .eval()
3727 .expect("methods should dispatch");
3728 assert_eq!(result, 6);
3729 assert_eq!(
3730 counter
3731 .with_borrow::<Counter, _>(|counter| counter.value)
3732 .expect("borrow should work"),
3733 6
3734 );
3735
3736 {
3737 let borrowed = counter
3738 .borrow::<Counter>()
3739 .expect("borrow guard should work");
3740 assert_eq!(borrowed.value, 6);
3741 }
3742
3743 {
3744 let mut borrowed = counter
3745 .borrow_mut::<Counter>()
3746 .expect("mutable borrow guard should work");
3747 borrowed.value = 9;
3748 }
3749
3750 assert_eq!(
3751 lua.load("return counter:get()")
3752 .eval::<i64>()
3753 .expect("method should see guard mutation"),
3754 9
3755 );
3756 }
3757
3758 #[test]
3759 fn userdata_payload_survives_gc_while_lua_holds_userdata() {
3760 let lua = Lua::new();
3761 let globals = lua.globals();
3762 let counter = lua
3763 .create_userdata(Counter { value: 10 })
3764 .expect("userdata should create");
3765 globals
3766 .set("counter", counter)
3767 .expect("userdata should register");
3768
3769 lua.gc_collect();
3770 let result: i64 = lua
3771 .load("counter:inc(2); collectgarbage('collect'); return counter:get()")
3772 .eval()
3773 .expect("userdata should survive collection");
3774 assert_eq!(result, 12);
3775 }
3776
3777 #[test]
3778 fn userdata_runtime_borrow_conflict_returns_lua_error() {
3779 let lua = Lua::new();
3780 let globals = lua.globals();
3781 let counter = lua
3782 .create_userdata(Counter { value: 1 })
3783 .expect("userdata should create");
3784 globals
3785 .set("counter", &counter)
3786 .expect("userdata should register");
3787
3788 let failed = counter
3789 .with_borrow::<Counter, _>(|_| lua.load("return counter:inc(1)").eval::<i64>().is_err())
3790 .expect("outer borrow should succeed");
3791 assert!(
3792 failed,
3793 "mutable method should fail while immutable borrow is held"
3794 );
3795 assert_eq!(
3796 counter
3797 .with_borrow::<Counter, _>(|counter| counter.value)
3798 .expect("borrow should work"),
3799 1
3800 );
3801 }
3802
3803 #[test]
3804 fn userdata_index_and_newindex_metamethods_dispatch() {
3805 let lua = Lua::new();
3806 let globals = lua.globals();
3807 let bag = lua
3808 .create_userdata(PropertyBag { value: 7 })
3809 .expect("userdata should create");
3810 globals.set("bag", &bag).expect("userdata should register");
3811
3812 let result: i64 = lua
3813 .load("bag.value = 42; return bag.value")
3814 .eval()
3815 .expect("metamethods should dispatch");
3816 assert_eq!(result, 42);
3817 assert_eq!(
3818 bag.with_borrow::<PropertyBag, _>(|bag| bag.value)
3819 .expect("borrow should work"),
3820 42
3821 );
3822 }
3823
3824 #[test]
3825 fn userdata_values_convert_directly_with_into_lua() {
3826 let lua = Lua::new();
3827 let globals = lua.globals();
3828 globals
3829 .set("counter", Counter { value: 3 })
3830 .expect("userdata should convert through IntoLua");
3831
3832 let result: i64 = lua
3833 .load("counter:inc(4); return counter:get()")
3834 .eval()
3835 .expect("converted userdata should dispatch methods");
3836 assert_eq!(result, 7);
3837 }
3838
3839 #[test]
3840 fn variadic_args_and_returns_convert_all_values() {
3841 let lua = Lua::new();
3842 let globals = lua.globals();
3843
3844 let sum = lua
3845 .create_function(|_lua, values: Variadic<i64>| Ok(values.iter().sum::<i64>()))
3846 .expect("variadic callback should create");
3847 globals.set("sum", sum).expect("callback should register");
3848 let result: i64 = lua
3849 .load("return sum(3, 2, 5)")
3850 .eval()
3851 .expect("variadic callback should run");
3852 assert_eq!(result, 10);
3853
3854 let echo = lua
3855 .create_function(|_lua, values: Variadic<Value>| Ok(values))
3856 .expect("variadic return callback should create");
3857 globals.set("echo", echo).expect("callback should register");
3858 let result: (i64, i64, i64) = lua
3859 .load("return echo(1, 2, 3)")
3860 .eval()
3861 .expect("variadic returns should stay separate");
3862 assert_eq!(result, (1, 2, 3));
3863
3864 let values: Variadic<i64> = lua
3865 .load("return 4, 5, 6")
3866 .eval()
3867 .expect("variadic eval should collect all returns");
3868 assert_eq!(values.into_vec(), vec![4, 5, 6]);
3869 }
3870
3871 #[test]
3872 fn vectors_maps_and_triple_returns_convert_through_tables() {
3873 let lua = Lua::new();
3874 let globals = lua.globals();
3875
3876 globals
3877 .set("list", vec![1_i64, 2, 3])
3878 .expect("vector should convert to table");
3879 let second: i64 = lua
3880 .load("return list[2]")
3881 .eval()
3882 .expect("table should be readable from Lua");
3883 assert_eq!(second, 2);
3884
3885 let list: Vec<i64> = lua
3886 .load("return {4, 5, 6}")
3887 .eval()
3888 .expect("table should convert to vector");
3889 assert_eq!(list, vec![4, 5, 6]);
3890
3891 let mut map = HashMap::new();
3892 map.insert("left".to_string(), 10_i64);
3893 map.insert("right".to_string(), 20_i64);
3894 globals
3895 .set("map", map)
3896 .expect("map should convert to table");
3897 let sum: i64 = lua
3898 .load("return map.left + map.right")
3899 .eval()
3900 .expect("map table should be readable from Lua");
3901 assert_eq!(sum, 30);
3902
3903 let map: HashMap<String, i64> = lua
3904 .load("return {alpha = 3, beta = 9}")
3905 .eval()
3906 .expect("table should convert to map");
3907 assert_eq!(map.get("alpha"), Some(&3));
3908 assert_eq!(map.get("beta"), Some(&9));
3909
3910 let triple: (i64, i64, i64) = lua
3911 .load("return 1, 2, 3")
3912 .eval()
3913 .expect("triple returns should convert");
3914 assert_eq!(triple, (1, 2, 3));
3915 }
3916
3917 fn runtime_error_message(err: &LuaError) -> String {
3923 match err {
3924 LuaError::Runtime(v) | LuaError::Syntax(v) => match v {
3925 RawLuaValue::Str(s) => String::from_utf8_lossy(s.as_bytes()).into_owned(),
3926 other => format!("{other:?}"),
3927 },
3928 other => format!("{other:?}"),
3929 }
3930 }
3931
3932 struct ScopedCounter {
3937 value: i64,
3938 calls: Cell<u32>,
3939 }
3940
3941 impl UserData for ScopedCounter {
3942 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
3943 methods.add_method("get", |_lua, this, ()| {
3944 this.calls.set(this.calls.get() + 1);
3945 Ok(this.value)
3946 });
3947 methods.add_method_mut("inc", |_lua, this, delta: i64| {
3948 this.value += delta;
3949 Ok(this.value)
3950 });
3951 methods.add_method("calls", |_lua, this, ()| Ok(this.calls.get() as i64));
3952 methods.add_method("call_get_via_global", |lua, _this, ()| {
3953 lua.load("return c:get()").eval::<i64>()
3954 });
3955 methods.add_method_mut("inc_via_global", |lua, this, ()| {
3956 this.value += 1;
3957 lua.load("return c:get()").eval::<i64>()
3958 });
3959 }
3960 }
3961
3962 struct ScopedBag {
3963 value: i64,
3964 }
3965
3966 impl UserData for ScopedBag {
3967 fn add_meta_methods<M: UserDataMethods<Self>>(methods: &mut M) {
3968 methods.add_meta_method(MetaMethod::Index, |_lua, this, key: String| {
3969 if key == "value" {
3970 Ok(Value::Integer(this.value))
3971 } else {
3972 Ok(Value::Nil)
3973 }
3974 });
3975 methods.add_meta_method_mut(
3976 MetaMethod::NewIndex,
3977 |_lua, this, (key, value): (String, i64)| {
3978 if key != "value" {
3979 return Err(LuaError::runtime(format_args!("unknown property")));
3980 }
3981 this.value = value;
3982 Ok(())
3983 },
3984 );
3985 }
3986 }
3987
3988 struct ScopedFielded {
3989 n: i64,
3990 }
3991
3992 impl UserData for ScopedFielded {
3993 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
3994 methods.add_field_method_get("n", |_lua, this| Ok(this.n));
3995 methods.add_field_method_set("n", |_lua, this, new: i64| {
3996 this.n = new;
3997 Ok(())
3998 });
3999 }
4000 }
4001
4002 #[test]
4006 fn scope_userdata_dispatches_method_calls_against_borrow() {
4007 let lua = Lua::new();
4008 let mut counter = ScopedCounter {
4009 value: 10,
4010 calls: Cell::new(0),
4011 };
4012
4013 let observed: i64 = lua
4014 .scope(|scope| {
4015 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4016 lua.globals().set("c", &ud)?;
4017 lua.load("return c:get()").eval::<i64>()
4018 })
4019 .expect("scope body should succeed");
4020 assert_eq!(observed, 10);
4021 assert_eq!(counter.value, 10);
4022 assert_eq!(counter.calls.get(), 1);
4023 }
4024
4025 #[test]
4029 fn scope_userdata_mut_method_propagates_to_external_borrow() {
4030 let lua = Lua::new();
4031 let mut counter = ScopedCounter {
4032 value: 0,
4033 calls: Cell::new(0),
4034 };
4035
4036 lua.scope(|scope| {
4037 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4038 lua.globals().set("c", &ud)?;
4039 lua.load("c:inc(5); c:inc(7)").exec()
4040 })
4041 .expect("scope body should succeed");
4042 assert_eq!(counter.value, 12);
4043 }
4044
4045 #[test]
4051 fn scope_userdata_invalidated_after_scope_returns_runtime_error() {
4052 let lua = Lua::new();
4053 let mut counter = ScopedCounter {
4054 value: 99,
4055 calls: Cell::new(0),
4056 };
4057
4058 lua.scope(|scope| {
4059 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4060 lua.globals().set("leaked", &ud)?;
4061 Ok(())
4062 })
4063 .expect("scope body should succeed");
4064
4065 let err = lua
4066 .load("return leaked:get()")
4067 .eval::<i64>()
4068 .expect_err("scoped userdata must be unusable after scope ends");
4069 let msg = runtime_error_message(&err);
4070 assert!(
4071 msg.contains("no longer valid") || msg.contains("scope has ended"),
4072 "expected invalidation error, got: {msg}"
4073 );
4074 }
4075
4076 #[test]
4080 fn scope_userdata_invalidated_is_recoverable_via_pcall() {
4081 let lua = Lua::new();
4082 let mut counter = ScopedCounter {
4083 value: 5,
4084 calls: Cell::new(0),
4085 };
4086
4087 lua.scope(|scope| {
4088 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4089 lua.globals().set("leaked", &ud)?;
4090 Ok(())
4091 })
4092 .expect("scope body should succeed");
4093
4094 let (ok, _err_msg): (bool, String) = lua
4095 .load("local ok, e = pcall(function() return leaked:get() end); return ok, tostring(e)")
4096 .eval()
4097 .expect("pcall harness should produce two values");
4098 assert!(!ok, "post-scope call must fail");
4099 }
4100
4101 #[test]
4106 fn scope_userdata_reentrant_borrow_during_mut_method_returns_error() {
4107 let lua = Lua::new();
4108 let mut counter = ScopedCounter {
4109 value: 0,
4110 calls: Cell::new(0),
4111 };
4112
4113 let err = lua
4114 .scope(|scope| {
4115 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4116 lua.globals().set("c", &ud)?;
4117 lua.load("return c:inc_via_global()").eval::<i64>()
4118 })
4119 .expect_err("re-entry while mut-borrowed must fail");
4120 let msg = runtime_error_message(&err);
4121 assert!(
4122 msg.contains("already") && msg.contains("borrowed"),
4123 "expected borrow-conflict error, got: {msg}"
4124 );
4125 assert_eq!(counter.value, 1, "outer mutation persists despite inner failure");
4126 }
4127
4128 #[test]
4131 fn scope_userdata_reentrant_shared_borrows_are_compatible() {
4132 let lua = Lua::new();
4133 let mut counter = ScopedCounter {
4134 value: 17,
4135 calls: Cell::new(0),
4136 };
4137
4138 let observed: i64 = lua
4139 .scope(|scope| {
4140 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4141 lua.globals().set("c", &ud)?;
4142 lua.load("return c:call_get_via_global()").eval::<i64>()
4143 })
4144 .expect("nested shared borrows should succeed");
4145 assert_eq!(observed, 17);
4146 assert_eq!(counter.calls.get(), 1);
4147 }
4148
4149 #[test]
4153 fn scope_userdata_field_methods_get_and_set() {
4154 let lua = Lua::new();
4155 let mut bag = ScopedFielded { n: 3 };
4156
4157 let read_back: i64 = lua
4158 .scope(|scope| {
4159 let ud = scope.create_userdata_ref_mut(&lua, &mut bag)?;
4160 lua.globals().set("f", &ud)?;
4161 lua.load("f.n = f.n + 39; return f.n").eval::<i64>()
4162 })
4163 .expect("field methods should dispatch");
4164 assert_eq!(read_back, 42);
4165 assert_eq!(bag.n, 42);
4166 }
4167
4168 #[test]
4171 fn scope_userdata_meta_methods_dispatch() {
4172 let lua = Lua::new();
4173 let mut bag = ScopedBag { value: 100 };
4174
4175 let read: i64 = lua
4176 .scope(|scope| {
4177 let ud = scope.create_userdata_ref_mut(&lua, &mut bag)?;
4178 lua.globals().set("b", &ud)?;
4179 lua.load("b.value = 200; return b.value").eval::<i64>()
4180 })
4181 .expect("scoped meta-methods should dispatch");
4182 assert_eq!(read, 200);
4183 assert_eq!(bag.value, 200);
4184 }
4185
4186 #[test]
4189 fn scope_userdata_multiple_borrows_same_type_in_one_scope() {
4190 let lua = Lua::new();
4191 let mut a = ScopedCounter {
4192 value: 1,
4193 calls: Cell::new(0),
4194 };
4195 let mut b = ScopedCounter {
4196 value: 100,
4197 calls: Cell::new(0),
4198 };
4199
4200 lua.scope(|scope| {
4201 let ua = scope.create_userdata_ref_mut(&lua, &mut a)?;
4202 let ub = scope.create_userdata_ref_mut(&lua, &mut b)?;
4203 lua.globals().set("a", &ua)?;
4204 lua.globals().set("b", &ub)?;
4205 lua.load("a:inc(10); b:inc(1)").exec()
4206 })
4207 .expect("scope body should succeed");
4208 assert_eq!(a.value, 11);
4209 assert_eq!(b.value, 101);
4210 }
4211
4212 #[test]
4215 fn scope_userdata_different_types_coexist_in_one_scope() {
4216 let lua = Lua::new();
4217 let mut counter = ScopedCounter {
4218 value: 0,
4219 calls: Cell::new(0),
4220 };
4221 let mut bag = ScopedBag { value: 0 };
4222
4223 lua.scope(|scope| {
4224 let uc = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4225 let ub = scope.create_userdata_ref_mut(&lua, &mut bag)?;
4226 lua.globals().set("c", &uc)?;
4227 lua.globals().set("b", &ub)?;
4228 lua.load("c:inc(7); b.value = 13").exec()
4229 })
4230 .expect("scope body should succeed");
4231 assert_eq!(counter.value, 7);
4232 assert_eq!(bag.value, 13);
4233 }
4234
4235 #[test]
4238 fn scope_userdata_scope_returns_closure_value() {
4239 let lua = Lua::new();
4240 let mut counter = ScopedCounter {
4241 value: 4,
4242 calls: Cell::new(0),
4243 };
4244
4245 let doubled: i64 = lua
4246 .scope(|scope| {
4247 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4248 lua.globals().set("c", &ud)?;
4249 lua.load("return c:inc(c:get())").eval::<i64>()
4250 })
4251 .expect("scope body should succeed");
4252 assert_eq!(doubled, 8);
4253 assert_eq!(counter.value, 8);
4254 }
4255
4256 #[test]
4264 fn scope_userdata_metatable_cache_is_per_state() {
4265 let lua_a = Lua::new();
4266 let lua_b = Lua::new();
4267 let mut a = ScopedCounter {
4268 value: 1,
4269 calls: Cell::new(0),
4270 };
4271 let mut b = ScopedCounter {
4272 value: 2,
4273 calls: Cell::new(0),
4274 };
4275
4276 lua_a
4277 .scope(|scope| {
4278 let _ud = scope.create_userdata_ref_mut(&lua_a, &mut a)?;
4279 Ok(())
4280 })
4281 .expect("scope on A should succeed");
4282 lua_b
4283 .scope(|scope| {
4284 let _ud = scope.create_userdata_ref_mut(&lua_b, &mut b)?;
4285 Ok(())
4286 })
4287 .expect("scope on B should succeed");
4288
4289 let cache_a_len = lua_a.inner.userdata_scoped_metatables.borrow().len();
4290 let cache_b_len = lua_b.inner.userdata_scoped_metatables.borrow().len();
4291 assert_eq!(cache_a_len, 1);
4292 assert_eq!(cache_b_len, 1);
4293 }
4294
4295 #[test]
4300 fn scope_userdata_metatable_is_built_once_per_type() {
4301 let lua = Lua::new();
4302 let mut a = ScopedCounter {
4303 value: 0,
4304 calls: Cell::new(0),
4305 };
4306 let mut b = ScopedCounter {
4307 value: 0,
4308 calls: Cell::new(0),
4309 };
4310
4311 lua.scope(|scope| {
4312 let _ud = scope.create_userdata_ref_mut(&lua, &mut a)?;
4313 Ok(())
4314 })
4315 .expect("first scope should succeed");
4316 let after_first = lua.inner.userdata_scoped_metatables.borrow().len();
4317
4318 lua.scope(|scope| {
4319 let _ud = scope.create_userdata_ref_mut(&lua, &mut b)?;
4320 Ok(())
4321 })
4322 .expect("second scope should succeed");
4323 let after_second = lua.inner.userdata_scoped_metatables.borrow().len();
4324
4325 assert_eq!(after_first, 1);
4326 assert_eq!(after_second, 1);
4327 }
4328
4329 #[test]
4331 fn scope_userdata_rust_side_scoped_borrow_inside_scope() {
4332 let lua = Lua::new();
4333 let mut counter = ScopedCounter {
4334 value: 21,
4335 calls: Cell::new(0),
4336 };
4337
4338 let observed = lua
4339 .scope(|scope| {
4340 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4341 ud.scoped_borrow::<ScopedCounter, _>(|c| c.value)
4342 })
4343 .expect("scoped_borrow should succeed inside scope");
4344 assert_eq!(observed, 21);
4345 }
4346
4347 #[test]
4349 fn scope_userdata_rust_side_scoped_borrow_mut_inside_scope() {
4350 let lua = Lua::new();
4351 let mut counter = ScopedCounter {
4352 value: 0,
4353 calls: Cell::new(0),
4354 };
4355
4356 lua.scope(|scope| {
4357 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4358 ud.scoped_borrow_mut::<ScopedCounter, _>(|c| c.value = 5)
4359 })
4360 .expect("scoped_borrow_mut should succeed");
4361 assert_eq!(counter.value, 5);
4362 }
4363
4364 #[test]
4368 fn scope_userdata_rust_side_borrow_after_scope_errors() {
4369 let lua = Lua::new();
4370 let mut counter = ScopedCounter {
4371 value: 7,
4372 calls: Cell::new(0),
4373 };
4374
4375 let leaked: AnyUserData = lua
4376 .scope(|scope| scope.create_userdata_ref_mut(&lua, &mut counter))
4377 .expect("scope body should succeed");
4378
4379 let err = leaked
4380 .scoped_borrow::<ScopedCounter, _>(|c| c.value)
4381 .expect_err("post-scope Rust borrow must fail");
4382 let msg = runtime_error_message(&err);
4383 assert!(
4384 msg.contains("no longer valid") || msg.contains("scope has ended"),
4385 "expected invalidation error, got: {msg}"
4386 );
4387
4388 let err = leaked
4389 .scoped_borrow_mut::<ScopedCounter, _>(|c| c.value = 99)
4390 .expect_err("post-scope Rust mut-borrow must fail");
4391 let msg = runtime_error_message(&err);
4392 assert!(
4393 msg.contains("no longer valid") || msg.contains("scope has ended"),
4394 "expected invalidation error, got: {msg}"
4395 );
4396
4397 assert_eq!(counter.value, 7, "the borrow must not have been touched");
4398 }
4399
4400 #[test]
4406 fn scope_userdata_owned_borrow_path_rejects_scoped_cells() {
4407 let lua = Lua::new();
4408 let mut counter = ScopedCounter {
4409 value: 1,
4410 calls: Cell::new(0),
4411 };
4412
4413 let err = lua
4414 .scope(|scope| {
4415 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4416 Ok(ud.with_borrow::<ScopedCounter, _>(|c| c.value))
4417 })
4418 .expect("scope body should succeed")
4419 .expect_err("owned borrow path must not reach a scoped cell");
4420 let msg = runtime_error_message(&err);
4421 assert!(
4422 msg.contains("type mismatch"),
4423 "expected type-mismatch error, got: {msg}"
4424 );
4425 }
4426
4427 #[test]
4430 fn scope_userdata_scoped_borrow_rejects_owned_cells() {
4431 let lua = Lua::new();
4432 let ud = lua
4433 .create_userdata(ScopedCounter {
4434 value: 5,
4435 calls: Cell::new(0),
4436 })
4437 .expect("owned userdata should create");
4438
4439 let err = ud
4440 .scoped_borrow::<ScopedCounter, _>(|c| c.value)
4441 .expect_err("scoped borrow must not reach an owned cell");
4442 let msg = runtime_error_message(&err);
4443 assert!(
4444 msg.contains("type mismatch"),
4445 "expected type-mismatch error, got: {msg}"
4446 );
4447 }
4448
4449 #[test]
4453 fn scope_function_captures_borrow_and_is_callable_from_lua() {
4454 let lua = Lua::new();
4455 let mut acc: i64 = 0;
4456
4457 let total: i64 = lua
4458 .scope(|scope| {
4459 let f = scope.create_function_mut(&lua, |_lua, n: i64| {
4460 acc += n;
4461 Ok(acc)
4462 })?;
4463 lua.globals().set("add", &f)?;
4464 lua.load("add(2); add(3); return add(5)").eval::<i64>()
4465 })
4466 .expect("scoped function should dispatch");
4467 assert_eq!(total, 10);
4468 assert_eq!(acc, 10);
4469 }
4470
4471 #[test]
4474 fn scope_function_calls_share_one_closure() {
4475 let lua = Lua::new();
4476 let counts = Cell::new(0u32);
4477
4478 lua.scope(|scope| {
4479 let f = scope.create_function(&lua, |_lua, ()| {
4480 counts.set(counts.get() + 1);
4481 Ok(())
4482 })?;
4483 lua.globals().set("tick", &f)?;
4484 lua.load("for _ = 1, 4 do tick() end").exec()
4485 })
4486 .expect("scope should succeed");
4487 assert_eq!(counts.get(), 4);
4488 }
4489
4490 #[test]
4494 fn scope_function_invalidated_after_scope_returns_runtime_error() {
4495 let lua = Lua::new();
4496 let mut acc: i64 = 0;
4497
4498 lua.scope(|scope| {
4499 let f = scope.create_function_mut(&lua, |_lua, n: i64| {
4500 acc += n;
4501 Ok(acc)
4502 })?;
4503 lua.globals().set("add", &f)?;
4504 lua.load("add(1)").exec()
4505 })
4506 .expect("scope body should succeed");
4507 assert_eq!(acc, 1);
4508
4509 let err = lua
4510 .load("return add(100)")
4511 .eval::<i64>()
4512 .expect_err("post-scope call must fail");
4513 let msg = runtime_error_message(&err);
4514 assert!(
4515 msg.contains("no longer valid") || msg.contains("scope has ended"),
4516 "expected invalidation error, got: {msg}"
4517 );
4518 assert_eq!(acc, 1, "the closure's borrow must not have been touched");
4519 }
4520
4521 #[test]
4525 fn scope_function_reentrant_fnmut_is_rejected() {
4526 let lua = Lua::new();
4527 let mut count: i64 = 0;
4528
4529 let err = lua
4530 .scope(|scope| {
4531 let f = scope.create_function_mut(&lua, |lua, ()| {
4532 count += 1;
4533 if count < 2 {
4534 lua.load("recurse()").exec()?;
4535 }
4536 Ok(())
4537 })?;
4538 lua.globals().set("recurse", &f)?;
4539 lua.load("recurse()").exec()
4540 })
4541 .expect_err("re-entrant FnMut must error");
4542 let msg = runtime_error_message(&err);
4543 assert!(
4544 msg.contains("already borrowed"),
4545 "expected FnMut-conflict error, got: {msg}"
4546 );
4547 }
4548
4549 #[test]
4554 fn scope_function_and_userdata_in_same_scope() {
4555 let lua = Lua::new();
4556 let mut bag = ScopedFielded { n: 0 };
4557 let log = Cell::new(0i64);
4558
4559 lua.scope(|scope| {
4560 let ud = scope.create_userdata_ref_mut(&lua, &mut bag)?;
4561 let logger = scope.create_function(&lua, |_lua, n: i64| {
4562 log.set(log.get() + n);
4563 Ok(())
4564 })?;
4565 lua.globals().set("b", &ud)?;
4566 lua.globals().set("log", &logger)?;
4567 lua.load("b.n = 42; log(b.n); log(b.n + 1)").exec()
4568 })
4569 .expect("mixed scope body should succeed");
4570 assert_eq!(bag.n, 42);
4571 assert_eq!(log.get(), 85);
4572 }
4573
4574 #[test]
4578 fn scope_function_invalidated_even_when_body_errors() {
4579 let lua = Lua::new();
4580 let value = Cell::new(5i64);
4581
4582 let _err = lua
4583 .scope(|scope| -> Result<()> {
4584 let f = scope.create_function(&lua, |_lua, ()| Ok(value.get()))?;
4585 lua.globals().set("get", &f)?;
4586 Err(LuaError::runtime(format_args!("aborting")))
4587 })
4588 .expect_err("scope body should propagate error");
4589
4590 let err = lua
4591 .load("return get()")
4592 .eval::<i64>()
4593 .expect_err("function must be invalidated after error-exit scope");
4594 let msg = runtime_error_message(&err);
4595 assert!(
4596 msg.contains("no longer valid") || msg.contains("scope has ended"),
4597 "expected invalidation error, got: {msg}"
4598 );
4599 }
4600
4601 #[test]
4605 fn scope_function_many_closures_in_one_scope() {
4606 let lua = Lua::new();
4607 let total = Cell::new(0i64);
4608 let total_ref = &total;
4609
4610 lua.scope(|scope| {
4611 for i in 1..=8 {
4612 let f = scope.create_function(&lua, move |_lua, ()| {
4613 total_ref.set(total_ref.get() + i);
4614 Ok(())
4615 })?;
4616 lua.globals().set(format!("f{}", i).as_str(), &f)?;
4617 }
4618 lua.load("f1(); f2(); f3(); f4(); f5(); f6(); f7(); f8()").exec()
4619 })
4620 .expect("scope with many closures should succeed");
4621 assert_eq!(total.get(), 36);
4622 }
4623
4624 #[test]
4629 fn scope_userdata_invalidated_even_when_body_errors() {
4630 let lua = Lua::new();
4631 let mut counter = ScopedCounter {
4632 value: 1,
4633 calls: Cell::new(0),
4634 };
4635
4636 let err = lua
4637 .scope(|scope| -> Result<()> {
4638 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4639 lua.globals().set("c", &ud)?;
4640 Err(LuaError::runtime(format_args!("aborting scope")))
4641 })
4642 .expect_err("scope body should propagate error");
4643 let _ = err;
4644
4645 let leaked_err = lua
4646 .load("return c:get()")
4647 .eval::<i64>()
4648 .expect_err("leaked userdata must still be invalidated");
4649 let msg = runtime_error_message(&leaked_err);
4650 assert!(
4651 msg.contains("no longer valid") || msg.contains("scope has ended"),
4652 "expected invalidation error after scope-with-error, got: {msg}"
4653 );
4654 }
4655
4656 #[test]
4662 fn scope_userdata_cloned_handles_invalidate_together() {
4663 let lua = Lua::new();
4664 let mut counter = ScopedCounter {
4665 value: 9,
4666 calls: Cell::new(0),
4667 };
4668
4669 lua.scope(|scope| {
4670 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4671 let clone = ud.clone();
4672 lua.globals().set("a", &ud)?;
4673 lua.globals().set("b", &clone)?;
4674 lua.load("assert(a:get() == 9); assert(b:get() == 9)").exec()
4675 })
4676 .expect("scope body should succeed");
4677
4678 let err_a = lua
4679 .load("return a:get()")
4680 .eval::<i64>()
4681 .expect_err("original handle must error post-scope");
4682 let err_b = lua
4683 .load("return b:get()")
4684 .eval::<i64>()
4685 .expect_err("cloned handle must error post-scope");
4686 assert!(runtime_error_message(&err_a).contains("no longer valid"));
4687 assert!(runtime_error_message(&err_b).contains("no longer valid"));
4688 }
4689
4690 #[test]
4694 fn scope_userdata_nested_scopes_isolated() {
4695 let lua = Lua::new();
4696 let mut outer_counter = ScopedCounter {
4697 value: 1,
4698 calls: Cell::new(0),
4699 };
4700 let mut inner_counter = ScopedCounter {
4701 value: 100,
4702 calls: Cell::new(0),
4703 };
4704
4705 lua.scope(|outer| {
4706 let o = outer.create_userdata_ref_mut(&lua, &mut outer_counter)?;
4707 lua.globals().set("outer", &o)?;
4708
4709 lua.scope(|inner| {
4710 let i = inner.create_userdata_ref_mut(&lua, &mut inner_counter)?;
4711 lua.globals().set("inner", &i)?;
4712 lua.load("assert(outer:get() == 1); assert(inner:get() == 100)").exec()
4713 })?;
4714
4715 let inner_err = lua
4717 .load("return inner:get()")
4718 .eval::<i64>()
4719 .expect_err("inner userdata must be dead after inner scope");
4720 assert!(runtime_error_message(&inner_err).contains("no longer valid"));
4721
4722 let outer_alive: i64 = lua
4723 .load("return outer:get()")
4724 .eval()
4725 .expect("outer userdata must still be alive in outer scope");
4726 assert_eq!(outer_alive, 1);
4727 Ok(())
4728 })
4729 .expect("scope body should succeed");
4730
4731 let err = lua
4733 .load("return outer:get()")
4734 .eval::<i64>()
4735 .expect_err("outer userdata must be dead after outer scope");
4736 assert!(runtime_error_message(&err).contains("no longer valid"));
4737 }
4738
4739 #[test]
4750 fn scope_cell_shared_then_shared_succeeds() {
4751 let mut data = 17_i32;
4752 let cell = ScopedCell::<i32>::new(&mut data);
4753
4754 let a = cell.try_borrow().expect("first shared borrow");
4755 let b = cell.try_borrow().expect("second shared borrow");
4756 assert_eq!(*a, 17);
4757 assert_eq!(*b, 17);
4758 drop(a);
4759 drop(b);
4760
4761 cell.invalidate();
4762 assert!(cell.try_borrow().is_err(), "post-invalidate must fail");
4763 }
4764
4765 #[test]
4766 fn scope_cell_mut_then_shared_fails() {
4767 let mut data = 5_i32;
4768 let cell = ScopedCell::<i32>::new(&mut data);
4769
4770 let mut m = cell.try_borrow_mut().expect("first mut borrow");
4771 *m = 42;
4772 let s = cell.try_borrow();
4773 assert!(s.is_err(), "shared borrow while mut-held must fail");
4774 drop(m);
4775
4776 let s = cell.try_borrow().expect("shared borrow after mut release");
4777 assert_eq!(*s, 42);
4778 }
4779
4780 #[test]
4781 fn scope_cell_shared_then_mut_fails() {
4782 let mut data = 99_i32;
4783 let cell = ScopedCell::<i32>::new(&mut data);
4784
4785 let s = cell.try_borrow().expect("first shared borrow");
4786 let m = cell.try_borrow_mut();
4787 assert!(m.is_err(), "mut borrow while shared-held must fail");
4788 drop(s);
4789
4790 let mut m = cell.try_borrow_mut().expect("mut borrow after shared release");
4791 *m = 100;
4792 drop(m);
4793 assert_eq!(data, 100);
4794 }
4795
4796 #[test]
4797 fn scope_cell_invalidate_after_drop_of_guards_is_clean() {
4798 let mut data = String::from("hi");
4799 let cell = ScopedCell::<String>::new(&mut data);
4800 {
4801 let guard = cell.try_borrow().expect("borrow");
4802 assert_eq!(&*guard, "hi");
4803 }
4804 cell.invalidate();
4805 assert!(cell.try_borrow().is_err());
4806 assert!(cell.try_borrow_mut().is_err());
4807 }
4808
4809 #[test]
4810 fn scope_cell_drop_guard_decrements_borrow_count() {
4811 let mut data = 0_i32;
4812 let cell = ScopedCell::<i32>::new(&mut data);
4813 {
4814 let _a = cell.try_borrow().expect("a");
4815 let _b = cell.try_borrow().expect("b");
4816 assert!(cell.try_borrow_mut().is_err());
4817 }
4818 cell.try_borrow_mut().expect("mut borrow once guards drop");
4819 }
4820
4821 #[test]
4822 fn scope_fn_cell_dispatches_and_invalidates() {
4823 let counter = Cell::new(0i64);
4824 let adapter: Box<dyn Fn(&Lua, Vec<Value>) -> Result<Vec<Value>>> =
4825 Box::new(|_lua, _args| Ok(Vec::new()));
4826 let cell = Rc::new(ScopedFnCell {
4827 boxed: RefCell::new(Some(adapter)),
4828 });
4829
4830 let lua = Lua::new();
4831 cell.try_call(&lua, Vec::new()).expect("pre-invalidate call");
4832 counter.set(counter.get() + 1);
4833
4834 cell.invalidate();
4835
4836 let err = cell
4837 .try_call(&lua, Vec::new())
4838 .expect_err("post-invalidate call must fail");
4839 let msg = runtime_error_message(&err);
4840 assert!(msg.contains("no longer valid"), "got: {msg}");
4841 assert_eq!(counter.get(), 1);
4842 }
4843}