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