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