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, LuaVersion, NumberModel};
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)]
296pub struct Lua {
297 inner: Rc<LuaInner>,
298}
299
300struct LuaInner {
301 version: LuaVersion,
305 state: RefCell<LuaState>,
306 active_state: Cell<*mut LuaState>,
307 pending_external_unroots: RefCell<Vec<ExternalRootKey>>,
308 userdata_metatables: RefCell<HashMap<TypeId, GcRef<RawLuaTable>>>,
313 userdata_scoped_metatables: RefCell<HashMap<TypeId, GcRef<RawLuaTable>>>,
318}
319
320struct UserDataCell<T> {
321 value: RefCell<T>,
322}
323
324struct ScopedCell<T: 'static> {
351 ptr: Cell<Option<NonNull<T>>>,
352 borrow: Cell<isize>,
355}
356
357impl<T: 'static> ScopedCell<T> {
358 fn new(data: &mut T) -> Self {
359 Self {
360 ptr: Cell::new(Some(NonNull::from(data))),
361 borrow: Cell::new(0),
362 }
363 }
364
365 fn try_borrow(&self) -> Result<ScopedRef<'_, T>> {
366 let b = self.borrow.get();
367 if b < 0 {
368 return Err(LuaError::runtime(format_args!(
369 "scoped userdata is already mutably borrowed"
370 )));
371 }
372 let ptr = self.ptr.get().ok_or_else(|| {
373 LuaError::runtime(format_args!(
374 "scoped userdata is no longer valid (its scope has ended)"
375 ))
376 })?;
377 self.borrow.set(b + 1);
378 Ok(ScopedRef { cell: self, ptr })
379 }
380
381 fn try_borrow_mut(&self) -> Result<ScopedRefMut<'_, T>> {
382 let b = self.borrow.get();
383 if b != 0 {
384 return Err(LuaError::runtime(format_args!(
385 "scoped userdata is already borrowed"
386 )));
387 }
388 let ptr = self.ptr.get().ok_or_else(|| {
389 LuaError::runtime(format_args!(
390 "scoped userdata is no longer valid (its scope has ended)"
391 ))
392 })?;
393 self.borrow.set(-1);
394 Ok(ScopedRefMut { cell: self, ptr })
395 }
396}
397
398trait ScopeInvalidate {
401 fn invalidate(&self);
402}
403
404impl<T: 'static> ScopeInvalidate for ScopedCell<T> {
405 fn invalidate(&self) {
406 self.ptr.set(None);
412 }
413}
414
415struct ScopedRef<'a, T: 'static> {
416 cell: &'a ScopedCell<T>,
417 ptr: NonNull<T>,
418}
419
420impl<'a, T: 'static> Drop for ScopedRef<'a, T> {
421 fn drop(&mut self) {
422 self.cell.borrow.set(self.cell.borrow.get() - 1);
423 }
424}
425
426impl<'a, T: 'static> Deref for ScopedRef<'a, T> {
427 type Target = T;
428 fn deref(&self) -> &T {
429 unsafe { self.ptr.as_ref() }
435 }
436}
437
438struct ScopedRefMut<'a, T: 'static> {
439 cell: &'a ScopedCell<T>,
440 ptr: NonNull<T>,
441}
442
443impl<'a, T: 'static> Drop for ScopedRefMut<'a, T> {
444 fn drop(&mut self) {
445 self.cell.borrow.set(0);
446 }
447}
448
449impl<'a, T: 'static> Deref for ScopedRefMut<'a, T> {
450 type Target = T;
451 fn deref(&self) -> &T {
452 unsafe { self.ptr.as_ref() }
454 }
455}
456
457impl<'a, T: 'static> DerefMut for ScopedRefMut<'a, T> {
458 fn deref_mut(&mut self) -> &mut T {
459 unsafe { self.ptr.as_mut() }
462 }
463}
464
465pub struct Scope<'scope> {
472 invalidators: RefCell<Vec<Rc<dyn ScopeInvalidate>>>,
473 _phantom: std::marker::PhantomData<&'scope mut ()>,
474}
475
476impl<'scope> Scope<'scope> {
477 fn new() -> Self {
478 Self {
479 invalidators: RefCell::new(Vec::new()),
480 _phantom: std::marker::PhantomData,
481 }
482 }
483
484 pub fn create_userdata_ref_mut<T>(&self, lua: &Lua, data: &'scope mut T) -> Result<AnyUserData>
495 where
496 T: UserData,
497 {
498 let cell = Rc::new(ScopedCell::<T>::new(data));
499 self.invalidators
500 .borrow_mut()
501 .push(cell.clone() as Rc<dyn ScopeInvalidate>);
502 lua.create_scoped_userdata::<T>(cell)
503 }
504
505 pub fn create_function<A, R, F>(&self, lua: &Lua, func: F) -> Result<Function>
515 where
516 A: FromLuaMulti + 'static,
517 R: IntoLuaMulti + 'static,
518 F: Fn(&Lua, A) -> Result<R> + 'scope,
519 {
520 let adapter: Box<dyn Fn(&Lua, Vec<Value>) -> Result<Vec<Value>> + 'scope> =
521 Box::new(move |lua, args| {
522 let args = A::from_lua_multi(args, lua)?;
523 let returns = func(lua, args)?;
524 returns.into_lua_multi(lua)
525 });
526 self.install_function(lua, adapter)
527 }
528
529 pub fn create_function_mut<A, R, F>(&self, lua: &Lua, func: F) -> Result<Function>
534 where
535 A: FromLuaMulti + 'static,
536 R: IntoLuaMulti + 'static,
537 F: FnMut(&Lua, A) -> Result<R> + 'scope,
538 {
539 let func = RefCell::new(func);
540 self.create_function(lua, move |lua, args| {
541 let mut func = func.try_borrow_mut().map_err(|_| {
542 LuaError::runtime(format_args!("mutable Rust callback is already borrowed"))
543 })?;
544 func(lua, args)
545 })
546 }
547
548 fn install_function(
553 &self,
554 lua: &Lua,
555 adapter: Box<dyn Fn(&Lua, Vec<Value>) -> Result<Vec<Value>> + 'scope>,
556 ) -> Result<Function> {
557 let adapter_static: Box<dyn Fn(&Lua, Vec<Value>) -> Result<Vec<Value>>> =
567 unsafe { std::mem::transmute(adapter) };
568 let cell = Rc::new(ScopedFnCell {
569 boxed: RefCell::new(Some(adapter_static)),
570 });
571 self.invalidators
572 .borrow_mut()
573 .push(cell.clone() as Rc<dyn ScopeInvalidate>);
574 lua.create_scoped_function(cell)
575 }
576}
577
578impl<'scope> Drop for Scope<'scope> {
579 fn drop(&mut self) {
580 for inv in self.invalidators.borrow().iter() {
581 inv.invalidate();
582 }
583 }
584}
585
586struct ScopedFnCell {
591 boxed: RefCell<Option<Box<dyn Fn(&Lua, Vec<Value>) -> Result<Vec<Value>>>>>,
592}
593
594impl ScopedFnCell {
595 fn try_call(&self, lua: &Lua, args: Vec<Value>) -> Result<Vec<Value>> {
598 let guard = self.boxed.borrow();
599 let func = guard.as_deref().ok_or_else(|| {
600 LuaError::runtime(format_args!(
601 "scoped function is no longer valid (its scope has ended)"
602 ))
603 })?;
604 func(lua, args)
605 }
606}
607
608impl ScopeInvalidate for ScopedFnCell {
609 fn invalidate(&self) {
610 *self.boxed.borrow_mut() = None;
611 }
612}
613
614enum DelegateEnter<S: 'static> {
643 Mut(Box<dyn Fn(&mut dyn FnMut(&mut S)) -> Result<()>>),
644 Ref(Box<dyn Fn(&mut dyn FnMut(&S)) -> Result<()>>),
645}
646
647struct DelegatedCell<S: 'static> {
648 enter: RefCell<Option<DelegateEnter<S>>>,
649}
650
651impl<S: 'static> DelegatedCell<S> {
652 fn invalid() -> LuaError {
653 LuaError::runtime(format_args!(
654 "scoped userdata is no longer valid (its scope has ended)"
655 ))
656 }
657
658 fn enter_ref(&self, f: &mut dyn FnMut(&S)) -> Result<()> {
661 let guard = self.enter.borrow();
662 match guard.as_ref().ok_or_else(Self::invalid)? {
663 DelegateEnter::Mut(g) => g(&mut |t| f(&*t)),
664 DelegateEnter::Ref(g) => g(f),
665 }
666 }
667
668 fn enter_mut(&self, f: &mut dyn FnMut(&mut S)) -> Result<()> {
671 let guard = self.enter.borrow();
672 match guard.as_ref().ok_or_else(Self::invalid)? {
673 DelegateEnter::Mut(g) => g(f),
674 DelegateEnter::Ref(_) => Err(LuaError::runtime(format_args!(
675 "cannot call a mutating method on a read-only delegated reference"
676 ))),
677 }
678 }
679}
680
681impl<S: 'static> ScopeInvalidate for DelegatedCell<S> {
682 fn invalidate(&self) {
683 *self.enter.borrow_mut() = None;
684 }
685}
686
687struct RustCallbackCell {
690 function: LuaRustFunction,
691}
692
693struct ActiveStateGuard<'a> {
694 inner: &'a LuaInner,
695 previous: *mut LuaState,
696}
697
698impl Drop for ActiveStateGuard<'_> {
699 fn drop(&mut self) {
700 self.inner.active_state.set(self.previous);
701 }
702}
703
704impl LuaInner {
705 fn enter_active(&self, state: *mut LuaState) -> ActiveStateGuard<'_> {
706 let previous = self.active_state.replace(state);
707 ActiveStateGuard {
708 inner: self,
709 previous,
710 }
711 }
712
713 fn flush_pending_external_unroots(&self, state: &mut LuaState) {
714 let pending = self.pending_external_unroots.replace(Vec::new());
715 if pending.is_empty() {
716 return;
717 }
718
719 let mut still_pending = Vec::new();
720 for key in pending {
721 if state.try_external_unroot_value(key).is_err() {
722 still_pending.push(key);
723 }
724 }
725
726 if !still_pending.is_empty() {
727 self.pending_external_unroots
728 .borrow_mut()
729 .extend(still_pending);
730 }
731 }
732}
733
734impl fmt::Debug for Lua {
735 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
736 f.debug_struct("Lua").finish_non_exhaustive()
737 }
738}
739
740impl Lua {
741 pub fn new() -> Self {
746 Self::try_new().expect("Lua runtime should initialize")
747 }
748
749 pub fn new_versioned(version: LuaVersion) -> Self {
760 Self::try_new_versioned(version).expect("Lua runtime should initialize")
761 }
762
763 pub fn try_new() -> Result<Self> {
765 Self::with_hooks(HostHooks::default())
766 }
767
768 pub fn try_new_versioned(version: LuaVersion) -> Result<Self> {
770 Self::with_hooks_versioned(HostHooks::default(), version)
771 }
772
773 pub fn with_hooks(hooks: HostHooks) -> Result<Self> {
775 Self::with_hooks_versioned(hooks, LuaVersion::default())
776 }
777
778 pub fn with_hooks_versioned(hooks: HostHooks, version: LuaVersion) -> Result<Self> {
781 if !version.is_supported() {
782 return Err(LuaError::runtime(format_args!(
785 "{} is not yet supported by lua-rs (supported: 5.3, 5.4, 5.5)",
786 version.version_str()
787 )));
788 }
789 let mut state = new_state().ok_or(LuaError::Memory)?;
790 state.global_mut().lua_version = version;
791 install_parser_hook(&mut state);
792 hooks.install(&mut state);
793 open_libs(&mut state)?;
794 let lua = Self::from_initialized_state(state, version);
795 lua.sync_version_global()?;
796 Ok(lua)
797 }
798
799 pub fn version(&self) -> LuaVersion {
801 self.inner.version
802 }
803
804 fn sync_version_global(&self) -> Result<()> {
811 self.globals()
812 .set("_VERSION", self.inner.version.version_str())
813 }
814
815 fn from_initialized_state(state: LuaState, version: LuaVersion) -> Self {
816 Lua {
817 inner: Rc::new(LuaInner {
818 version,
819 state: RefCell::new(state),
820 active_state: Cell::new(std::ptr::null_mut()),
821 pending_external_unroots: RefCell::new(Vec::new()),
822 userdata_metatables: RefCell::new(HashMap::new()),
823 userdata_scoped_metatables: RefCell::new(HashMap::new()),
824 }),
825 }
826 }
827
828 fn with_state<R>(&self, f: impl FnOnce(&mut LuaState) -> R) -> R {
829 if let Ok(mut state) = self.inner.state.try_borrow_mut() {
830 let _active = self.inner.enter_active(&mut *state);
831 self.inner.flush_pending_external_unroots(&mut state);
832 let result = f(&mut state);
833 self.inner.flush_pending_external_unroots(&mut state);
834 return result;
835 }
836
837 let state = self
838 .active_state_mut()
839 .expect("re-entrant Lua access without an active state");
840 let result = f(state);
841 self.inner.flush_pending_external_unroots(state);
842 result
843 }
844
845 fn active_state_mut(&self) -> Option<&mut LuaState> {
846 let state = self.inner.active_state.get();
847 if state.is_null() {
848 return None;
849 }
850
851 Some(unsafe { &mut *state })
857 }
858
859 fn unroot_external_key(&self, key: ExternalRootKey) {
860 let removed = if let Ok(mut state) = self.inner.state.try_borrow_mut() {
861 let _active = self.inner.enter_active(&mut *state);
862 self.inner.flush_pending_external_unroots(&mut state);
863 let removed = state.try_external_unroot_value(key).is_ok();
864 self.inner.flush_pending_external_unroots(&mut state);
865 removed
866 } else {
867 if let Some(state) = self.active_state_mut() {
868 let removed = state.try_external_unroot_value(key).is_ok();
869 self.inner.flush_pending_external_unroots(state);
870 removed
871 } else {
872 false
873 }
874 };
875
876 if !removed {
877 self.inner.pending_external_unroots.borrow_mut().push(key);
878 }
879 }
880
881 fn root_raw(&self, value: RawLuaValue) -> RootedValue {
882 let key = self.with_state(|state| state.external_root_value(value));
883 RootedValue {
884 lua: self.clone(),
885 key,
886 }
887 }
888
889 fn root_raw_in_state(&self, state: &mut LuaState, value: RawLuaValue) -> RootedValue {
890 let key = state.external_root_value(value);
891 RootedValue {
892 lua: self.clone(),
893 key,
894 }
895 }
896
897 fn userdata_cell<'a, T: 'static>(
898 &self,
899 userdata: &'a AnyUserData,
900 ) -> Result<&'a UserDataCell<T>> {
901 if !Rc::ptr_eq(&self.inner, &userdata.root.lua.inner) {
902 return Err(LuaError::runtime(format_args!(
903 "Lua userdata belongs to a different state"
904 )));
905 }
906 userdata.host_cell()
907 }
908
909 pub fn load(&self, source: impl AsRef<[u8]>) -> Chunk {
911 Chunk {
912 lua: self.clone(),
913 source: source.as_ref().to_vec(),
914 name: b"chunk".to_vec(),
915 }
916 }
917
918 pub fn globals(&self) -> Table {
920 let raw = self.with_state(|state| state.global().globals.clone());
921 Table {
922 root: self.root_raw(raw),
923 }
924 }
925
926 pub fn create_table(&self) -> Result<Table> {
928 let root = self.with_state(|state| {
929 let _heap_guard = heap_guard(state);
930 let table = state.new_table();
931 let raw = RawLuaValue::Table(table);
932 let key = state.external_root_value(raw);
933 state.gc().check_step();
934 RootedValue {
935 lua: self.clone(),
936 key,
937 }
938 });
939 Ok(Table { root })
940 }
941
942 pub fn create_string(&self, bytes: impl AsRef<[u8]>) -> Result<LuaString> {
944 let bytes = bytes.as_ref();
945 let root = self.with_state(|state| {
946 let _heap_guard = heap_guard(state);
947 let string = state.new_string(bytes)?;
948 let raw = RawLuaValue::Str(string);
949 let key = state.external_root_value(raw);
950 state.gc().check_step();
951 Ok::<_, LuaError>(RootedValue {
952 lua: self.clone(),
953 key,
954 })
955 })?;
956 Ok(LuaString { root })
957 }
958
959 pub fn create_function<A, R, F>(&self, func: F) -> Result<Function>
960 where
961 A: FromLuaMulti + 'static,
962 R: IntoLuaMulti + 'static,
963 F: Fn(&Lua, A) -> Result<R> + 'static,
964 {
965 let lua_weak = Rc::downgrade(&self.inner);
966 let callable: LuaRustFunction = Rc::new(move |state| {
967 let lua = match lua_weak.upgrade() {
968 Some(inner) => Lua { inner },
969 None => {
970 return Err(LuaError::runtime(format_args!(
971 "Lua callback fired after the state was dropped"
972 )))
973 }
974 };
975 match catch_unwind(AssertUnwindSafe(|| {
976 let args = callback_args(state, &lua)?;
977 let args = A::from_lua_multi(args, &lua)?;
978 let returns = func(&lua, args)?;
979 let returns = returns.into_lua_multi(&lua)?;
980 push_callback_returns(state, &lua, returns)
981 })) {
982 Ok(result) => result,
983 Err(_) => Err(LuaError::runtime(format_args!("Rust callback panicked"))),
984 }
985 });
986 self.create_registered_function(callable)
987 }
988
989 pub fn create_function_mut<A, R, F>(&self, func: F) -> Result<Function>
990 where
991 A: FromLuaMulti + 'static,
992 R: IntoLuaMulti + 'static,
993 F: FnMut(&Lua, A) -> Result<R> + 'static,
994 {
995 let func = RefCell::new(func);
996 self.create_function(move |lua, args| {
997 let mut func = func.try_borrow_mut().map_err(|_| {
998 LuaError::runtime(format_args!("mutable Rust callback is already borrowed"))
999 })?;
1000 func(lua, args)
1001 })
1002 }
1003
1004 fn create_registered_function(&self, callable: LuaRustFunction) -> Result<Function> {
1005 let root = self.with_state(|state| {
1006 let trampoline = rust_callback_trampoline as lua_vm::state::LuaCFunction;
1007 let idx = {
1008 let mut global = state.global_mut();
1009 match global.c_functions.iter().position(|existing| {
1010 existing
1011 .as_bare()
1012 .is_some_and(|existing| std::ptr::fn_addr_eq(existing, trampoline))
1013 }) {
1014 Some(idx) => idx,
1015 None => {
1016 let idx = global.c_functions.len();
1017 global.c_functions.push(LuaCallable::bare(trampoline));
1018 idx
1019 }
1020 }
1021 };
1022 let raw = with_heap_guard(state, || {
1023 let callback_payload = GcRef::new(RawLuaUserData {
1024 data: Box::new([]),
1025 uv: Vec::new(),
1026 metatable: RefCell::new(None),
1027 host_value: RefCell::new(Some(
1028 Rc::new(RustCallbackCell { function: callable }) as Rc<dyn Any>,
1029 )),
1030 });
1031 RawLuaValue::Function(RawLuaClosure::C(GcRef::new(RawLuaCClosure {
1032 func: idx,
1033 upvalues: vec![RawLuaValue::UserData(callback_payload)],
1034 })))
1035 });
1036 let key = state.external_root_value(raw);
1037 state.gc().check_step();
1038 RootedValue {
1039 lua: self.clone(),
1040 key,
1041 }
1042 });
1043 Ok(Function { root })
1044 }
1045
1046 fn create_userdata_method<T, A, R, F>(&self, method: F) -> Result<Function>
1047 where
1048 T: UserData,
1049 A: FromLuaMulti + 'static,
1050 R: IntoLuaMulti + 'static,
1051 F: Fn(&Lua, &T, A) -> Result<R> + 'static,
1052 {
1053 let lua_weak = Rc::downgrade(&self.inner);
1054 let callable: LuaRustFunction = Rc::new(move |state| {
1055 let lua = match lua_weak.upgrade() {
1056 Some(inner) => Lua { inner },
1057 None => {
1058 return Err(LuaError::runtime(format_args!(
1059 "Lua callback fired after the state was dropped"
1060 )))
1061 }
1062 };
1063 match catch_unwind(AssertUnwindSafe(|| {
1064 let (userdata, args) = callback_userdata_args(state, &lua)?;
1065 let args = A::from_lua_multi(args, &lua)?;
1066 let cell = lua.userdata_cell::<T>(&userdata)?;
1067 let value = cell.value.try_borrow().map_err(|_| {
1068 LuaError::runtime(format_args!("userdata is already mutably borrowed"))
1069 })?;
1070 let returns = method(&lua, &value, args)?;
1071 let returns = returns.into_lua_multi(&lua)?;
1072 push_callback_returns(state, &lua, returns)
1073 })) {
1074 Ok(result) => result,
1075 Err(_) => Err(LuaError::runtime(format_args!(
1076 "Rust userdata method panicked"
1077 ))),
1078 }
1079 });
1080 self.create_registered_function(callable)
1081 }
1082
1083 fn create_userdata_method_mut<T, A, R, F>(&self, method: F) -> Result<Function>
1084 where
1085 T: UserData,
1086 A: FromLuaMulti + 'static,
1087 R: IntoLuaMulti + 'static,
1088 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
1089 {
1090 let lua_weak = Rc::downgrade(&self.inner);
1091 let callable: LuaRustFunction = Rc::new(move |state| {
1092 let lua = match lua_weak.upgrade() {
1093 Some(inner) => Lua { inner },
1094 None => {
1095 return Err(LuaError::runtime(format_args!(
1096 "Lua callback fired after the state was dropped"
1097 )))
1098 }
1099 };
1100 match catch_unwind(AssertUnwindSafe(|| {
1101 let (userdata, args) = callback_userdata_args(state, &lua)?;
1102 let args = A::from_lua_multi(args, &lua)?;
1103 let cell = lua.userdata_cell::<T>(&userdata)?;
1104 let mut value = cell
1105 .value
1106 .try_borrow_mut()
1107 .map_err(|_| LuaError::runtime(format_args!("userdata is already borrowed")))?;
1108 let returns = method(&lua, &mut value, args)?;
1109 let returns = returns.into_lua_multi(&lua)?;
1110 push_callback_returns(state, &lua, returns)
1111 })) {
1112 Ok(result) => result,
1113 Err(_) => Err(LuaError::runtime(format_args!(
1114 "Rust userdata method panicked"
1115 ))),
1116 }
1117 });
1118 self.create_registered_function(callable)
1119 }
1120
1121 pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData>
1122 where
1123 T: UserData,
1124 {
1125 let type_id = TypeId::of::<T>();
1126 let cached = self
1127 .inner
1128 .userdata_metatables
1129 .borrow()
1130 .get(&type_id)
1131 .cloned();
1132 let metatable = match cached {
1133 Some(metatable) => metatable,
1134 None => {
1135 let mut methods = UserDataMethodRegistry::<T>::new(self);
1136 T::add_methods(&mut methods);
1137 T::add_meta_methods(&mut methods);
1138 let metatable = methods.build_metatable()?;
1139 self.inner
1140 .userdata_metatables
1141 .borrow_mut()
1142 .insert(type_id, metatable.clone());
1143 metatable
1144 }
1145 };
1146 self.attach_userdata(data, metatable)
1147 }
1148
1149 fn attach_userdata<T: UserData>(
1153 &self,
1154 data: T,
1155 metatable: GcRef<RawLuaTable>,
1156 ) -> Result<AnyUserData> {
1157 let cell: Rc<dyn Any> = Rc::new(UserDataCell {
1158 value: RefCell::new(data),
1159 });
1160 let host_value = cell.clone();
1161 let root = self.with_state(|state| {
1162 let userdata = with_heap_guard(state, || {
1163 GcRef::new(RawLuaUserData {
1164 data: Box::new([]),
1165 uv: Vec::new(),
1166 metatable: RefCell::new(None),
1167 host_value: RefCell::new(None),
1168 })
1169 });
1170 userdata.set_metatable(Some(metatable));
1171 userdata.set_host_value(Some(cell));
1172 let key = state.external_root_value(RawLuaValue::UserData(userdata));
1173 RootedValue {
1174 lua: self.clone(),
1175 key,
1176 }
1177 });
1178 Ok(AnyUserData {
1179 root,
1180 host_value: Some(host_value),
1181 })
1182 }
1183
1184 pub fn scope<F, R>(&self, f: F) -> Result<R>
1223 where
1224 F: for<'scope> FnOnce(&Scope<'scope>) -> Result<R>,
1225 {
1226 let scope = Scope::new();
1227 let result = f(&scope);
1228 drop(scope);
1232 result
1233 }
1234
1235 fn scoped_metatable_for<T>(&self) -> Result<GcRef<RawLuaTable>>
1241 where
1242 T: UserData,
1243 {
1244 let type_id = TypeId::of::<T>();
1245 let cached = self
1246 .inner
1247 .userdata_scoped_metatables
1248 .borrow()
1249 .get(&type_id)
1250 .cloned();
1251 if let Some(mt) = cached {
1252 return Ok(mt);
1253 }
1254 let mut methods = UserDataMethodRegistry::<T>::new_scoped(self);
1255 T::add_methods(&mut methods);
1256 T::add_meta_methods(&mut methods);
1257 let mt = methods.build_metatable()?;
1258 self.inner
1259 .userdata_scoped_metatables
1260 .borrow_mut()
1261 .insert(type_id, mt.clone());
1262 Ok(mt)
1263 }
1264
1265 fn create_scoped_userdata<T>(&self, cell: Rc<ScopedCell<T>>) -> Result<AnyUserData>
1268 where
1269 T: UserData,
1270 {
1271 let metatable = self.scoped_metatable_for::<T>()?;
1272 self.attach_scoped_userdata::<T>(cell, metatable)
1273 }
1274
1275 fn create_delegated_userdata<S>(&self, cell: Rc<DelegatedCell<S>>) -> Result<AnyUserData>
1279 where
1280 S: UserData,
1281 {
1282 let metatable = self.scoped_metatable_for::<S>()?;
1283 let host_value: Rc<dyn Any> = cell;
1284 let root = self.with_state(|state| {
1285 let userdata = with_heap_guard(state, || {
1286 GcRef::new(RawLuaUserData {
1287 data: Box::new([]),
1288 uv: Vec::new(),
1289 metatable: RefCell::new(None),
1290 host_value: RefCell::new(None),
1291 })
1292 });
1293 userdata.set_metatable(Some(metatable));
1294 userdata.set_host_value(Some(host_value.clone()));
1295 let key = state.external_root_value(RawLuaValue::UserData(userdata));
1296 RootedValue {
1297 lua: self.clone(),
1298 key,
1299 }
1300 });
1301 Ok(AnyUserData {
1302 root,
1303 host_value: Some(host_value),
1304 })
1305 }
1306
1307 fn attach_scoped_userdata<T>(
1310 &self,
1311 cell: Rc<ScopedCell<T>>,
1312 metatable: GcRef<RawLuaTable>,
1313 ) -> Result<AnyUserData>
1314 where
1315 T: UserData,
1316 {
1317 let host_value: Rc<dyn Any> = cell;
1318 let root = self.with_state(|state| {
1319 let userdata = with_heap_guard(state, || {
1320 GcRef::new(RawLuaUserData {
1321 data: Box::new([]),
1322 uv: Vec::new(),
1323 metatable: RefCell::new(None),
1324 host_value: RefCell::new(None),
1325 })
1326 });
1327 userdata.set_metatable(Some(metatable));
1328 userdata.set_host_value(Some(host_value.clone()));
1329 let key = state.external_root_value(RawLuaValue::UserData(userdata));
1330 RootedValue {
1331 lua: self.clone(),
1332 key,
1333 }
1334 });
1335 Ok(AnyUserData {
1336 root,
1337 host_value: Some(host_value),
1338 })
1339 }
1340
1341 fn dispatch_scoped_borrow<T, F, R>(
1352 &self,
1353 userdata: &AnyUserData,
1354 f: F,
1355 ) -> Result<R>
1356 where
1357 T: 'static,
1358 F: FnOnce(&T) -> Result<R>,
1359 {
1360 let host = userdata
1361 .host_value
1362 .as_ref()
1363 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust userdata payload")))?;
1364
1365 if let Ok(scoped) = Rc::clone(host).downcast::<ScopedCell<T>>() {
1366 let borrow = scoped.try_borrow()?;
1367 return f(&*borrow);
1368 }
1369
1370 if let Ok(delegated) = Rc::clone(host).downcast::<DelegatedCell<T>>() {
1371 let mut slot: Option<Result<R>> = None;
1372 let mut f_slot = Some(f);
1373 delegated.enter_ref(&mut |t| {
1374 if let Some(f) = f_slot.take() {
1375 slot = Some(f(t));
1376 }
1377 })?;
1378 return slot.expect("delegated enter_ref must invoke its callback");
1379 }
1380
1381 Err(LuaError::runtime(format_args!(
1382 "scoped userdata type mismatch"
1383 )))
1384 }
1385
1386 fn dispatch_scoped_borrow_mut<T, F, R>(
1387 &self,
1388 userdata: &AnyUserData,
1389 f: F,
1390 ) -> Result<R>
1391 where
1392 T: 'static,
1393 F: FnOnce(&mut T) -> Result<R>,
1394 {
1395 let host = userdata
1396 .host_value
1397 .as_ref()
1398 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust userdata payload")))?;
1399
1400 if let Ok(scoped) = Rc::clone(host).downcast::<ScopedCell<T>>() {
1401 let mut borrow = scoped.try_borrow_mut()?;
1402 return f(&mut *borrow);
1403 }
1404
1405 if let Ok(delegated) = Rc::clone(host).downcast::<DelegatedCell<T>>() {
1406 let mut slot: Option<Result<R>> = None;
1407 let mut f_slot = Some(f);
1408 delegated.enter_mut(&mut |t| {
1409 if let Some(f) = f_slot.take() {
1410 slot = Some(f(t));
1411 }
1412 })?;
1413 return slot.expect("delegated enter_mut must invoke its callback");
1414 }
1415
1416 Err(LuaError::runtime(format_args!(
1417 "scoped userdata type mismatch"
1418 )))
1419 }
1420
1421 fn create_scoped_userdata_method<T, A, R, F>(&self, method: F) -> Result<Function>
1426 where
1427 T: UserData,
1428 A: FromLuaMulti + 'static,
1429 R: IntoLuaMulti + 'static,
1430 F: Fn(&Lua, &T, A) -> Result<R> + 'static,
1431 {
1432 let lua_weak = Rc::downgrade(&self.inner);
1433 let callable: LuaRustFunction = Rc::new(move |state| {
1434 let lua = match lua_weak.upgrade() {
1435 Some(inner) => Lua { inner },
1436 None => {
1437 return Err(LuaError::runtime(format_args!(
1438 "Lua callback fired after the state was dropped"
1439 )))
1440 }
1441 };
1442 match catch_unwind(AssertUnwindSafe(|| {
1443 let (userdata, args) = callback_userdata_args(state, &lua)?;
1444 let args = A::from_lua_multi(args, &lua)?;
1445 let returns = lua.dispatch_scoped_borrow::<T, _, _>(&userdata, |t| {
1446 method(&lua, t, args)
1447 })?;
1448 let returns = returns.into_lua_multi(&lua)?;
1449 push_callback_returns(state, &lua, returns)
1450 })) {
1451 Ok(result) => result,
1452 Err(_) => Err(LuaError::runtime(format_args!(
1453 "Rust userdata method panicked"
1454 ))),
1455 }
1456 });
1457 self.create_registered_function(callable)
1458 }
1459
1460 fn create_scoped_userdata_method_mut<T, A, R, F>(&self, method: F) -> Result<Function>
1461 where
1462 T: UserData,
1463 A: FromLuaMulti + 'static,
1464 R: IntoLuaMulti + 'static,
1465 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
1466 {
1467 let lua_weak = Rc::downgrade(&self.inner);
1468 let callable: LuaRustFunction = Rc::new(move |state| {
1469 let lua = match lua_weak.upgrade() {
1470 Some(inner) => Lua { inner },
1471 None => {
1472 return Err(LuaError::runtime(format_args!(
1473 "Lua callback fired after the state was dropped"
1474 )))
1475 }
1476 };
1477 match catch_unwind(AssertUnwindSafe(|| {
1478 let (userdata, args) = callback_userdata_args(state, &lua)?;
1479 let args = A::from_lua_multi(args, &lua)?;
1480 let returns = lua.dispatch_scoped_borrow_mut::<T, _, _>(&userdata, |t| {
1481 method(&lua, t, args)
1482 })?;
1483 let returns = returns.into_lua_multi(&lua)?;
1484 push_callback_returns(state, &lua, returns)
1485 })) {
1486 Ok(result) => result,
1487 Err(_) => Err(LuaError::runtime(format_args!(
1488 "Rust userdata method panicked"
1489 ))),
1490 }
1491 });
1492 self.create_registered_function(callable)
1493 }
1494
1495 fn create_scoped_function(&self, cell: Rc<ScopedFnCell>) -> Result<Function> {
1501 let lua_weak = Rc::downgrade(&self.inner);
1502 let callable: LuaRustFunction = Rc::new(move |state| {
1503 let lua = match lua_weak.upgrade() {
1504 Some(inner) => Lua { inner },
1505 None => {
1506 return Err(LuaError::runtime(format_args!(
1507 "Lua callback fired after the state was dropped"
1508 )))
1509 }
1510 };
1511 match catch_unwind(AssertUnwindSafe(|| {
1512 let args = callback_args(state, &lua)?;
1513 let returns = cell.try_call(&lua, args)?;
1514 push_callback_returns(state, &lua, returns)
1515 })) {
1516 Ok(result) => result,
1517 Err(_) => Err(LuaError::runtime(format_args!(
1518 "scoped Rust callback panicked"
1519 ))),
1520 }
1521 });
1522 self.create_registered_function(callable)
1523 }
1524
1525 pub fn gc_collect(&self) {
1527 self.with_state(|state| state.gc().full_collect());
1528 }
1529}
1530
1531pub struct Chunk {
1532 lua: Lua,
1533 source: Vec<u8>,
1534 name: Vec<u8>,
1535}
1536
1537impl Chunk {
1538 pub fn set_name(mut self, name: impl AsRef<[u8]>) -> Self {
1539 self.name = name.as_ref().to_vec();
1540 self
1541 }
1542
1543 pub fn exec(self) -> Result<()> {
1544 self.lua
1545 .with_state(|state| exec_state(state, &self.source, &self.name))
1546 }
1547
1548 pub fn eval<T: FromLuaMulti>(self) -> Result<T> {
1549 let raws = self.lua.with_state(|state| {
1550 let saved_top = state.top_idx();
1551 let status = load_buffer(state, &self.source, &self.name)?;
1552 if status != 0 {
1553 let err = state.pop();
1554 state.set_top_idx(saved_top);
1555 return Err(LuaError::from_value(err));
1556 }
1557 match lua_vm::api::pcall_k(state, 0, T::NRESULTS, 0, 0, None) {
1558 Ok(_) => {
1559 let nresults = if T::NRESULTS < 0 {
1560 state.top_idx().0.saturating_sub(saved_top.0) as i32
1561 } else {
1562 T::NRESULTS
1563 };
1564 let mut values = Vec::with_capacity(nresults as usize);
1565 for _ in 0..nresults {
1566 values.push(state.pop());
1567 }
1568 values.reverse();
1569 state.set_top_idx(saved_top);
1570 Ok(values)
1571 }
1572 Err(err) => {
1573 state.set_top_idx(saved_top);
1574 Err(err)
1575 }
1576 }
1577 })?;
1578 let values = raws
1579 .into_iter()
1580 .map(|raw| Value::from_raw(&self.lua, raw))
1581 .collect::<Result<Vec<_>>>()?;
1582 T::from_lua_multi(values, &self.lua)
1583 }
1584}
1585
1586#[derive(Debug)]
1587struct RootedValue {
1588 lua: Lua,
1589 key: ExternalRootKey,
1590}
1591
1592impl RootedValue {
1593 fn raw(&self) -> Result<RawLuaValue> {
1594 self.lua
1595 .with_state(|state| state.external_rooted_value(self.key))
1596 .ok_or_else(stale_handle_error)
1597 }
1598
1599 fn raw_for_lua(&self, lua: &Lua, state: &LuaState) -> Result<RawLuaValue> {
1600 if !Rc::ptr_eq(&self.lua.inner, &lua.inner) {
1601 return Err(LuaError::runtime(format_args!(
1602 "Lua handle belongs to a different state"
1603 )));
1604 }
1605 state
1606 .external_rooted_value(self.key)
1607 .ok_or_else(stale_handle_error)
1608 }
1609}
1610
1611impl Clone for RootedValue {
1612 fn clone(&self) -> Self {
1613 let raw = self.raw().expect("rooted Lua handle should not be stale");
1614 self.lua.root_raw(raw)
1615 }
1616}
1617
1618impl Drop for RootedValue {
1619 fn drop(&mut self) {
1620 self.lua.unroot_external_key(self.key);
1621 }
1622}
1623
1624#[derive(Debug, Clone)]
1626pub enum Value {
1627 Nil,
1628 Boolean(bool),
1629 Integer(i64),
1630 Number(f64),
1631 String(LuaString),
1632 Table(Table),
1633 Function(Function),
1634 UserData(AnyUserData),
1635 LightUserData(*mut c_void),
1636 Thread(Thread),
1637}
1638
1639impl Value {
1640 fn from_raw(lua: &Lua, raw: RawLuaValue) -> Result<Self> {
1641 lua.with_state(|state| Self::from_raw_in_state(lua, state, raw))
1642 }
1643
1644 fn from_raw_in_state(lua: &Lua, state: &mut LuaState, raw: RawLuaValue) -> Result<Self> {
1645 Ok(match raw {
1646 RawLuaValue::Nil => Value::Nil,
1647 RawLuaValue::Bool(v) => Value::Boolean(v),
1648 RawLuaValue::Int(v) => Value::Integer(v),
1649 RawLuaValue::Float(v) => Value::Number(v),
1650 RawLuaValue::Str(v) => Value::String(LuaString {
1651 root: lua.root_raw_in_state(state, RawLuaValue::Str(v)),
1652 }),
1653 RawLuaValue::Table(v) => Value::Table(Table {
1654 root: lua.root_raw_in_state(state, RawLuaValue::Table(v)),
1655 }),
1656 RawLuaValue::Function(v) => Value::Function(Function {
1657 root: lua.root_raw_in_state(state, RawLuaValue::Function(v)),
1658 }),
1659 RawLuaValue::UserData(v) => {
1660 let host_value = v.host_value();
1661 Value::UserData(AnyUserData {
1662 root: lua.root_raw_in_state(state, RawLuaValue::UserData(v)),
1663 host_value,
1664 })
1665 }
1666 RawLuaValue::LightUserData(v) => Value::LightUserData(v),
1667 RawLuaValue::Thread(v) => Value::Thread(Thread {
1668 root: lua.root_raw_in_state(state, RawLuaValue::Thread(v)),
1669 }),
1670 })
1671 }
1672
1673 fn to_raw_for_lua(&self, lua: &Lua, state: &LuaState) -> Result<RawLuaValue> {
1674 match self {
1675 Value::Nil => Ok(RawLuaValue::Nil),
1676 Value::Boolean(v) => Ok(RawLuaValue::Bool(*v)),
1677 Value::Integer(v) => Ok(RawLuaValue::Int(*v)),
1678 Value::Number(v) => Ok(RawLuaValue::Float(*v)),
1679 Value::String(v) => v.root.raw_for_lua(lua, state),
1680 Value::Table(v) => v.root.raw_for_lua(lua, state),
1681 Value::Function(v) => v.root.raw_for_lua(lua, state),
1682 Value::UserData(v) => v.root.raw_for_lua(lua, state),
1683 Value::LightUserData(v) => Ok(RawLuaValue::LightUserData(*v)),
1684 Value::Thread(v) => v.root.raw_for_lua(lua, state),
1685 }
1686 }
1687}
1688
1689#[derive(Debug, Clone)]
1690pub struct Table {
1691 root: RootedValue,
1692}
1693
1694impl Table {
1695 fn raw_table(&self) -> Result<GcRef<RawLuaTable>> {
1696 match self.root.raw()? {
1697 RawLuaValue::Table(table) => Ok(table),
1698 other => Err(type_error_raw(&other, "table")),
1699 }
1700 }
1701
1702 pub fn get<K, V>(&self, key: K) -> Result<V>
1703 where
1704 K: IntoLua,
1705 V: FromLua,
1706 {
1707 let lua = self.root.lua.clone();
1708 let key = key.into_lua(&lua)?;
1709 let value_raw = lua.with_state(|state| {
1710 let key_raw = key.to_raw_for_lua(&lua, state)?;
1711 let table_raw = self.root.raw_for_lua(&lua, state)?;
1712 state.table_get_with_tm(&table_raw, &key_raw)
1713 })?;
1714 let value = Value::from_raw(&lua, value_raw)?;
1715 V::from_lua(value, &lua)
1716 }
1717
1718 pub fn set<K, V>(&self, key: K, value: V) -> Result<()>
1719 where
1720 K: IntoLua,
1721 V: IntoLua,
1722 {
1723 let lua = self.root.lua.clone();
1724 let key = key.into_lua(&lua)?;
1725 let value = value.into_lua(&lua)?;
1726 lua.with_state(|state| {
1727 let key_raw = key.to_raw_for_lua(&lua, state)?;
1728 let value_raw = value.to_raw_for_lua(&lua, state)?;
1729 let table_raw = self.root.raw_for_lua(&lua, state)?;
1730 state.table_set_with_tm(&table_raw, key_raw, value_raw)
1731 })
1732 }
1733
1734 pub fn len(&self) -> Result<u64> {
1735 Ok(self.raw_table()?.getn())
1736 }
1737}
1738
1739#[derive(Debug, Clone)]
1740pub struct Function {
1741 root: RootedValue,
1742}
1743
1744impl Function {
1745 pub fn call<A, R>(&self, args: A) -> Result<R>
1746 where
1747 A: IntoLuaMulti,
1748 R: FromLuaMulti,
1749 {
1750 let lua = self.root.lua.clone();
1751 let args = args.into_lua_multi(&lua)?;
1752 let result_raws = lua.with_state(|state| {
1753 let arg_raws = args
1754 .iter()
1755 .map(|value| value.to_raw_for_lua(&lua, state))
1756 .collect::<Result<Vec<_>>>()?;
1757 let function_raw = self.root.raw_for_lua(&lua, state)?;
1758 let saved_top = state.top_idx();
1759 state.push(function_raw);
1760 for arg in &arg_raws {
1761 state.push(*arg);
1762 }
1763 match lua_vm::api::pcall_k(state, arg_raws.len() as i32, R::NRESULTS, 0, 0, None) {
1764 Ok(_) => {
1765 let nresults = if R::NRESULTS < 0 {
1766 state.top_idx().0.saturating_sub(saved_top.0) as i32
1767 } else {
1768 R::NRESULTS
1769 };
1770 let mut results = Vec::with_capacity(nresults as usize);
1771 for _ in 0..nresults {
1772 results.push(state.pop());
1773 }
1774 results.reverse();
1775 state.set_top_idx(saved_top);
1776 Ok(results)
1777 }
1778 Err(err) => {
1779 state.set_top_idx(saved_top);
1780 Err(err)
1781 }
1782 }
1783 })?;
1784 let values = result_raws
1785 .into_iter()
1786 .map(|raw| Value::from_raw(&lua, raw))
1787 .collect::<Result<Vec<_>>>()?;
1788 R::from_lua_multi(values, &lua)
1789 }
1790}
1791
1792#[derive(Debug, Clone)]
1793pub struct LuaString {
1794 root: RootedValue,
1795}
1796
1797impl LuaString {
1798 fn raw_string(&self) -> Result<GcRef<RawLuaString>> {
1799 match self.root.raw()? {
1800 RawLuaValue::Str(string) => Ok(string),
1801 other => Err(type_error_raw(&other, "string")),
1802 }
1803 }
1804
1805 pub fn as_bytes(&self) -> Result<Vec<u8>> {
1806 Ok(self.raw_string()?.as_bytes().to_vec())
1807 }
1808
1809 pub fn to_str(&self) -> Result<String> {
1810 let bytes = self.as_bytes()?;
1811 String::from_utf8(bytes)
1812 .map_err(|_| LuaError::runtime(format_args!("string is not valid UTF-8")))
1813 }
1814}
1815
1816#[derive(Clone)]
1817pub struct AnyUserData {
1818 root: RootedValue,
1819 host_value: Option<Rc<dyn Any>>,
1820}
1821
1822impl fmt::Debug for AnyUserData {
1823 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1824 f.debug_struct("AnyUserData")
1825 .field("root", &self.root)
1826 .field("has_host_value", &self.host_value.is_some())
1827 .finish()
1828 }
1829}
1830
1831impl AnyUserData {
1832 fn host_cell<T: 'static>(&self) -> Result<&UserDataCell<T>> {
1833 let host = self
1834 .host_value
1835 .as_deref()
1836 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust userdata payload")))?;
1837 host.downcast_ref::<UserDataCell<T>>()
1838 .ok_or_else(|| LuaError::runtime(format_args!("userdata type mismatch")))
1839 }
1840
1841 pub fn borrow<T>(&self) -> Result<Ref<'_, T>>
1842 where
1843 T: 'static,
1844 {
1845 self.host_cell::<T>()?
1846 .value
1847 .try_borrow()
1848 .map_err(|_| LuaError::runtime(format_args!("userdata is already mutably borrowed")))
1849 }
1850
1851 pub fn borrow_mut<T>(&self) -> Result<RefMut<'_, T>>
1852 where
1853 T: 'static,
1854 {
1855 self.host_cell::<T>()?
1856 .value
1857 .try_borrow_mut()
1858 .map_err(|_| LuaError::runtime(format_args!("userdata is already borrowed")))
1859 }
1860
1861 pub fn with_borrow<T, R>(&self, f: impl FnOnce(&T) -> R) -> Result<R>
1862 where
1863 T: 'static,
1864 {
1865 let value = self.borrow::<T>()?;
1866 Ok(f(&value))
1867 }
1868
1869 pub fn with_borrow_mut<T, R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R>
1870 where
1871 T: 'static,
1872 {
1873 let mut value = self.borrow_mut::<T>()?;
1874 Ok(f(&mut value))
1875 }
1876
1877 fn host_scoped_cell<T: 'static>(&self) -> Result<&ScopedCell<T>> {
1880 let host = self
1881 .host_value
1882 .as_deref()
1883 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust userdata payload")))?;
1884 host.downcast_ref::<ScopedCell<T>>()
1885 .ok_or_else(|| LuaError::runtime(format_args!("scoped userdata type mismatch")))
1886 }
1887
1888 pub fn scoped_borrow<T, R>(&self, f: impl FnOnce(&T) -> R) -> Result<R>
1893 where
1894 T: 'static,
1895 {
1896 let cell = self.host_scoped_cell::<T>()?;
1897 let guard = cell.try_borrow()?;
1898 Ok(f(&*guard))
1899 }
1900
1901 pub fn scoped_borrow_mut<T, R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R>
1904 where
1905 T: 'static,
1906 {
1907 let cell = self.host_scoped_cell::<T>()?;
1908 let mut guard = cell.try_borrow_mut()?;
1909 Ok(f(&mut *guard))
1910 }
1911
1912 pub fn delegate<P, S, F>(&self, lua: &Lua, accessor: F) -> Result<AnyUserData>
1927 where
1928 P: UserData,
1929 S: UserData,
1930 F: Fn(&mut P) -> &mut S + 'static,
1931 {
1932 let host = self
1933 .host_value
1934 .as_ref()
1935 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust userdata payload")))?;
1936
1937 if let Ok(parent_cell) = Rc::clone(host).downcast::<ScopedCell<P>>() {
1941 let parent_for_closure = Rc::clone(&parent_cell);
1942 let enter: Box<dyn Fn(&mut dyn FnMut(&mut S)) -> Result<()>> =
1943 Box::new(move |f| {
1944 let mut guard = parent_for_closure.try_borrow_mut()?;
1945 f(accessor(&mut *guard));
1946 Ok(())
1947 });
1948 let cell = Rc::new(DelegatedCell::<S> {
1949 enter: RefCell::new(Some(DelegateEnter::Mut(enter))),
1950 });
1951 return lua.create_delegated_userdata::<S>(cell);
1952 }
1953
1954 if let Ok(parent_cell) = Rc::clone(host).downcast::<DelegatedCell<P>>() {
1955 let parent_for_closure = Rc::clone(&parent_cell);
1956 let enter: Box<dyn Fn(&mut dyn FnMut(&mut S)) -> Result<()>> =
1957 Box::new(move |f| {
1958 parent_for_closure.enter_mut(&mut |p| {
1959 f(accessor(p));
1960 })
1961 });
1962 let cell = Rc::new(DelegatedCell::<S> {
1963 enter: RefCell::new(Some(DelegateEnter::Mut(enter))),
1964 });
1965 return lua.create_delegated_userdata::<S>(cell);
1966 }
1967
1968 Err(LuaError::runtime(format_args!(
1969 "delegate: receiver is not a scoped userdata of the expected type"
1970 )))
1971 }
1972
1973 pub fn delegate_ref<P, S, F>(&self, lua: &Lua, accessor: F) -> Result<AnyUserData>
1978 where
1979 P: UserData,
1980 S: UserData,
1981 F: Fn(&P) -> &S + 'static,
1982 {
1983 let host = self
1984 .host_value
1985 .as_ref()
1986 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust userdata payload")))?;
1987
1988 if let Ok(parent_cell) = Rc::clone(host).downcast::<ScopedCell<P>>() {
1989 let parent_for_closure = Rc::clone(&parent_cell);
1990 let enter: Box<dyn Fn(&mut dyn FnMut(&S)) -> Result<()>> = Box::new(move |f| {
1991 let guard = parent_for_closure.try_borrow()?;
1992 f(accessor(&*guard));
1993 Ok(())
1994 });
1995 let cell = Rc::new(DelegatedCell::<S> {
1996 enter: RefCell::new(Some(DelegateEnter::Ref(enter))),
1997 });
1998 return lua.create_delegated_userdata::<S>(cell);
1999 }
2000
2001 if let Ok(parent_cell) = Rc::clone(host).downcast::<DelegatedCell<P>>() {
2002 let parent_for_closure = Rc::clone(&parent_cell);
2003 let enter: Box<dyn Fn(&mut dyn FnMut(&S)) -> Result<()>> = Box::new(move |f| {
2004 parent_for_closure.enter_ref(&mut |p| {
2005 f(accessor(p));
2006 })
2007 });
2008 let cell = Rc::new(DelegatedCell::<S> {
2009 enter: RefCell::new(Some(DelegateEnter::Ref(enter))),
2010 });
2011 return lua.create_delegated_userdata::<S>(cell);
2012 }
2013
2014 Err(LuaError::runtime(format_args!(
2015 "delegate_ref: receiver is not a scoped userdata of the expected type"
2016 )))
2017 }
2018}
2019
2020#[derive(Debug, Clone)]
2021pub struct Thread {
2022 root: RootedValue,
2023}
2024
2025#[derive(Debug, Clone, Default, PartialEq, Eq)]
2031pub struct Variadic<T>(Vec<T>);
2032
2033impl<T> Variadic<T> {
2034 pub const fn new() -> Self {
2035 Self(Vec::new())
2036 }
2037
2038 pub fn with_capacity(capacity: usize) -> Self {
2039 Self(Vec::with_capacity(capacity))
2040 }
2041
2042 pub fn into_vec(self) -> Vec<T> {
2043 self.0
2044 }
2045}
2046
2047impl<T> Deref for Variadic<T> {
2048 type Target = Vec<T>;
2049
2050 fn deref(&self) -> &Self::Target {
2051 &self.0
2052 }
2053}
2054
2055impl<T> DerefMut for Variadic<T> {
2056 fn deref_mut(&mut self) -> &mut Self::Target {
2057 &mut self.0
2058 }
2059}
2060
2061impl<T> From<Vec<T>> for Variadic<T> {
2062 fn from(value: Vec<T>) -> Self {
2063 Self(value)
2064 }
2065}
2066
2067impl<T> From<Variadic<T>> for Vec<T> {
2068 fn from(value: Variadic<T>) -> Self {
2069 value.0
2070 }
2071}
2072
2073impl<T> FromIterator<T> for Variadic<T> {
2074 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
2075 Self(Vec::from_iter(iter))
2076 }
2077}
2078
2079impl<T> IntoIterator for Variadic<T> {
2080 type Item = T;
2081 type IntoIter = std::vec::IntoIter<T>;
2082
2083 fn into_iter(self) -> Self::IntoIter {
2084 self.0.into_iter()
2085 }
2086}
2087
2088pub trait UserData: 'static {
2089 fn add_methods<M: UserDataMethods<Self>>(_methods: &mut M)
2090 where
2091 Self: Sized,
2092 {
2093 }
2094
2095 fn add_meta_methods<M: UserDataMethods<Self>>(_methods: &mut M)
2096 where
2097 Self: Sized,
2098 {
2099 }
2100}
2101
2102pub trait UserDataMethods<T: UserData> {
2103 fn add_method<A, R, F>(&mut self, name: &str, method: F)
2104 where
2105 A: FromLuaMulti + 'static,
2106 R: IntoLuaMulti + 'static,
2107 F: Fn(&Lua, &T, A) -> Result<R> + 'static;
2108
2109 fn add_method_mut<A, R, F>(&mut self, name: &str, method: F)
2110 where
2111 A: FromLuaMulti + 'static,
2112 R: IntoLuaMulti + 'static,
2113 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static;
2114
2115 fn add_meta_method<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
2116 where
2117 A: FromLuaMulti + 'static,
2118 R: IntoLuaMulti + 'static,
2119 F: Fn(&Lua, &T, A) -> Result<R> + 'static;
2120
2121 fn add_meta_method_mut<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
2122 where
2123 A: FromLuaMulti + 'static,
2124 R: IntoLuaMulti + 'static,
2125 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static;
2126
2127 fn add_field_method_get<R, F>(&mut self, name: &str, getter: F)
2131 where
2132 R: IntoLuaMulti + 'static,
2133 F: Fn(&Lua, &T) -> Result<R> + 'static;
2134
2135 fn add_field_method_set<A, F>(&mut self, name: &str, setter: F)
2138 where
2139 A: FromLuaMulti + 'static,
2140 F: Fn(&Lua, &mut T, A) -> Result<()> + 'static;
2141
2142 fn add_function<A, R, F>(&mut self, name: &str, function: F)
2152 where
2153 A: FromLuaMulti + 'static,
2154 R: IntoLuaMulti + 'static,
2155 F: Fn(&Lua, A) -> Result<R> + 'static;
2156
2157 fn add_function_mut<A, R, F>(&mut self, name: &str, function: F)
2160 where
2161 A: FromLuaMulti + 'static,
2162 R: IntoLuaMulti + 'static,
2163 F: FnMut(&Lua, A) -> Result<R> + 'static;
2164}
2165
2166#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2167pub enum MetaMethod {
2168 Index,
2169 NewIndex,
2170 Add,
2171 Sub,
2172 Mul,
2173 Div,
2174 Mod,
2175 Pow,
2176 Unm,
2177 Len,
2178 Eq,
2179 Lt,
2180 Le,
2181 Concat,
2182 Call,
2183 ToString,
2184 Pairs,
2185}
2186
2187impl MetaMethod {
2188 fn name(self) -> &'static str {
2189 match self {
2190 MetaMethod::Index => "__index",
2191 MetaMethod::NewIndex => "__newindex",
2192 MetaMethod::Add => "__add",
2193 MetaMethod::Sub => "__sub",
2194 MetaMethod::Mul => "__mul",
2195 MetaMethod::Div => "__div",
2196 MetaMethod::Mod => "__mod",
2197 MetaMethod::Pow => "__pow",
2198 MetaMethod::Unm => "__unm",
2199 MetaMethod::Len => "__len",
2200 MetaMethod::Eq => "__eq",
2201 MetaMethod::Lt => "__lt",
2202 MetaMethod::Le => "__le",
2203 MetaMethod::Concat => "__concat",
2204 MetaMethod::Call => "__call",
2205 MetaMethod::ToString => "__tostring",
2206 MetaMethod::Pairs => "__pairs",
2207 }
2208 }
2209}
2210
2211fn root_for_state_lifetime(state: &mut LuaState, value: RawLuaValue) {
2222 let _ = state.external_root_value(value);
2223}
2224
2225#[derive(Clone, Copy)]
2230enum RegistryMode {
2231 Owned,
2232 Scoped,
2233}
2234
2235struct UserDataMethodRegistry<'lua, T: UserData> {
2236 lua: &'lua Lua,
2237 mode: RegistryMode,
2238 methods: Vec<(String, Function)>,
2239 meta_methods: Vec<(MetaMethod, Function)>,
2240 fields_get: Vec<(String, Function)>,
2241 fields_set: Vec<(String, Function)>,
2242 error: Option<LuaError>,
2243 _marker: std::marker::PhantomData<T>,
2244}
2245
2246impl<'lua, T: UserData> UserDataMethodRegistry<'lua, T> {
2247 fn new(lua: &'lua Lua) -> Self {
2248 Self::with_mode(lua, RegistryMode::Owned)
2249 }
2250
2251 fn new_scoped(lua: &'lua Lua) -> Self {
2252 Self::with_mode(lua, RegistryMode::Scoped)
2253 }
2254
2255 fn with_mode(lua: &'lua Lua, mode: RegistryMode) -> Self {
2256 Self {
2257 lua,
2258 mode,
2259 methods: Vec::new(),
2260 meta_methods: Vec::new(),
2261 fields_get: Vec::new(),
2262 fields_set: Vec::new(),
2263 error: None,
2264 _marker: std::marker::PhantomData,
2265 }
2266 }
2267
2268 fn record(&mut self, result: Result<Function>, insert: impl FnOnce(&mut Self, Function)) {
2269 if self.error.is_some() {
2270 return;
2271 }
2272 match result {
2273 Ok(function) => insert(self, function),
2274 Err(err) => self.error = Some(err),
2275 }
2276 }
2277
2278 fn build_metatable(mut self) -> Result<GcRef<RawLuaTable>> {
2282 if let Some(err) = self.error.take() {
2283 return Err(err);
2284 }
2285
2286 let lua = self.lua;
2287
2288 let method_table = lua.create_table()?;
2289 for (name, function) in &self.methods {
2290 method_table.set(name.as_str(), function)?;
2291 }
2292
2293 let field_getters = lua.create_table()?;
2294 for (name, function) in &self.fields_get {
2295 field_getters.set(name.as_str(), function)?;
2296 }
2297 let field_setters = lua.create_table()?;
2298 for (name, function) in &self.fields_set {
2299 field_setters.set(name.as_str(), function)?;
2300 }
2301
2302 let metatable = lua.create_table()?;
2305 let mut raw_index: Option<Function> = None;
2306 let mut raw_newindex: Option<Function> = None;
2307 for (metamethod, function) in &self.meta_methods {
2308 match metamethod {
2309 MetaMethod::Index => raw_index = Some(function.clone()),
2310 MetaMethod::NewIndex => raw_newindex = Some(function.clone()),
2311 other => {
2312 metatable.set(other.name(), function)?;
2313 }
2314 }
2315 }
2316
2317 let has_fields_get = !self.fields_get.is_empty();
2335 let has_methods = !self.methods.is_empty();
2336 let needs_index_composition = has_fields_get || (raw_index.is_some() && has_methods);
2337
2338 if needs_index_composition {
2339 let (getters_raw, methods_raw, raw_index_raw) = lua.with_state(|state| {
2340 let g = match field_getters.root.raw_for_lua(lua, state)? {
2341 RawLuaValue::Table(g) => g,
2342 v => return Err(type_error_raw(&v, "table")),
2343 };
2344 root_for_state_lifetime(state, RawLuaValue::Table(g.clone()));
2345 let m = match method_table.root.raw_for_lua(lua, state)? {
2346 RawLuaValue::Table(m) => m,
2347 v => return Err(type_error_raw(&v, "table")),
2348 };
2349 root_for_state_lifetime(state, RawLuaValue::Table(m.clone()));
2350 let r = match &raw_index {
2351 Some(f) => {
2352 let rv = f.root.raw_for_lua(lua, state)?;
2353 root_for_state_lifetime(state, rv.clone());
2354 Some(rv)
2355 }
2356 None => None,
2357 };
2358 Ok::<_, LuaError>((g, m, r))
2359 })?;
2360 let index_fn = lua.create_function(move |lua, (ud, key): (Value, Value)| {
2361 let getters = Table {
2362 root: lua.root_raw(RawLuaValue::Table(getters_raw.clone())),
2363 };
2364 let methods = Table {
2365 root: lua.root_raw(RawLuaValue::Table(methods_raw.clone())),
2366 };
2367 if let Value::Function(getter) = getters.get::<_, Value>(key.clone())? {
2368 return getter.call::<_, Value>(ud);
2369 }
2370 let method = methods.get::<_, Value>(key.clone())?;
2371 if !matches!(method, Value::Nil) {
2372 return Ok(method);
2373 }
2374 if let Some(raw_idx) = &raw_index_raw {
2375 let raw_fn = Function {
2376 root: lua.root_raw(raw_idx.clone()),
2377 };
2378 return raw_fn.call::<_, Value>((ud, key));
2379 }
2380 Ok(Value::Nil)
2381 })?;
2382 metatable.set(MetaMethod::Index.name(), &index_fn)?;
2383 } else if let Some(raw) = raw_index.as_ref() {
2384 metatable.set(MetaMethod::Index.name(), raw)?;
2385 } else {
2386 metatable.set(MetaMethod::Index.name(), &method_table)?;
2387 }
2388
2389 let has_fields_set = !self.fields_set.is_empty();
2392
2393 if has_fields_set {
2394 let (setters_raw, raw_newindex_raw) = lua.with_state(|state| {
2395 let s = match field_setters.root.raw_for_lua(lua, state)? {
2396 RawLuaValue::Table(s) => s,
2397 v => return Err(type_error_raw(&v, "table")),
2398 };
2399 root_for_state_lifetime(state, RawLuaValue::Table(s.clone()));
2400 let r = match &raw_newindex {
2401 Some(f) => {
2402 let rv = f.root.raw_for_lua(lua, state)?;
2403 root_for_state_lifetime(state, rv.clone());
2404 Some(rv)
2405 }
2406 None => None,
2407 };
2408 Ok::<_, LuaError>((s, r))
2409 })?;
2410 let newindex_fn =
2411 lua.create_function(move |lua, (ud, key, value): (Value, Value, Value)| {
2412 let setters = Table {
2413 root: lua.root_raw(RawLuaValue::Table(setters_raw.clone())),
2414 };
2415 if let Value::Function(setter) = setters.get::<_, Value>(key.clone())? {
2416 return setter.call::<_, Value>((ud, value));
2417 }
2418 if let Some(raw) = &raw_newindex_raw {
2419 let raw_fn = Function {
2420 root: lua.root_raw(raw.clone()),
2421 };
2422 return raw_fn.call::<_, Value>((ud, key, value));
2423 }
2424 Err(LuaError::runtime(format_args!(
2425 "cannot assign to unknown or read-only userdata field"
2426 )))
2427 })?;
2428 metatable.set(MetaMethod::NewIndex.name(), &newindex_fn)?;
2429 } else if let Some(raw) = raw_newindex.as_ref() {
2430 metatable.set(MetaMethod::NewIndex.name(), raw)?;
2431 }
2432
2433 self.lua.with_state(|state| {
2434 let metatable_raw = metatable.root.raw_for_lua(self.lua, state)?;
2435 let RawLuaValue::Table(metatable) = metatable_raw else {
2436 return Err(type_error_raw(&metatable_raw, "table"));
2437 };
2438 root_for_state_lifetime(state, RawLuaValue::Table(metatable.clone()));
2439 Ok(metatable)
2440 })
2441 }
2442}
2443
2444impl<T: UserData> UserDataMethods<T> for UserDataMethodRegistry<'_, T> {
2445 fn add_method<A, R, F>(&mut self, name: &str, method: F)
2446 where
2447 A: FromLuaMulti + 'static,
2448 R: IntoLuaMulti + 'static,
2449 F: Fn(&Lua, &T, A) -> Result<R> + 'static,
2450 {
2451 let name = name.to_string();
2452 let result = match self.mode {
2453 RegistryMode::Owned => self.lua.create_userdata_method(method),
2454 RegistryMode::Scoped => self.lua.create_scoped_userdata_method(method),
2455 };
2456 self.record(result, move |this, function| {
2457 this.methods.push((name, function));
2458 });
2459 }
2460
2461 fn add_method_mut<A, R, F>(&mut self, name: &str, method: F)
2462 where
2463 A: FromLuaMulti + 'static,
2464 R: IntoLuaMulti + 'static,
2465 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
2466 {
2467 let name = name.to_string();
2468 let result = match self.mode {
2469 RegistryMode::Owned => self.lua.create_userdata_method_mut(method),
2470 RegistryMode::Scoped => self.lua.create_scoped_userdata_method_mut(method),
2471 };
2472 self.record(result, move |this, function| {
2473 this.methods.push((name, function));
2474 });
2475 }
2476
2477 fn add_meta_method<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
2478 where
2479 A: FromLuaMulti + 'static,
2480 R: IntoLuaMulti + 'static,
2481 F: Fn(&Lua, &T, A) -> Result<R> + 'static,
2482 {
2483 let result = match self.mode {
2484 RegistryMode::Owned => self.lua.create_userdata_method(method),
2485 RegistryMode::Scoped => self.lua.create_scoped_userdata_method(method),
2486 };
2487 self.record(result, move |this, function| {
2488 this.meta_methods.push((metamethod, function));
2489 });
2490 }
2491
2492 fn add_meta_method_mut<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
2493 where
2494 A: FromLuaMulti + 'static,
2495 R: IntoLuaMulti + 'static,
2496 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
2497 {
2498 let result = match self.mode {
2499 RegistryMode::Owned => self.lua.create_userdata_method_mut(method),
2500 RegistryMode::Scoped => self.lua.create_scoped_userdata_method_mut(method),
2501 };
2502 self.record(result, move |this, function| {
2503 this.meta_methods.push((metamethod, function));
2504 });
2505 }
2506
2507 fn add_field_method_get<R, F>(&mut self, name: &str, getter: F)
2508 where
2509 R: IntoLuaMulti + 'static,
2510 F: Fn(&Lua, &T) -> Result<R> + 'static,
2511 {
2512 let name = name.to_string();
2513 let wrapped = move |lua: &Lua, this: &T, ()| getter(lua, this);
2514 let result = match self.mode {
2515 RegistryMode::Owned => self.lua.create_userdata_method(wrapped),
2516 RegistryMode::Scoped => self.lua.create_scoped_userdata_method(wrapped),
2517 };
2518 self.record(result, move |this, function| {
2519 this.fields_get.push((name, function));
2520 });
2521 }
2522
2523 fn add_field_method_set<A, F>(&mut self, name: &str, setter: F)
2524 where
2525 A: FromLuaMulti + 'static,
2526 F: Fn(&Lua, &mut T, A) -> Result<()> + 'static,
2527 {
2528 let name = name.to_string();
2529 let wrapped = move |lua: &Lua, this: &mut T, arg: A| setter(lua, this, arg);
2530 let result = match self.mode {
2531 RegistryMode::Owned => self.lua.create_userdata_method_mut(wrapped),
2532 RegistryMode::Scoped => self.lua.create_scoped_userdata_method_mut(wrapped),
2533 };
2534 self.record(result, move |this, function| {
2535 this.fields_set.push((name, function));
2536 });
2537 }
2538
2539 fn add_function<A, R, F>(&mut self, name: &str, function: F)
2540 where
2541 A: FromLuaMulti + 'static,
2542 R: IntoLuaMulti + 'static,
2543 F: Fn(&Lua, A) -> Result<R> + 'static,
2544 {
2545 let name = name.to_string();
2546 let result = self.lua.create_function(function);
2550 self.record(result, move |this, function| {
2551 this.methods.push((name, function));
2552 });
2553 }
2554
2555 fn add_function_mut<A, R, F>(&mut self, name: &str, function: F)
2556 where
2557 A: FromLuaMulti + 'static,
2558 R: IntoLuaMulti + 'static,
2559 F: FnMut(&Lua, A) -> Result<R> + 'static,
2560 {
2561 let name = name.to_string();
2562 let result = self.lua.create_function_mut(function);
2563 self.record(result, move |this, function| {
2564 this.methods.push((name, function));
2565 });
2566 }
2567}
2568
2569pub trait IntoLua {
2570 fn into_lua(self, lua: &Lua) -> Result<Value>;
2571}
2572
2573pub trait FromLua: Sized {
2574 fn from_lua(value: Value, lua: &Lua) -> Result<Self>;
2575}
2576
2577pub trait IntoLuaMulti {
2578 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>>;
2579}
2580
2581pub trait FromLuaMulti: Sized {
2582 const NRESULTS: i32;
2583
2584 fn from_lua_multi(values: Vec<Value>, lua: &Lua) -> Result<Self>;
2585}
2586
2587impl IntoLua for Value {
2588 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2589 Ok(self)
2590 }
2591}
2592
2593impl IntoLua for &Value {
2594 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2595 Ok(self.clone())
2596 }
2597}
2598
2599impl FromLua for Value {
2600 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2601 Ok(value)
2602 }
2603}
2604
2605impl IntoLua for bool {
2606 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2607 Ok(Value::Boolean(self))
2608 }
2609}
2610
2611impl FromLua for bool {
2612 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2613 match value {
2614 Value::Boolean(v) => Ok(v),
2615 other => Err(type_error_value(&other, "boolean")),
2616 }
2617 }
2618}
2619
2620impl IntoLua for i64 {
2621 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2622 Ok(Value::Integer(self))
2623 }
2624}
2625
2626impl FromLua for i64 {
2627 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2628 match value {
2629 Value::Integer(v) => Ok(v),
2630 Value::Number(v) if v.fract() == 0.0 && v.is_finite() => Ok(v as i64),
2631 other => Err(type_error_value(&other, "integer")),
2632 }
2633 }
2634}
2635
2636impl IntoLua for i32 {
2637 fn into_lua(self, lua: &Lua) -> Result<Value> {
2638 i64::from(self).into_lua(lua)
2639 }
2640}
2641
2642impl FromLua for i32 {
2643 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
2644 let v = i64::from_lua(value, lua)?;
2645 i32::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
2646 }
2647}
2648
2649impl IntoLua for usize {
2650 fn into_lua(self, lua: &Lua) -> Result<Value> {
2651 let v = i64::try_from(self)
2652 .map_err(|_| LuaError::runtime(format_args!("integer out of range")))?;
2653 v.into_lua(lua)
2654 }
2655}
2656
2657impl FromLua for usize {
2658 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
2659 let v = i64::from_lua(value, lua)?;
2660 usize::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
2661 }
2662}
2663
2664impl IntoLua for u64 {
2665 fn into_lua(self, lua: &Lua) -> Result<Value> {
2666 let v = i64::try_from(self)
2667 .map_err(|_| LuaError::runtime(format_args!("integer out of range")))?;
2668 v.into_lua(lua)
2669 }
2670}
2671
2672impl FromLua for u64 {
2673 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
2674 let v = i64::from_lua(value, lua)?;
2675 u64::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
2676 }
2677}
2678
2679impl IntoLua for u32 {
2680 fn into_lua(self, lua: &Lua) -> Result<Value> {
2681 u64::from(self).into_lua(lua)
2682 }
2683}
2684
2685impl FromLua for u32 {
2686 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
2687 let v = u64::from_lua(value, lua)?;
2688 u32::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
2689 }
2690}
2691
2692impl IntoLua for f64 {
2693 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2694 Ok(Value::Number(self))
2695 }
2696}
2697
2698impl FromLua for f64 {
2699 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2700 match value {
2701 Value::Integer(v) => Ok(v as f64),
2702 Value::Number(v) => Ok(v),
2703 other => Err(type_error_value(&other, "number")),
2704 }
2705 }
2706}
2707
2708impl IntoLua for &str {
2709 fn into_lua(self, lua: &Lua) -> Result<Value> {
2710 Ok(Value::String(lua.create_string(self.as_bytes())?))
2711 }
2712}
2713
2714impl IntoLua for String {
2715 fn into_lua(self, lua: &Lua) -> Result<Value> {
2716 Ok(Value::String(lua.create_string(self.into_bytes())?))
2717 }
2718}
2719
2720impl FromLua for String {
2721 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2722 match value {
2723 Value::String(s) => s.to_str(),
2724 other => Err(type_error_value(&other, "string")),
2725 }
2726 }
2727}
2728
2729impl IntoLua for &[u8] {
2730 fn into_lua(self, lua: &Lua) -> Result<Value> {
2731 Ok(Value::String(lua.create_string(self)?))
2732 }
2733}
2734
2735impl IntoLua for LuaString {
2736 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2737 Ok(Value::String(self))
2738 }
2739}
2740
2741impl IntoLua for &LuaString {
2742 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2743 Ok(Value::String(self.clone()))
2744 }
2745}
2746
2747impl FromLua for LuaString {
2748 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2749 match value {
2750 Value::String(v) => Ok(v),
2751 other => Err(type_error_value(&other, "string")),
2752 }
2753 }
2754}
2755
2756impl IntoLua for Table {
2757 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2758 Ok(Value::Table(self))
2759 }
2760}
2761
2762impl IntoLua for &Table {
2763 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2764 Ok(Value::Table(self.clone()))
2765 }
2766}
2767
2768impl FromLua for Table {
2769 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2770 match value {
2771 Value::Table(v) => Ok(v),
2772 other => Err(type_error_value(&other, "table")),
2773 }
2774 }
2775}
2776
2777impl IntoLua for Function {
2778 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2779 Ok(Value::Function(self))
2780 }
2781}
2782
2783impl IntoLua for &Function {
2784 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2785 Ok(Value::Function(self.clone()))
2786 }
2787}
2788
2789impl FromLua for Function {
2790 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2791 match value {
2792 Value::Function(v) => Ok(v),
2793 other => Err(type_error_value(&other, "function")),
2794 }
2795 }
2796}
2797
2798impl IntoLua for AnyUserData {
2799 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2800 Ok(Value::UserData(self))
2801 }
2802}
2803
2804impl IntoLua for &AnyUserData {
2805 fn into_lua(self, _lua: &Lua) -> Result<Value> {
2806 Ok(Value::UserData(self.clone()))
2807 }
2808}
2809
2810impl FromLua for AnyUserData {
2811 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
2812 match value {
2813 Value::UserData(v) => Ok(v),
2814 other => Err(type_error_value(&other, "userdata")),
2815 }
2816 }
2817}
2818
2819impl<T> IntoLua for T
2820where
2821 T: UserData,
2822{
2823 fn into_lua(self, lua: &Lua) -> Result<Value> {
2824 Ok(Value::UserData(lua.create_userdata(self)?))
2825 }
2826}
2827
2828impl<T> IntoLua for Option<T>
2829where
2830 T: IntoLua,
2831{
2832 fn into_lua(self, lua: &Lua) -> Result<Value> {
2833 match self {
2834 Some(value) => value.into_lua(lua),
2835 None => Ok(Value::Nil),
2836 }
2837 }
2838}
2839
2840impl<T> FromLua for Option<T>
2841where
2842 T: FromLua,
2843{
2844 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
2845 match value {
2846 Value::Nil => Ok(None),
2847 other => T::from_lua(other, lua).map(Some),
2848 }
2849 }
2850}
2851
2852impl<T> IntoLua for Vec<T>
2853where
2854 T: IntoLua,
2855{
2856 fn into_lua(self, lua: &Lua) -> Result<Value> {
2857 let table = lua.create_table()?;
2858 for (idx, value) in self.into_iter().enumerate() {
2859 table.set((idx + 1) as i64, value)?;
2860 }
2861 Ok(Value::Table(table))
2862 }
2863}
2864
2865impl<T> FromLua for Vec<T>
2866where
2867 T: FromLua,
2868{
2869 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
2870 let table = Table::from_lua(value, lua)?;
2871 let raw = table.raw_table()?;
2872 let len = raw.getn();
2873 let mut out = Vec::with_capacity(len as usize);
2874 for idx in 1..=len {
2875 let value = Value::from_raw(lua, raw.get_int(idx as i64))?;
2876 out.push(T::from_lua(value, lua)?);
2877 }
2878 Ok(out)
2879 }
2880}
2881
2882impl<K, V> IntoLua for HashMap<K, V>
2883where
2884 K: IntoLua,
2885 V: IntoLua,
2886{
2887 fn into_lua(self, lua: &Lua) -> Result<Value> {
2888 let table = lua.create_table()?;
2889 for (key, value) in self {
2890 table.set(key, value)?;
2891 }
2892 Ok(Value::Table(table))
2893 }
2894}
2895
2896impl<K, V> FromLua for HashMap<K, V>
2897where
2898 K: FromLua + Eq + Hash,
2899 V: FromLua,
2900{
2901 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
2902 let table = Table::from_lua(value, lua)?;
2903 let raw = table.raw_table()?;
2904 let mut out = HashMap::new();
2905 let mut result = Ok(());
2906 raw.for_each_entry(|key, value| {
2907 if result.is_err() {
2908 return;
2909 }
2910 result = (|| {
2911 let key = Value::from_raw(lua, *key)?;
2912 let value = Value::from_raw(lua, *value)?;
2913 out.insert(K::from_lua(key, lua)?, V::from_lua(value, lua)?);
2914 Ok(())
2915 })();
2916 });
2917 result?;
2918 Ok(out)
2919 }
2920}
2921
2922impl<T> IntoLuaMulti for Variadic<T>
2923where
2924 T: IntoLua,
2925{
2926 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
2927 self.into_iter().map(|value| value.into_lua(lua)).collect()
2928 }
2929}
2930
2931impl<T> FromLuaMulti for Variadic<T>
2932where
2933 T: FromLua,
2934{
2935 const NRESULTS: i32 = -1;
2936
2937 fn from_lua_multi(values: Vec<Value>, lua: &Lua) -> Result<Self> {
2938 values
2939 .into_iter()
2940 .map(|value| T::from_lua(value, lua))
2941 .collect()
2942 }
2943}
2944
2945impl IntoLuaMulti for () {
2946 fn into_lua_multi(self, _lua: &Lua) -> Result<Vec<Value>> {
2947 Ok(Vec::new())
2948 }
2949}
2950
2951impl<T> IntoLuaMulti for T
2952where
2953 T: IntoLua,
2954{
2955 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
2956 Ok(vec![self.into_lua(lua)?])
2957 }
2958}
2959
2960impl<A, B> IntoLuaMulti for (A, B)
2961where
2962 A: IntoLua,
2963 B: IntoLua,
2964{
2965 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
2966 Ok(vec![self.0.into_lua(lua)?, self.1.into_lua(lua)?])
2967 }
2968}
2969
2970impl<A, T> IntoLuaMulti for (A, Variadic<T>)
2971where
2972 A: IntoLua,
2973 T: IntoLua,
2974{
2975 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
2976 let mut values = vec![self.0.into_lua(lua)?];
2977 values.extend(self.1.into_lua_multi(lua)?);
2978 Ok(values)
2979 }
2980}
2981
2982impl<A, B, C> IntoLuaMulti for (A, B, C)
2983where
2984 A: IntoLua,
2985 B: IntoLua,
2986 C: IntoLua,
2987{
2988 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
2989 Ok(vec![
2990 self.0.into_lua(lua)?,
2991 self.1.into_lua(lua)?,
2992 self.2.into_lua(lua)?,
2993 ])
2994 }
2995}
2996
2997impl<A, B, T> IntoLuaMulti for (A, B, Variadic<T>)
2998where
2999 A: IntoLua,
3000 B: IntoLua,
3001 T: IntoLua,
3002{
3003 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
3004 let mut values = vec![self.0.into_lua(lua)?, self.1.into_lua(lua)?];
3005 values.extend(self.2.into_lua_multi(lua)?);
3006 Ok(values)
3007 }
3008}
3009
3010impl FromLuaMulti for () {
3011 const NRESULTS: i32 = 0;
3012
3013 fn from_lua_multi(_values: Vec<Value>, _lua: &Lua) -> Result<Self> {
3014 Ok(())
3015 }
3016}
3017
3018impl<T> FromLuaMulti for T
3019where
3020 T: FromLua,
3021{
3022 const NRESULTS: i32 = 1;
3023
3024 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
3025 let value = if values.is_empty() {
3026 Value::Nil
3027 } else {
3028 values.remove(0)
3029 };
3030 T::from_lua(value, lua)
3031 }
3032}
3033
3034impl<A, B> FromLuaMulti for (A, B)
3035where
3036 A: FromLua,
3037 B: FromLua,
3038{
3039 const NRESULTS: i32 = 2;
3040
3041 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
3042 let first = if values.is_empty() {
3043 Value::Nil
3044 } else {
3045 values.remove(0)
3046 };
3047 let second = if values.is_empty() {
3048 Value::Nil
3049 } else {
3050 values.remove(0)
3051 };
3052 Ok((A::from_lua(first, lua)?, B::from_lua(second, lua)?))
3053 }
3054}
3055
3056impl<A, T> FromLuaMulti for (A, Variadic<T>)
3057where
3058 A: FromLua,
3059 T: FromLua,
3060{
3061 const NRESULTS: i32 = -1;
3062
3063 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
3064 let first = if values.is_empty() {
3065 Value::Nil
3066 } else {
3067 values.remove(0)
3068 };
3069 Ok((
3070 A::from_lua(first, lua)?,
3071 Variadic::from_lua_multi(values, lua)?,
3072 ))
3073 }
3074}
3075
3076impl<A, B, C> FromLuaMulti for (A, B, C)
3077where
3078 A: FromLua,
3079 B: FromLua,
3080 C: FromLua,
3081{
3082 const NRESULTS: i32 = 3;
3083
3084 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
3085 let first = if values.is_empty() {
3086 Value::Nil
3087 } else {
3088 values.remove(0)
3089 };
3090 let second = if values.is_empty() {
3091 Value::Nil
3092 } else {
3093 values.remove(0)
3094 };
3095 let third = if values.is_empty() {
3096 Value::Nil
3097 } else {
3098 values.remove(0)
3099 };
3100 Ok((
3101 A::from_lua(first, lua)?,
3102 B::from_lua(second, lua)?,
3103 C::from_lua(third, lua)?,
3104 ))
3105 }
3106}
3107
3108impl<A, B, T> FromLuaMulti for (A, B, Variadic<T>)
3109where
3110 A: FromLua,
3111 B: FromLua,
3112 T: FromLua,
3113{
3114 const NRESULTS: i32 = -1;
3115
3116 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
3117 let first = if values.is_empty() {
3118 Value::Nil
3119 } else {
3120 values.remove(0)
3121 };
3122 let second = if values.is_empty() {
3123 Value::Nil
3124 } else {
3125 values.remove(0)
3126 };
3127 Ok((
3128 A::from_lua(first, lua)?,
3129 B::from_lua(second, lua)?,
3130 Variadic::from_lua_multi(values, lua)?,
3131 ))
3132 }
3133}
3134
3135fn rust_callback_trampoline(state: &mut LuaState) -> Result<usize> {
3136 let func_idx = state.current_call_info().func;
3137 let callback = match state.get_at(func_idx) {
3138 RawLuaValue::Function(RawLuaClosure::C(closure)) => {
3139 let Some(RawLuaValue::UserData(userdata)) = closure.upvalues.first() else {
3140 return Err(LuaError::runtime(format_args!(
3141 "missing Rust callback payload"
3142 )));
3143 };
3144 let host = userdata
3145 .host_value()
3146 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust callback payload")))?;
3147 host.downcast::<RustCallbackCell>().map_err(|_| {
3148 LuaError::runtime(format_args!("Rust callback payload type mismatch"))
3149 })?
3150 }
3151 _ => {
3152 return Err(LuaError::runtime(format_args!(
3153 "Rust callback trampoline called without C closure"
3154 )));
3155 }
3156 };
3157 (callback.function)(state)
3158}
3159
3160fn with_heap_guard<R>(state: &LuaState, f: impl FnOnce() -> R) -> R {
3161 let _heap_guard = heap_guard(state);
3162 f()
3163}
3164
3165fn heap_guard(state: &LuaState) -> lua_gc::HeapGuard {
3166 let global = state.global();
3167 lua_gc::HeapGuard::push(&global.heap)
3168}
3169
3170fn callback_args(state: &mut LuaState, lua: &Lua) -> Result<Vec<Value>> {
3171 let func_idx = state.current_call_info().func;
3172 let nargs = state.top_idx().0.saturating_sub(func_idx.0 + 1);
3173 let mut args = Vec::with_capacity(nargs as usize);
3174 for i in 0..nargs {
3175 let raw = state.get_at(func_idx + 1 + i as i32);
3176 args.push(Value::from_raw_in_state(lua, state, raw)?);
3177 }
3178 Ok(args)
3179}
3180
3181fn callback_userdata_args(state: &mut LuaState, lua: &Lua) -> Result<(AnyUserData, Vec<Value>)> {
3182 let mut args = callback_args(state, lua)?;
3183 if args.is_empty() {
3184 return Err(LuaError::runtime(format_args!(
3185 "userdata method missing self argument"
3186 )));
3187 }
3188 let userdata = AnyUserData::from_lua(args.remove(0), lua)?;
3189 Ok((userdata, args))
3190}
3191
3192fn push_callback_returns(state: &mut LuaState, lua: &Lua, returns: Vec<Value>) -> Result<usize> {
3193 let mut count = 0usize;
3194 for value in returns {
3195 let raw = value.to_raw_for_lua(lua, state)?;
3196 state.push(raw);
3197 count += 1;
3198 }
3199 Ok(count)
3200}
3201
3202fn stale_handle_error() -> LuaError {
3203 LuaError::runtime(format_args!("stale Lua handle"))
3204}
3205
3206fn type_error_raw(value: &RawLuaValue, expected: &str) -> LuaError {
3207 LuaError::runtime(format_args!(
3208 "{} expected, got {}",
3209 expected,
3210 value.type_name()
3211 ))
3212}
3213
3214fn type_error_value(value: &Value, expected: &str) -> LuaError {
3215 let got = match value {
3216 Value::Nil => "nil",
3217 Value::Boolean(_) => "boolean",
3218 Value::Integer(_) | Value::Number(_) => "number",
3219 Value::String(_) => "string",
3220 Value::Table(_) => "table",
3221 Value::Function(_) => "function",
3222 Value::UserData(_) | Value::LightUserData(_) => "userdata",
3223 Value::Thread(_) => "thread",
3224 };
3225 LuaError::runtime(format_args!("{} expected, got {}", expected, got))
3226}
3227
3228pub struct LuaRuntime {
3230 state: LuaState,
3231}
3232
3233impl LuaRuntime {
3234 pub fn new() -> Result<Self> {
3240 Self::with_hooks(HostHooks::default())
3241 }
3242
3243 pub fn with_hooks(hooks: HostHooks) -> Result<Self> {
3245 let mut state = new_state().ok_or(LuaError::Memory)?;
3246 install_parser_hook(&mut state);
3247 hooks.install(&mut state);
3248 open_libs(&mut state)?;
3249 Ok(Self { state })
3250 }
3251
3252 pub fn state(&self) -> &LuaState {
3253 &self.state
3254 }
3255
3256 pub fn state_mut(&mut self) -> &mut LuaState {
3257 &mut self.state
3258 }
3259
3260 pub fn into_state(self) -> LuaState {
3261 self.state
3262 }
3263
3264 pub fn into_lua(self) -> Lua {
3265 Lua::from_initialized_state(self.state, LuaVersion::default())
3266 }
3267
3268 pub fn exec(&mut self, source: &[u8], name: &[u8]) -> Result<()> {
3270 exec_state(&mut self.state, source, name)
3271 }
3272
3273 pub fn install_sandbox(&mut self, config: SandboxConfig) -> Result<()> {
3281 apply_sandbox_config(&mut self.state, &config)
3282 }
3283
3284 pub fn sandbox_tripped(&self) -> Option<TripReason> {
3286 trip_reason_from_code(self.state.sandbox_tripped_code())
3287 }
3288
3289 pub fn sandbox_reset(&self) {
3292 self.state.sandbox_reset();
3293 }
3294}
3295
3296fn exec_state(state: &mut LuaState, source: &[u8], name: &[u8]) -> Result<()> {
3297 let status = load_buffer(state, source, name)?;
3298 if status != 0 {
3299 let err = state.pop();
3300 return Err(LuaError::from_value(err));
3301 }
3302 lua_vm::api::pcall_k(state, 0, 0, 0, 0, None)?;
3303 Ok(())
3304}
3305
3306pub fn install_parser_hook(state: &mut LuaState) {
3307 state.global_mut().parser_hook = Some(parser_hook);
3308}
3309
3310fn parser_hook(
3311 state: &mut LuaState,
3312 source: &[u8],
3313 name: &[u8],
3314 firstchar: i32,
3315) -> Result<GcRef<LuaLClosure>> {
3316 let _heap_guard = heap_guard(state);
3317 let proto = lua_parse::parse(
3318 state,
3319 lua_parse::DynData::default(),
3320 source,
3321 name,
3322 firstchar,
3323 )?;
3324 let nupvals = proto.upvalues.len();
3325 let mut upvals = Vec::with_capacity(nupvals);
3326 for _ in 0..nupvals {
3327 upvals.push(std::cell::Cell::new(GcRef::new(UpVal::closed(
3328 RawLuaValue::Nil,
3329 ))));
3330 }
3331 Ok(GcRef::new(LuaLClosure {
3332 proto: GcRef::new(*proto),
3333 upvals,
3334 }))
3335}
3336
3337#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3369pub enum TripReason {
3370 Instructions,
3372 Memory,
3374}
3375
3376#[derive(Clone)]
3380pub struct Sandbox {
3381 lua: Lua,
3382}
3383
3384impl Sandbox {
3385 pub fn instructions_remaining(&self) -> Option<u64> {
3388 self.lua.with_state(|state| {
3389 if state.sandbox_instr_limited() {
3390 Some(state.sandbox_instr_remaining())
3391 } else {
3392 None
3393 }
3394 })
3395 }
3396
3397 pub fn instructions_used(&self) -> Option<u64> {
3400 self.lua.with_state(|state| {
3401 if state.sandbox_instr_limited() {
3402 Some(state.sandbox_instr_limit() - state.sandbox_instr_remaining())
3403 } else {
3404 None
3405 }
3406 })
3407 }
3408
3409 pub fn tripped(&self) -> Option<TripReason> {
3411 self.lua
3412 .with_state(|state| trip_reason_from_code(state.sandbox_tripped_code()))
3413 }
3414
3415 pub fn reset(&self) {
3418 self.lua.with_state(|state| state.sandbox_reset());
3419 }
3420}
3421
3422#[derive(Debug, Clone)]
3424pub struct SandboxConfig {
3425 pub instruction_limit: Option<u64>,
3427 pub memory_limit_bytes: Option<usize>,
3429 pub check_interval: u32,
3432 pub remove_globals: Vec<Vec<u8>>,
3436}
3437
3438impl SandboxConfig {
3439 pub fn strict() -> Self {
3442 Self {
3443 instruction_limit: Some(10_000_000),
3444 memory_limit_bytes: Some(64 * 1024 * 1024),
3445 check_interval: 1000,
3446 remove_globals: lua_stdlib::sandbox::STRICT_REMOVED_GLOBALS
3447 .iter()
3448 .map(|s| s.to_vec())
3449 .collect(),
3450 }
3451 }
3452}
3453
3454impl Default for SandboxConfig {
3455 fn default() -> Self {
3456 Self::strict()
3457 }
3458}
3459
3460fn strip_globals(state: &mut LuaState, names: &[Vec<u8>]) -> Result<()> {
3461 let refs: Vec<&[u8]> = names.iter().map(|n| n.as_slice()).collect();
3462 lua_stdlib::sandbox::strip_globals(state, &refs)
3463}
3464
3465fn apply_sandbox_config(state: &mut LuaState, config: &SandboxConfig) -> Result<()> {
3469 strip_globals(state, &config.remove_globals)?;
3470 if config.instruction_limit.is_some() || config.memory_limit_bytes.is_some() {
3471 let interval = config.check_interval.max(1) as i32;
3472 state.install_sandbox_limits(interval, config.instruction_limit, config.memory_limit_bytes);
3473 }
3474 Ok(())
3475}
3476
3477fn trip_reason_from_code(code: u8) -> Option<TripReason> {
3479 match code {
3480 lua_vm::state::SANDBOX_TRIP_INSTRUCTIONS => Some(TripReason::Instructions),
3481 lua_vm::state::SANDBOX_TRIP_MEMORY => Some(TripReason::Memory),
3482 _ => None,
3483 }
3484}
3485
3486impl Lua {
3487 pub fn sandboxed(config: SandboxConfig) -> Result<(Self, Sandbox)> {
3492 let lua = Self::with_hooks(HostHooks::default())?;
3493 let sandbox = lua.install_sandbox(config)?;
3494 Ok((lua, sandbox))
3495 }
3496
3497 pub fn install_sandbox(&self, config: SandboxConfig) -> Result<Sandbox> {
3504 self.with_state(|state| apply_sandbox_config(state, &config))?;
3505 Ok(Sandbox { lua: self.clone() })
3506 }
3507}
3508
3509#[cfg(test)]
3510mod tests {
3511 use super::*;
3512 use std::cell::Cell;
3513
3514 fn external_root_count(lua: &Lua) -> usize {
3515 lua.with_state(|state| state.global().external_roots.len())
3516 }
3517
3518 struct Counter {
3519 value: i64,
3520 }
3521
3522 impl UserData for Counter {
3523 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
3524 methods.add_method("get", |_lua, this, ()| Ok(this.value));
3525 methods.add_method_mut("inc", |_lua, this, delta: i64| {
3526 this.value += delta;
3527 Ok(this.value)
3528 });
3529 }
3530 }
3531
3532 struct PropertyBag {
3533 value: i64,
3534 }
3535
3536 impl UserData for PropertyBag {
3537 fn add_meta_methods<M: UserDataMethods<Self>>(methods: &mut M) {
3538 methods.add_meta_method(MetaMethod::Index, |_lua, this, key: String| {
3539 if key == "value" {
3540 Ok(Value::Integer(this.value))
3541 } else {
3542 Ok(Value::Nil)
3543 }
3544 });
3545 methods.add_meta_method_mut(
3546 MetaMethod::NewIndex,
3547 |_lua, this, (key, value): (String, i64)| {
3548 if key != "value" {
3549 return Err(LuaError::runtime(format_args!("unknown property")));
3550 }
3551 this.value = value;
3552 Ok(())
3553 },
3554 );
3555 }
3556 }
3557
3558 #[test]
3559 fn default_lua_is_v54_and_reports_version() {
3560 let lua = Lua::new();
3561 assert_eq!(lua.version(), LuaVersion::V54);
3562 let v: String = lua.globals().get("_VERSION").unwrap();
3563 assert_eq!(v, "Lua 5.4");
3564 }
3565
3566 #[test]
3567 fn new_versioned_threads_version_to_version_global() {
3568 let lua = Lua::new_versioned(LuaVersion::V53);
3569 assert_eq!(lua.version(), LuaVersion::V53);
3570 let v: String = lua.globals().get("_VERSION").unwrap();
3571 assert_eq!(v, "Lua 5.3");
3572 let from_lua: String = lua.load("return _VERSION").eval().unwrap();
3573 assert_eq!(from_lua, "Lua 5.3");
3574 }
3575
3576 #[test]
3577 fn rooted_table_clone_and_drop_manage_root_slots() {
3578 let lua = Lua::new();
3579 assert_eq!(external_root_count(&lua), 0);
3580
3581 let table = lua.create_table().expect("table should allocate");
3582 assert_eq!(external_root_count(&lua), 1);
3583
3584 let cloned = table.clone();
3585 assert_eq!(external_root_count(&lua), 2);
3586
3587 drop(table);
3588 assert_eq!(external_root_count(&lua), 1);
3589
3590 cloned.set("answer", 42_i64).expect("set should succeed");
3591 lua.gc_collect();
3592 assert_eq!(
3593 cloned.get::<_, i64>("answer").expect("get should succeed"),
3594 42
3595 );
3596
3597 drop(cloned);
3598 assert_eq!(external_root_count(&lua), 0);
3599 }
3600
3601 #[test]
3602 fn table_values_survive_forced_collection_between_operations() {
3603 let lua = Lua::new();
3604 let table = lua.create_table().expect("table should allocate");
3605
3606 lua.gc_collect();
3607 table.set("k", "v").expect("set should succeed");
3608 table.set(1_i64, "array").expect("array set should succeed");
3609 lua.gc_collect();
3610
3611 let value: String = table.get("k").expect("get should succeed");
3612 assert_eq!(value, "v");
3613 assert_eq!(table.len().expect("len should succeed"), 1);
3614 }
3615
3616 #[test]
3617 fn chunk_exec_eval_and_function_call_use_rooted_handles() {
3618 let lua = Lua::new();
3619 lua.load("function add(a, b) return a + b end")
3620 .set_name("test")
3621 .exec()
3622 .expect("chunk should execute");
3623
3624 let globals = lua.globals();
3625 let add: Function = globals.get("add").expect("function should exist");
3626 let result: i64 = add.call((20_i64, 22_i64)).expect("call should work");
3627 assert_eq!(result, 42);
3628
3629 let eval_result: i64 = lua
3630 .load("return add(1, 2)")
3631 .eval()
3632 .expect("eval should work");
3633 assert_eq!(eval_result, 3);
3634 }
3635
3636 #[test]
3637 fn rust_callback_captures_state_and_reenters_lua() {
3638 let lua = Lua::new();
3639 lua.load("function twice(v) return v * 2 end")
3640 .exec()
3641 .expect("chunk should execute");
3642
3643 let globals = lua.globals();
3644 let twice: Function = globals.get("twice").expect("function should exist");
3645 let calls = Rc::new(Cell::new(0));
3646 let calls_for_callback = calls.clone();
3647
3648 let callback = lua
3649 .create_function(move |_lua, value: i64| {
3650 calls_for_callback.set(calls_for_callback.get() + 1);
3651 let doubled: i64 = twice.call(value)?;
3652 Ok(doubled + 1)
3653 })
3654 .expect("callback should create");
3655 globals
3656 .set("from_rust", callback)
3657 .expect("callback should register");
3658
3659 let result: i64 = lua
3660 .load("return from_rust(20)")
3661 .eval()
3662 .expect("callback should run");
3663 assert_eq!(result, 41);
3664 assert_eq!(calls.get(), 1);
3665 }
3666
3667 #[test]
3668 fn rust_callback_accepts_and_returns_collectable_values() {
3669 let lua = Lua::new();
3670 let globals = lua.globals();
3671 let callback = lua
3672 .create_function(|lua, name: String| {
3673 let table = lua.create_table()?;
3674 table.set("name", name)?;
3675 Ok(table)
3676 })
3677 .expect("callback should create");
3678 globals
3679 .set("make_record", callback)
3680 .expect("callback should register");
3681
3682 let result: String = lua
3683 .load("return make_record('lua-rs').name")
3684 .eval()
3685 .expect("callback should return table");
3686 assert_eq!(result, "lua-rs");
3687 }
3688
3689 #[test]
3690 fn rust_callback_mut_tracks_state() {
3691 let lua = Lua::new();
3692 let globals = lua.globals();
3693 let mut next = 0_i64;
3694 let callback = lua
3695 .create_function_mut(move |_lua, delta: i64| {
3696 next += delta;
3697 Ok(next)
3698 })
3699 .expect("callback should create");
3700 globals
3701 .set("next", callback)
3702 .expect("callback should register");
3703
3704 let result: (i64, i64) = lua
3705 .load("return next(2), next(5)")
3706 .eval()
3707 .expect("callback should run");
3708 assert_eq!(result, (2, 7));
3709 }
3710
3711 #[test]
3712 fn dropped_rust_callback_releases_captured_handles_after_gc() {
3713 let lua = Lua::new();
3714 let table = lua.create_table().expect("table should allocate");
3715 table.set("value", 42_i64).expect("set should succeed");
3716 assert_eq!(external_root_count(&lua), 1);
3717
3718 let callback = {
3719 let captured = table.clone();
3720 lua.create_function(move |_lua, ()| captured.get::<_, i64>("value"))
3721 .expect("callback should create")
3722 };
3723 assert_eq!(external_root_count(&lua), 3);
3724
3725 drop(callback);
3726 lua.gc_collect();
3727 assert_eq!(external_root_count(&lua), 1);
3728 assert_eq!(table.get::<_, i64>("value").expect("table should live"), 42);
3729 }
3730
3731 #[test]
3732 fn metatable_is_built_once_per_type() {
3733 use std::sync::atomic::{AtomicUsize, Ordering};
3734 static BUILDS: AtomicUsize = AtomicUsize::new(0);
3735
3736 struct Widget {
3737 n: i64,
3738 }
3739 impl UserData for Widget {
3740 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
3741 BUILDS.fetch_add(1, Ordering::SeqCst);
3742 methods.add_method("n", |_lua, this, ()| Ok(this.n));
3743 }
3744 }
3745
3746 let lua = Lua::new();
3747 let a = lua.create_userdata(Widget { n: 1 }).expect("first");
3748 let b = lua.create_userdata(Widget { n: 2 }).expect("second");
3749 let c = lua.create_userdata(Widget { n: 3 }).expect("third");
3750
3751 assert_eq!(BUILDS.load(Ordering::SeqCst), 1);
3753
3754 let globals = lua.globals();
3756 globals.set("a", &a).unwrap();
3757 globals.set("b", &b).unwrap();
3758 globals.set("c", &c).unwrap();
3759 let sum: i64 = lua.load("return a:n() + b:n() + c:n()").eval().unwrap();
3760 assert_eq!(sum, 6);
3761 }
3762
3763 #[test]
3775 fn lua_state_frees_after_userdata_with_methods_is_dropped() {
3776 use std::rc::Rc;
3777
3778 let weak_inner = {
3779 let lua = Lua::new();
3780 let weak = Rc::downgrade(&lua.inner);
3781 let _ = lua
3785 .create_userdata(Counter { value: 1 })
3786 .expect("userdata should create");
3787 weak
3788 };
3789
3790 assert!(
3791 weak_inner.upgrade().is_none(),
3792 "LuaInner is still alive after every external Lua handle dropped: \
3793 internal callback closures hold a strong Rc<LuaInner>, leaking the state"
3794 );
3795 }
3796
3797 #[test]
3801 fn lua_state_frees_after_create_function_handle_drops() {
3802 use std::rc::Rc;
3803
3804 let weak_inner = {
3805 let lua = Lua::new();
3806 let weak = Rc::downgrade(&lua.inner);
3807 let _f = lua
3808 .create_function(|_, ()| Ok(()))
3809 .expect("create_function should succeed");
3810 weak
3811 };
3812
3813 assert!(
3814 weak_inner.upgrade().is_none(),
3815 "LuaInner is still alive after the only Lua handle dropped: \
3816 the create_function callback held a strong Rc<LuaInner>"
3817 );
3818 }
3819
3820 #[test]
3827 fn lua_state_frees_after_userdata_with_fields_drops() {
3828 use std::rc::Rc;
3829
3830 struct Point {
3831 x: f64,
3832 }
3833 impl UserData for Point {
3834 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
3835 m.add_field_method_get("x", |_, this| Ok(this.x));
3836 m.add_field_method_set("x", |_, this, v: f64| {
3837 this.x = v;
3838 Ok(())
3839 });
3840 }
3841 }
3842
3843 let weak_inner = {
3844 let lua = Lua::new();
3845 let weak = Rc::downgrade(&lua.inner);
3846 let _ = lua
3847 .create_userdata(Point { x: 1.0 })
3848 .expect("userdata should create");
3849 weak
3850 };
3851
3852 assert!(
3853 weak_inner.upgrade().is_none(),
3854 "LuaInner leaked via the composed __index/__newindex closures: \
3855 they capture Table/Function values whose RootedValue holds a \
3856 strong Rc<LuaInner>"
3857 );
3858 }
3859
3860 #[test]
3866 fn lua_state_frees_with_fields_methods_and_raw_meta() {
3867 use std::rc::Rc;
3868
3869 struct Mixed {
3870 x: f64,
3871 log: Vec<String>,
3872 }
3873 impl UserData for Mixed {
3874 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
3875 m.add_field_method_get("x", |_, this| Ok(this.x));
3876 m.add_field_method_set("x", |_, this, v: f64| {
3877 this.x = v;
3878 Ok(())
3879 });
3880 m.add_method("log_len", |_, this, ()| Ok(this.log.len() as i64));
3881 m.add_method_mut("push_log", |_, this, s: String| {
3882 this.log.push(s);
3883 Ok(())
3884 });
3885 m.add_meta_method(MetaMethod::Index, |_, _this, key: String| {
3886 Ok(::std::format!("dynamic:{key}"))
3887 });
3888 m.add_meta_method_mut(
3889 MetaMethod::NewIndex,
3890 |_, _this, (_k, _v): (String, Value)| Ok(()),
3891 );
3892 }
3893 }
3894
3895 let weak_inner = {
3896 let lua = Lua::new();
3897 let weak = Rc::downgrade(&lua.inner);
3898 let _ = lua
3899 .create_userdata(Mixed {
3900 x: 1.0,
3901 log: Vec::new(),
3902 })
3903 .expect("create");
3904 weak
3905 };
3906
3907 assert!(
3908 weak_inner.upgrade().is_none(),
3909 "maximal-composition userdata leaked LuaInner: \
3910 check the composed __index / __newindex captures"
3911 );
3912 }
3913
3914 #[test]
3920 fn composed_dispatch_does_not_accumulate_external_roots() {
3921 struct Probe {
3922 x: i64,
3923 }
3924 impl UserData for Probe {
3925 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
3926 m.add_field_method_get("x", |_, this| Ok(this.x));
3927 }
3928 }
3929
3930 let lua = Lua::new();
3931 lua.globals()
3932 .set("v", lua.create_userdata(Probe { x: 1 }).unwrap())
3933 .unwrap();
3934 let baseline = external_root_count(&lua);
3935
3936 for _ in 0..1000 {
3937 let _: i64 = lua.load("return v.x").eval().unwrap();
3938 }
3939 let after = external_root_count(&lua);
3942
3943 assert!(
3944 after <= baseline + 2,
3945 "external roots grew under composed __index churn: baseline={baseline} after={after}"
3946 );
3947 }
3948
3949 #[test]
3955 fn userdata_method_can_reenter_lua_from_callback() {
3956 struct Calc;
3957 impl UserData for Calc {
3958 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
3959 m.add_method("apply", |_lua, _this, f: Function| {
3960 let r: i64 = f.call(7_i64)?;
3961 Ok(r + 1)
3962 });
3963 }
3964 }
3965
3966 let lua = Lua::new();
3967 lua.globals()
3968 .set("c", lua.create_userdata(Calc).unwrap())
3969 .unwrap();
3970 let r: i64 = lua
3971 .load("return c:apply(function(n) return n * 2 end)")
3972 .eval()
3973 .unwrap();
3974 assert_eq!(r, 15);
3975 }
3976
3977 #[test]
3982 fn metatable_cache_is_per_lua_state() {
3983 use std::sync::atomic::{AtomicUsize, Ordering};
3984 static BUILDS: AtomicUsize = AtomicUsize::new(0);
3985
3986 struct Marker {
3987 v: i64,
3988 }
3989 impl UserData for Marker {
3990 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
3991 BUILDS.fetch_add(1, Ordering::SeqCst);
3992 m.add_method("v", |_, this, ()| Ok(this.v));
3993 }
3994 }
3995
3996 let start = BUILDS.load(Ordering::SeqCst);
3997
3998 let lua_a = Lua::new();
3999 let _a1 = lua_a.create_userdata(Marker { v: 1 }).unwrap();
4000 assert_eq!(BUILDS.load(Ordering::SeqCst) - start, 1, "state A first build");
4001 let _a2 = lua_a.create_userdata(Marker { v: 2 }).unwrap();
4002 assert_eq!(BUILDS.load(Ordering::SeqCst) - start, 1, "state A reuses cache");
4003
4004 let lua_b = Lua::new();
4005 let _b1 = lua_b.create_userdata(Marker { v: 3 }).unwrap();
4006 assert_eq!(BUILDS.load(Ordering::SeqCst) - start, 2, "state B is independent");
4007
4008 let _a3 = lua_a.create_userdata(Marker { v: 4 }).unwrap();
4009 assert_eq!(BUILDS.load(Ordering::SeqCst) - start, 2, "state A still cached");
4010 }
4011
4012 #[test]
4016 fn field_shadows_method_of_same_name() {
4017 struct Shadow {
4018 x: i64,
4019 }
4020 impl UserData for Shadow {
4021 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
4022 m.add_field_method_get("x", |_, this| Ok(this.x));
4023 m.add_method("x", |_, _this, ()| Ok(999_i64));
4024 }
4025 }
4026
4027 let lua = Lua::new();
4028 lua.globals()
4029 .set("v", lua.create_userdata(Shadow { x: 42 }).unwrap())
4030 .unwrap();
4031
4032 let r: i64 = lua.load("return v.x").eval().unwrap();
4033 assert_eq!(r, 42, "the field getter should beat the method of the same name");
4034 }
4035
4036 #[test]
4040 fn cached_metatable_is_shared_across_values_in_lua() {
4041 struct Twin;
4042 impl UserData for Twin {
4043 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
4044 m.add_method("ping", |_, _this, ()| Ok(1_i64));
4045 }
4046 }
4047
4048 let lua = Lua::new();
4049 lua.globals()
4050 .set("a", lua.create_userdata(Twin).unwrap())
4051 .unwrap();
4052 lua.globals()
4053 .set("b", lua.create_userdata(Twin).unwrap())
4054 .unwrap();
4055
4056 let same: bool = lua
4057 .load("return getmetatable(a) == getmetatable(b)")
4058 .eval()
4059 .unwrap();
4060 assert!(same, "cached metatable must be shared across values of the same type");
4061 }
4062
4063 #[test]
4064 fn fields_and_methods_coexist() {
4065 struct Vec2 {
4066 x: f64,
4067 y: f64,
4068 }
4069 impl UserData for Vec2 {
4070 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
4071 m.add_field_method_get("x", |_, this| Ok(this.x));
4072 m.add_field_method_get("y", |_, this| Ok(this.y));
4073 m.add_field_method_set("x", |_, this, v: f64| {
4074 this.x = v;
4075 Ok(())
4076 });
4077 m.add_field_method_set("y", |_, this, v: f64| {
4078 this.y = v;
4079 Ok(())
4080 });
4081 m.add_method("length", |_, this, ()| {
4082 Ok((this.x * this.x + this.y * this.y).sqrt())
4083 });
4084 m.add_method_mut("scale", |_, this, k: f64| {
4085 this.x *= k;
4086 this.y *= k;
4087 Ok(())
4088 });
4089 }
4090 }
4091
4092 let lua = Lua::new();
4093 let v = lua.create_userdata(Vec2 { x: 3.0, y: 4.0 }).unwrap();
4094 lua.globals().set("v", &v).unwrap();
4095
4096 assert_eq!(lua.load("return v:length()").eval::<f64>().unwrap(), 5.0);
4098 assert_eq!(lua.load("return v.x + v.y").eval::<f64>().unwrap(), 7.0);
4099
4100 lua.load("v.x = 6").exec().unwrap();
4102 assert_eq!(lua.load("return v.x").eval::<f64>().unwrap(), 6.0);
4103
4104 lua.load("v:scale(2)").exec().unwrap();
4106 assert_eq!(lua.load("return v.x").eval::<f64>().unwrap(), 12.0);
4107 assert_eq!(lua.load("return v.y").eval::<f64>().unwrap(), 8.0);
4108
4109 assert!(lua.load("v.z = 1").exec().is_err());
4111 }
4112
4113 #[test]
4114 fn userdata_methods_dispatch_and_track_borrows() {
4115 let lua = Lua::new();
4116 let globals = lua.globals();
4117 let counter = lua
4118 .create_userdata(Counter { value: 1 })
4119 .expect("userdata should create");
4120 globals
4121 .set("counter", &counter)
4122 .expect("userdata should register");
4123
4124 let result: i64 = lua
4125 .load("counter:inc(5); return counter:get()")
4126 .eval()
4127 .expect("methods should dispatch");
4128 assert_eq!(result, 6);
4129 assert_eq!(
4130 counter
4131 .with_borrow::<Counter, _>(|counter| counter.value)
4132 .expect("borrow should work"),
4133 6
4134 );
4135
4136 {
4137 let borrowed = counter
4138 .borrow::<Counter>()
4139 .expect("borrow guard should work");
4140 assert_eq!(borrowed.value, 6);
4141 }
4142
4143 {
4144 let mut borrowed = counter
4145 .borrow_mut::<Counter>()
4146 .expect("mutable borrow guard should work");
4147 borrowed.value = 9;
4148 }
4149
4150 assert_eq!(
4151 lua.load("return counter:get()")
4152 .eval::<i64>()
4153 .expect("method should see guard mutation"),
4154 9
4155 );
4156 }
4157
4158 #[test]
4159 fn userdata_payload_survives_gc_while_lua_holds_userdata() {
4160 let lua = Lua::new();
4161 let globals = lua.globals();
4162 let counter = lua
4163 .create_userdata(Counter { value: 10 })
4164 .expect("userdata should create");
4165 globals
4166 .set("counter", counter)
4167 .expect("userdata should register");
4168
4169 lua.gc_collect();
4170 let result: i64 = lua
4171 .load("counter:inc(2); collectgarbage('collect'); return counter:get()")
4172 .eval()
4173 .expect("userdata should survive collection");
4174 assert_eq!(result, 12);
4175 }
4176
4177 #[test]
4178 fn userdata_runtime_borrow_conflict_returns_lua_error() {
4179 let lua = Lua::new();
4180 let globals = lua.globals();
4181 let counter = lua
4182 .create_userdata(Counter { value: 1 })
4183 .expect("userdata should create");
4184 globals
4185 .set("counter", &counter)
4186 .expect("userdata should register");
4187
4188 let failed = counter
4189 .with_borrow::<Counter, _>(|_| lua.load("return counter:inc(1)").eval::<i64>().is_err())
4190 .expect("outer borrow should succeed");
4191 assert!(
4192 failed,
4193 "mutable method should fail while immutable borrow is held"
4194 );
4195 assert_eq!(
4196 counter
4197 .with_borrow::<Counter, _>(|counter| counter.value)
4198 .expect("borrow should work"),
4199 1
4200 );
4201 }
4202
4203 #[test]
4204 fn userdata_index_and_newindex_metamethods_dispatch() {
4205 let lua = Lua::new();
4206 let globals = lua.globals();
4207 let bag = lua
4208 .create_userdata(PropertyBag { value: 7 })
4209 .expect("userdata should create");
4210 globals.set("bag", &bag).expect("userdata should register");
4211
4212 let result: i64 = lua
4213 .load("bag.value = 42; return bag.value")
4214 .eval()
4215 .expect("metamethods should dispatch");
4216 assert_eq!(result, 42);
4217 assert_eq!(
4218 bag.with_borrow::<PropertyBag, _>(|bag| bag.value)
4219 .expect("borrow should work"),
4220 42
4221 );
4222 }
4223
4224 #[test]
4225 fn userdata_values_convert_directly_with_into_lua() {
4226 let lua = Lua::new();
4227 let globals = lua.globals();
4228 globals
4229 .set("counter", Counter { value: 3 })
4230 .expect("userdata should convert through IntoLua");
4231
4232 let result: i64 = lua
4233 .load("counter:inc(4); return counter:get()")
4234 .eval()
4235 .expect("converted userdata should dispatch methods");
4236 assert_eq!(result, 7);
4237 }
4238
4239 #[test]
4240 fn variadic_args_and_returns_convert_all_values() {
4241 let lua = Lua::new();
4242 let globals = lua.globals();
4243
4244 let sum = lua
4245 .create_function(|_lua, values: Variadic<i64>| Ok(values.iter().sum::<i64>()))
4246 .expect("variadic callback should create");
4247 globals.set("sum", sum).expect("callback should register");
4248 let result: i64 = lua
4249 .load("return sum(3, 2, 5)")
4250 .eval()
4251 .expect("variadic callback should run");
4252 assert_eq!(result, 10);
4253
4254 let echo = lua
4255 .create_function(|_lua, values: Variadic<Value>| Ok(values))
4256 .expect("variadic return callback should create");
4257 globals.set("echo", echo).expect("callback should register");
4258 let result: (i64, i64, i64) = lua
4259 .load("return echo(1, 2, 3)")
4260 .eval()
4261 .expect("variadic returns should stay separate");
4262 assert_eq!(result, (1, 2, 3));
4263
4264 let values: Variadic<i64> = lua
4265 .load("return 4, 5, 6")
4266 .eval()
4267 .expect("variadic eval should collect all returns");
4268 assert_eq!(values.into_vec(), vec![4, 5, 6]);
4269 }
4270
4271 #[test]
4272 fn vectors_maps_and_triple_returns_convert_through_tables() {
4273 let lua = Lua::new();
4274 let globals = lua.globals();
4275
4276 globals
4277 .set("list", vec![1_i64, 2, 3])
4278 .expect("vector should convert to table");
4279 let second: i64 = lua
4280 .load("return list[2]")
4281 .eval()
4282 .expect("table should be readable from Lua");
4283 assert_eq!(second, 2);
4284
4285 let list: Vec<i64> = lua
4286 .load("return {4, 5, 6}")
4287 .eval()
4288 .expect("table should convert to vector");
4289 assert_eq!(list, vec![4, 5, 6]);
4290
4291 let mut map = HashMap::new();
4292 map.insert("left".to_string(), 10_i64);
4293 map.insert("right".to_string(), 20_i64);
4294 globals
4295 .set("map", map)
4296 .expect("map should convert to table");
4297 let sum: i64 = lua
4298 .load("return map.left + map.right")
4299 .eval()
4300 .expect("map table should be readable from Lua");
4301 assert_eq!(sum, 30);
4302
4303 let map: HashMap<String, i64> = lua
4304 .load("return {alpha = 3, beta = 9}")
4305 .eval()
4306 .expect("table should convert to map");
4307 assert_eq!(map.get("alpha"), Some(&3));
4308 assert_eq!(map.get("beta"), Some(&9));
4309
4310 let triple: (i64, i64, i64) = lua
4311 .load("return 1, 2, 3")
4312 .eval()
4313 .expect("triple returns should convert");
4314 assert_eq!(triple, (1, 2, 3));
4315 }
4316
4317 fn runtime_error_message(err: &LuaError) -> String {
4323 match err {
4324 LuaError::Runtime(v) | LuaError::Syntax(v) => match v {
4325 RawLuaValue::Str(s) => String::from_utf8_lossy(s.as_bytes()).into_owned(),
4326 other => format!("{other:?}"),
4327 },
4328 other => format!("{other:?}"),
4329 }
4330 }
4331
4332 struct ScopedCounter {
4337 value: i64,
4338 calls: Cell<u32>,
4339 }
4340
4341 impl UserData for ScopedCounter {
4342 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
4343 methods.add_method("get", |_lua, this, ()| {
4344 this.calls.set(this.calls.get() + 1);
4345 Ok(this.value)
4346 });
4347 methods.add_method_mut("inc", |_lua, this, delta: i64| {
4348 this.value += delta;
4349 Ok(this.value)
4350 });
4351 methods.add_method("calls", |_lua, this, ()| Ok(this.calls.get() as i64));
4352 methods.add_method("call_get_via_global", |lua, _this, ()| {
4353 lua.load("return c:get()").eval::<i64>()
4354 });
4355 methods.add_method_mut("inc_via_global", |lua, this, ()| {
4356 this.value += 1;
4357 lua.load("return c:get()").eval::<i64>()
4358 });
4359 }
4360 }
4361
4362 struct ScopedBag {
4363 value: i64,
4364 }
4365
4366 impl UserData for ScopedBag {
4367 fn add_meta_methods<M: UserDataMethods<Self>>(methods: &mut M) {
4368 methods.add_meta_method(MetaMethod::Index, |_lua, this, key: String| {
4369 if key == "value" {
4370 Ok(Value::Integer(this.value))
4371 } else {
4372 Ok(Value::Nil)
4373 }
4374 });
4375 methods.add_meta_method_mut(
4376 MetaMethod::NewIndex,
4377 |_lua, this, (key, value): (String, i64)| {
4378 if key != "value" {
4379 return Err(LuaError::runtime(format_args!("unknown property")));
4380 }
4381 this.value = value;
4382 Ok(())
4383 },
4384 );
4385 }
4386 }
4387
4388 struct ScopedFielded {
4389 n: i64,
4390 }
4391
4392 impl UserData for ScopedFielded {
4393 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
4394 methods.add_field_method_get("n", |_lua, this| Ok(this.n));
4395 methods.add_field_method_set("n", |_lua, this, new: i64| {
4396 this.n = new;
4397 Ok(())
4398 });
4399 }
4400 }
4401
4402 #[test]
4406 fn scope_userdata_dispatches_method_calls_against_borrow() {
4407 let lua = Lua::new();
4408 let mut counter = ScopedCounter {
4409 value: 10,
4410 calls: Cell::new(0),
4411 };
4412
4413 let observed: i64 = lua
4414 .scope(|scope| {
4415 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4416 lua.globals().set("c", &ud)?;
4417 lua.load("return c:get()").eval::<i64>()
4418 })
4419 .expect("scope body should succeed");
4420 assert_eq!(observed, 10);
4421 assert_eq!(counter.value, 10);
4422 assert_eq!(counter.calls.get(), 1);
4423 }
4424
4425 #[test]
4429 fn scope_userdata_mut_method_propagates_to_external_borrow() {
4430 let lua = Lua::new();
4431 let mut counter = ScopedCounter {
4432 value: 0,
4433 calls: Cell::new(0),
4434 };
4435
4436 lua.scope(|scope| {
4437 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4438 lua.globals().set("c", &ud)?;
4439 lua.load("c:inc(5); c:inc(7)").exec()
4440 })
4441 .expect("scope body should succeed");
4442 assert_eq!(counter.value, 12);
4443 }
4444
4445 #[test]
4451 fn scope_userdata_invalidated_after_scope_returns_runtime_error() {
4452 let lua = Lua::new();
4453 let mut counter = ScopedCounter {
4454 value: 99,
4455 calls: Cell::new(0),
4456 };
4457
4458 lua.scope(|scope| {
4459 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4460 lua.globals().set("leaked", &ud)?;
4461 Ok(())
4462 })
4463 .expect("scope body should succeed");
4464
4465 let err = lua
4466 .load("return leaked:get()")
4467 .eval::<i64>()
4468 .expect_err("scoped userdata must be unusable after scope ends");
4469 let msg = runtime_error_message(&err);
4470 assert!(
4471 msg.contains("no longer valid") || msg.contains("scope has ended"),
4472 "expected invalidation error, got: {msg}"
4473 );
4474 }
4475
4476 #[test]
4480 fn scope_userdata_invalidated_is_recoverable_via_pcall() {
4481 let lua = Lua::new();
4482 let mut counter = ScopedCounter {
4483 value: 5,
4484 calls: Cell::new(0),
4485 };
4486
4487 lua.scope(|scope| {
4488 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4489 lua.globals().set("leaked", &ud)?;
4490 Ok(())
4491 })
4492 .expect("scope body should succeed");
4493
4494 let (ok, _err_msg): (bool, String) = lua
4495 .load("local ok, e = pcall(function() return leaked:get() end); return ok, tostring(e)")
4496 .eval()
4497 .expect("pcall harness should produce two values");
4498 assert!(!ok, "post-scope call must fail");
4499 }
4500
4501 #[test]
4506 fn scope_userdata_reentrant_borrow_during_mut_method_returns_error() {
4507 let lua = Lua::new();
4508 let mut counter = ScopedCounter {
4509 value: 0,
4510 calls: Cell::new(0),
4511 };
4512
4513 let err = lua
4514 .scope(|scope| {
4515 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4516 lua.globals().set("c", &ud)?;
4517 lua.load("return c:inc_via_global()").eval::<i64>()
4518 })
4519 .expect_err("re-entry while mut-borrowed must fail");
4520 let msg = runtime_error_message(&err);
4521 assert!(
4522 msg.contains("already") && msg.contains("borrowed"),
4523 "expected borrow-conflict error, got: {msg}"
4524 );
4525 assert_eq!(counter.value, 1, "outer mutation persists despite inner failure");
4526 }
4527
4528 #[test]
4531 fn scope_userdata_reentrant_shared_borrows_are_compatible() {
4532 let lua = Lua::new();
4533 let mut counter = ScopedCounter {
4534 value: 17,
4535 calls: Cell::new(0),
4536 };
4537
4538 let observed: i64 = lua
4539 .scope(|scope| {
4540 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4541 lua.globals().set("c", &ud)?;
4542 lua.load("return c:call_get_via_global()").eval::<i64>()
4543 })
4544 .expect("nested shared borrows should succeed");
4545 assert_eq!(observed, 17);
4546 assert_eq!(counter.calls.get(), 1);
4547 }
4548
4549 #[test]
4553 fn scope_userdata_field_methods_get_and_set() {
4554 let lua = Lua::new();
4555 let mut bag = ScopedFielded { n: 3 };
4556
4557 let read_back: i64 = lua
4558 .scope(|scope| {
4559 let ud = scope.create_userdata_ref_mut(&lua, &mut bag)?;
4560 lua.globals().set("f", &ud)?;
4561 lua.load("f.n = f.n + 39; return f.n").eval::<i64>()
4562 })
4563 .expect("field methods should dispatch");
4564 assert_eq!(read_back, 42);
4565 assert_eq!(bag.n, 42);
4566 }
4567
4568 #[test]
4571 fn scope_userdata_meta_methods_dispatch() {
4572 let lua = Lua::new();
4573 let mut bag = ScopedBag { value: 100 };
4574
4575 let read: i64 = lua
4576 .scope(|scope| {
4577 let ud = scope.create_userdata_ref_mut(&lua, &mut bag)?;
4578 lua.globals().set("b", &ud)?;
4579 lua.load("b.value = 200; return b.value").eval::<i64>()
4580 })
4581 .expect("scoped meta-methods should dispatch");
4582 assert_eq!(read, 200);
4583 assert_eq!(bag.value, 200);
4584 }
4585
4586 #[test]
4589 fn scope_userdata_multiple_borrows_same_type_in_one_scope() {
4590 let lua = Lua::new();
4591 let mut a = ScopedCounter {
4592 value: 1,
4593 calls: Cell::new(0),
4594 };
4595 let mut b = ScopedCounter {
4596 value: 100,
4597 calls: Cell::new(0),
4598 };
4599
4600 lua.scope(|scope| {
4601 let ua = scope.create_userdata_ref_mut(&lua, &mut a)?;
4602 let ub = scope.create_userdata_ref_mut(&lua, &mut b)?;
4603 lua.globals().set("a", &ua)?;
4604 lua.globals().set("b", &ub)?;
4605 lua.load("a:inc(10); b:inc(1)").exec()
4606 })
4607 .expect("scope body should succeed");
4608 assert_eq!(a.value, 11);
4609 assert_eq!(b.value, 101);
4610 }
4611
4612 #[test]
4615 fn scope_userdata_different_types_coexist_in_one_scope() {
4616 let lua = Lua::new();
4617 let mut counter = ScopedCounter {
4618 value: 0,
4619 calls: Cell::new(0),
4620 };
4621 let mut bag = ScopedBag { value: 0 };
4622
4623 lua.scope(|scope| {
4624 let uc = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4625 let ub = scope.create_userdata_ref_mut(&lua, &mut bag)?;
4626 lua.globals().set("c", &uc)?;
4627 lua.globals().set("b", &ub)?;
4628 lua.load("c:inc(7); b.value = 13").exec()
4629 })
4630 .expect("scope body should succeed");
4631 assert_eq!(counter.value, 7);
4632 assert_eq!(bag.value, 13);
4633 }
4634
4635 #[test]
4638 fn scope_userdata_scope_returns_closure_value() {
4639 let lua = Lua::new();
4640 let mut counter = ScopedCounter {
4641 value: 4,
4642 calls: Cell::new(0),
4643 };
4644
4645 let doubled: i64 = lua
4646 .scope(|scope| {
4647 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4648 lua.globals().set("c", &ud)?;
4649 lua.load("return c:inc(c:get())").eval::<i64>()
4650 })
4651 .expect("scope body should succeed");
4652 assert_eq!(doubled, 8);
4653 assert_eq!(counter.value, 8);
4654 }
4655
4656 #[test]
4664 fn scope_userdata_metatable_cache_is_per_state() {
4665 let lua_a = Lua::new();
4666 let lua_b = Lua::new();
4667 let mut a = ScopedCounter {
4668 value: 1,
4669 calls: Cell::new(0),
4670 };
4671 let mut b = ScopedCounter {
4672 value: 2,
4673 calls: Cell::new(0),
4674 };
4675
4676 lua_a
4677 .scope(|scope| {
4678 let _ud = scope.create_userdata_ref_mut(&lua_a, &mut a)?;
4679 Ok(())
4680 })
4681 .expect("scope on A should succeed");
4682 lua_b
4683 .scope(|scope| {
4684 let _ud = scope.create_userdata_ref_mut(&lua_b, &mut b)?;
4685 Ok(())
4686 })
4687 .expect("scope on B should succeed");
4688
4689 let cache_a_len = lua_a.inner.userdata_scoped_metatables.borrow().len();
4690 let cache_b_len = lua_b.inner.userdata_scoped_metatables.borrow().len();
4691 assert_eq!(cache_a_len, 1);
4692 assert_eq!(cache_b_len, 1);
4693 }
4694
4695 #[test]
4700 fn scope_userdata_metatable_is_built_once_per_type() {
4701 let lua = Lua::new();
4702 let mut a = ScopedCounter {
4703 value: 0,
4704 calls: Cell::new(0),
4705 };
4706 let mut b = ScopedCounter {
4707 value: 0,
4708 calls: Cell::new(0),
4709 };
4710
4711 lua.scope(|scope| {
4712 let _ud = scope.create_userdata_ref_mut(&lua, &mut a)?;
4713 Ok(())
4714 })
4715 .expect("first scope should succeed");
4716 let after_first = lua.inner.userdata_scoped_metatables.borrow().len();
4717
4718 lua.scope(|scope| {
4719 let _ud = scope.create_userdata_ref_mut(&lua, &mut b)?;
4720 Ok(())
4721 })
4722 .expect("second scope should succeed");
4723 let after_second = lua.inner.userdata_scoped_metatables.borrow().len();
4724
4725 assert_eq!(after_first, 1);
4726 assert_eq!(after_second, 1);
4727 }
4728
4729 #[test]
4731 fn scope_userdata_rust_side_scoped_borrow_inside_scope() {
4732 let lua = Lua::new();
4733 let mut counter = ScopedCounter {
4734 value: 21,
4735 calls: Cell::new(0),
4736 };
4737
4738 let observed = lua
4739 .scope(|scope| {
4740 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4741 ud.scoped_borrow::<ScopedCounter, _>(|c| c.value)
4742 })
4743 .expect("scoped_borrow should succeed inside scope");
4744 assert_eq!(observed, 21);
4745 }
4746
4747 #[test]
4749 fn scope_userdata_rust_side_scoped_borrow_mut_inside_scope() {
4750 let lua = Lua::new();
4751 let mut counter = ScopedCounter {
4752 value: 0,
4753 calls: Cell::new(0),
4754 };
4755
4756 lua.scope(|scope| {
4757 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4758 ud.scoped_borrow_mut::<ScopedCounter, _>(|c| c.value = 5)
4759 })
4760 .expect("scoped_borrow_mut should succeed");
4761 assert_eq!(counter.value, 5);
4762 }
4763
4764 #[test]
4768 fn scope_userdata_rust_side_borrow_after_scope_errors() {
4769 let lua = Lua::new();
4770 let mut counter = ScopedCounter {
4771 value: 7,
4772 calls: Cell::new(0),
4773 };
4774
4775 let leaked: AnyUserData = lua
4776 .scope(|scope| scope.create_userdata_ref_mut(&lua, &mut counter))
4777 .expect("scope body should succeed");
4778
4779 let err = leaked
4780 .scoped_borrow::<ScopedCounter, _>(|c| c.value)
4781 .expect_err("post-scope Rust borrow must fail");
4782 let msg = runtime_error_message(&err);
4783 assert!(
4784 msg.contains("no longer valid") || msg.contains("scope has ended"),
4785 "expected invalidation error, got: {msg}"
4786 );
4787
4788 let err = leaked
4789 .scoped_borrow_mut::<ScopedCounter, _>(|c| c.value = 99)
4790 .expect_err("post-scope Rust mut-borrow must fail");
4791 let msg = runtime_error_message(&err);
4792 assert!(
4793 msg.contains("no longer valid") || msg.contains("scope has ended"),
4794 "expected invalidation error, got: {msg}"
4795 );
4796
4797 assert_eq!(counter.value, 7, "the borrow must not have been touched");
4798 }
4799
4800 #[test]
4806 fn scope_userdata_owned_borrow_path_rejects_scoped_cells() {
4807 let lua = Lua::new();
4808 let mut counter = ScopedCounter {
4809 value: 1,
4810 calls: Cell::new(0),
4811 };
4812
4813 let err = lua
4814 .scope(|scope| {
4815 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
4816 Ok(ud.with_borrow::<ScopedCounter, _>(|c| c.value))
4817 })
4818 .expect("scope body should succeed")
4819 .expect_err("owned borrow path must not reach a scoped cell");
4820 let msg = runtime_error_message(&err);
4821 assert!(
4822 msg.contains("type mismatch"),
4823 "expected type-mismatch error, got: {msg}"
4824 );
4825 }
4826
4827 #[test]
4830 fn scope_userdata_scoped_borrow_rejects_owned_cells() {
4831 let lua = Lua::new();
4832 let ud = lua
4833 .create_userdata(ScopedCounter {
4834 value: 5,
4835 calls: Cell::new(0),
4836 })
4837 .expect("owned userdata should create");
4838
4839 let err = ud
4840 .scoped_borrow::<ScopedCounter, _>(|c| c.value)
4841 .expect_err("scoped borrow must not reach an owned cell");
4842 let msg = runtime_error_message(&err);
4843 assert!(
4844 msg.contains("type mismatch"),
4845 "expected type-mismatch error, got: {msg}"
4846 );
4847 }
4848
4849 #[test]
4853 fn scope_function_captures_borrow_and_is_callable_from_lua() {
4854 let lua = Lua::new();
4855 let mut acc: i64 = 0;
4856
4857 let total: i64 = lua
4858 .scope(|scope| {
4859 let f = scope.create_function_mut(&lua, |_lua, n: i64| {
4860 acc += n;
4861 Ok(acc)
4862 })?;
4863 lua.globals().set("add", &f)?;
4864 lua.load("add(2); add(3); return add(5)").eval::<i64>()
4865 })
4866 .expect("scoped function should dispatch");
4867 assert_eq!(total, 10);
4868 assert_eq!(acc, 10);
4869 }
4870
4871 #[test]
4874 fn scope_function_calls_share_one_closure() {
4875 let lua = Lua::new();
4876 let counts = Cell::new(0u32);
4877
4878 lua.scope(|scope| {
4879 let f = scope.create_function(&lua, |_lua, ()| {
4880 counts.set(counts.get() + 1);
4881 Ok(())
4882 })?;
4883 lua.globals().set("tick", &f)?;
4884 lua.load("for _ = 1, 4 do tick() end").exec()
4885 })
4886 .expect("scope should succeed");
4887 assert_eq!(counts.get(), 4);
4888 }
4889
4890 #[test]
4894 fn scope_function_invalidated_after_scope_returns_runtime_error() {
4895 let lua = Lua::new();
4896 let mut acc: i64 = 0;
4897
4898 lua.scope(|scope| {
4899 let f = scope.create_function_mut(&lua, |_lua, n: i64| {
4900 acc += n;
4901 Ok(acc)
4902 })?;
4903 lua.globals().set("add", &f)?;
4904 lua.load("add(1)").exec()
4905 })
4906 .expect("scope body should succeed");
4907 assert_eq!(acc, 1);
4908
4909 let err = lua
4910 .load("return add(100)")
4911 .eval::<i64>()
4912 .expect_err("post-scope call must fail");
4913 let msg = runtime_error_message(&err);
4914 assert!(
4915 msg.contains("no longer valid") || msg.contains("scope has ended"),
4916 "expected invalidation error, got: {msg}"
4917 );
4918 assert_eq!(acc, 1, "the closure's borrow must not have been touched");
4919 }
4920
4921 #[test]
4925 fn scope_function_reentrant_fnmut_is_rejected() {
4926 let lua = Lua::new();
4927 let mut count: i64 = 0;
4928
4929 let err = lua
4930 .scope(|scope| {
4931 let f = scope.create_function_mut(&lua, |lua, ()| {
4932 count += 1;
4933 if count < 2 {
4934 lua.load("recurse()").exec()?;
4935 }
4936 Ok(())
4937 })?;
4938 lua.globals().set("recurse", &f)?;
4939 lua.load("recurse()").exec()
4940 })
4941 .expect_err("re-entrant FnMut must error");
4942 let msg = runtime_error_message(&err);
4943 assert!(
4944 msg.contains("already borrowed"),
4945 "expected FnMut-conflict error, got: {msg}"
4946 );
4947 }
4948
4949 #[test]
4954 fn scope_function_and_userdata_in_same_scope() {
4955 let lua = Lua::new();
4956 let mut bag = ScopedFielded { n: 0 };
4957 let log = Cell::new(0i64);
4958
4959 lua.scope(|scope| {
4960 let ud = scope.create_userdata_ref_mut(&lua, &mut bag)?;
4961 let logger = scope.create_function(&lua, |_lua, n: i64| {
4962 log.set(log.get() + n);
4963 Ok(())
4964 })?;
4965 lua.globals().set("b", &ud)?;
4966 lua.globals().set("log", &logger)?;
4967 lua.load("b.n = 42; log(b.n); log(b.n + 1)").exec()
4968 })
4969 .expect("mixed scope body should succeed");
4970 assert_eq!(bag.n, 42);
4971 assert_eq!(log.get(), 85);
4972 }
4973
4974 #[test]
4978 fn scope_function_invalidated_even_when_body_errors() {
4979 let lua = Lua::new();
4980 let value = Cell::new(5i64);
4981
4982 let _err = lua
4983 .scope(|scope| -> Result<()> {
4984 let f = scope.create_function(&lua, |_lua, ()| Ok(value.get()))?;
4985 lua.globals().set("get", &f)?;
4986 Err(LuaError::runtime(format_args!("aborting")))
4987 })
4988 .expect_err("scope body should propagate error");
4989
4990 let err = lua
4991 .load("return get()")
4992 .eval::<i64>()
4993 .expect_err("function must be invalidated after error-exit scope");
4994 let msg = runtime_error_message(&err);
4995 assert!(
4996 msg.contains("no longer valid") || msg.contains("scope has ended"),
4997 "expected invalidation error, got: {msg}"
4998 );
4999 }
5000
5001 #[test]
5005 fn scope_function_many_closures_in_one_scope() {
5006 let lua = Lua::new();
5007 let total = Cell::new(0i64);
5008 let total_ref = &total;
5009
5010 lua.scope(|scope| {
5011 for i in 1..=8 {
5012 let f = scope.create_function(&lua, move |_lua, ()| {
5013 total_ref.set(total_ref.get() + i);
5014 Ok(())
5015 })?;
5016 lua.globals().set(format!("f{}", i).as_str(), &f)?;
5017 }
5018 lua.load("f1(); f2(); f3(); f4(); f5(); f6(); f7(); f8()").exec()
5019 })
5020 .expect("scope with many closures should succeed");
5021 assert_eq!(total.get(), 36);
5022 }
5023
5024 #[test]
5029 fn scope_userdata_invalidated_even_when_body_errors() {
5030 let lua = Lua::new();
5031 let mut counter = ScopedCounter {
5032 value: 1,
5033 calls: Cell::new(0),
5034 };
5035
5036 let err = lua
5037 .scope(|scope| -> Result<()> {
5038 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
5039 lua.globals().set("c", &ud)?;
5040 Err(LuaError::runtime(format_args!("aborting scope")))
5041 })
5042 .expect_err("scope body should propagate error");
5043 let _ = err;
5044
5045 let leaked_err = lua
5046 .load("return c:get()")
5047 .eval::<i64>()
5048 .expect_err("leaked userdata must still be invalidated");
5049 let msg = runtime_error_message(&leaked_err);
5050 assert!(
5051 msg.contains("no longer valid") || msg.contains("scope has ended"),
5052 "expected invalidation error after scope-with-error, got: {msg}"
5053 );
5054 }
5055
5056 #[test]
5062 fn scope_userdata_cloned_handles_invalidate_together() {
5063 let lua = Lua::new();
5064 let mut counter = ScopedCounter {
5065 value: 9,
5066 calls: Cell::new(0),
5067 };
5068
5069 lua.scope(|scope| {
5070 let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
5071 let clone = ud.clone();
5072 lua.globals().set("a", &ud)?;
5073 lua.globals().set("b", &clone)?;
5074 lua.load("assert(a:get() == 9); assert(b:get() == 9)").exec()
5075 })
5076 .expect("scope body should succeed");
5077
5078 let err_a = lua
5079 .load("return a:get()")
5080 .eval::<i64>()
5081 .expect_err("original handle must error post-scope");
5082 let err_b = lua
5083 .load("return b:get()")
5084 .eval::<i64>()
5085 .expect_err("cloned handle must error post-scope");
5086 assert!(runtime_error_message(&err_a).contains("no longer valid"));
5087 assert!(runtime_error_message(&err_b).contains("no longer valid"));
5088 }
5089
5090 #[test]
5094 fn scope_userdata_nested_scopes_isolated() {
5095 let lua = Lua::new();
5096 let mut outer_counter = ScopedCounter {
5097 value: 1,
5098 calls: Cell::new(0),
5099 };
5100 let mut inner_counter = ScopedCounter {
5101 value: 100,
5102 calls: Cell::new(0),
5103 };
5104
5105 lua.scope(|outer| {
5106 let o = outer.create_userdata_ref_mut(&lua, &mut outer_counter)?;
5107 lua.globals().set("outer", &o)?;
5108
5109 lua.scope(|inner| {
5110 let i = inner.create_userdata_ref_mut(&lua, &mut inner_counter)?;
5111 lua.globals().set("inner", &i)?;
5112 lua.load("assert(outer:get() == 1); assert(inner:get() == 100)").exec()
5113 })?;
5114
5115 let inner_err = lua
5117 .load("return inner:get()")
5118 .eval::<i64>()
5119 .expect_err("inner userdata must be dead after inner scope");
5120 assert!(runtime_error_message(&inner_err).contains("no longer valid"));
5121
5122 let outer_alive: i64 = lua
5123 .load("return outer:get()")
5124 .eval()
5125 .expect("outer userdata must still be alive in outer scope");
5126 assert_eq!(outer_alive, 1);
5127 Ok(())
5128 })
5129 .expect("scope body should succeed");
5130
5131 let err = lua
5133 .load("return outer:get()")
5134 .eval::<i64>()
5135 .expect_err("outer userdata must be dead after outer scope");
5136 assert!(runtime_error_message(&err).contains("no longer valid"));
5137 }
5138
5139 #[test]
5150 fn scope_cell_shared_then_shared_succeeds() {
5151 let mut data = 17_i32;
5152 let cell = ScopedCell::<i32>::new(&mut data);
5153
5154 let a = cell.try_borrow().expect("first shared borrow");
5155 let b = cell.try_borrow().expect("second shared borrow");
5156 assert_eq!(*a, 17);
5157 assert_eq!(*b, 17);
5158 drop(a);
5159 drop(b);
5160
5161 cell.invalidate();
5162 assert!(cell.try_borrow().is_err(), "post-invalidate must fail");
5163 }
5164
5165 #[test]
5166 fn scope_cell_mut_then_shared_fails() {
5167 let mut data = 5_i32;
5168 let cell = ScopedCell::<i32>::new(&mut data);
5169
5170 let mut m = cell.try_borrow_mut().expect("first mut borrow");
5171 *m = 42;
5172 let s = cell.try_borrow();
5173 assert!(s.is_err(), "shared borrow while mut-held must fail");
5174 drop(m);
5175
5176 let s = cell.try_borrow().expect("shared borrow after mut release");
5177 assert_eq!(*s, 42);
5178 }
5179
5180 #[test]
5181 fn scope_cell_shared_then_mut_fails() {
5182 let mut data = 99_i32;
5183 let cell = ScopedCell::<i32>::new(&mut data);
5184
5185 let s = cell.try_borrow().expect("first shared borrow");
5186 let m = cell.try_borrow_mut();
5187 assert!(m.is_err(), "mut borrow while shared-held must fail");
5188 drop(s);
5189
5190 let mut m = cell.try_borrow_mut().expect("mut borrow after shared release");
5191 *m = 100;
5192 drop(m);
5193 assert_eq!(data, 100);
5194 }
5195
5196 #[test]
5197 fn scope_cell_invalidate_after_drop_of_guards_is_clean() {
5198 let mut data = String::from("hi");
5199 let cell = ScopedCell::<String>::new(&mut data);
5200 {
5201 let guard = cell.try_borrow().expect("borrow");
5202 assert_eq!(&*guard, "hi");
5203 }
5204 cell.invalidate();
5205 assert!(cell.try_borrow().is_err());
5206 assert!(cell.try_borrow_mut().is_err());
5207 }
5208
5209 #[test]
5210 fn scope_cell_drop_guard_decrements_borrow_count() {
5211 let mut data = 0_i32;
5212 let cell = ScopedCell::<i32>::new(&mut data);
5213 {
5214 let _a = cell.try_borrow().expect("a");
5215 let _b = cell.try_borrow().expect("b");
5216 assert!(cell.try_borrow_mut().is_err());
5217 }
5218 cell.try_borrow_mut().expect("mut borrow once guards drop");
5219 }
5220
5221 #[test]
5222 fn scope_fn_cell_dispatches_and_invalidates() {
5223 let counter = Cell::new(0i64);
5224 let adapter: Box<dyn Fn(&Lua, Vec<Value>) -> Result<Vec<Value>>> =
5225 Box::new(|_lua, _args| Ok(Vec::new()));
5226 let cell = Rc::new(ScopedFnCell {
5227 boxed: RefCell::new(Some(adapter)),
5228 });
5229
5230 let lua = Lua::new();
5231 cell.try_call(&lua, Vec::new()).expect("pre-invalidate call");
5232 counter.set(counter.get() + 1);
5233
5234 cell.invalidate();
5235
5236 let err = cell
5237 .try_call(&lua, Vec::new())
5238 .expect_err("post-invalidate call must fail");
5239 let msg = runtime_error_message(&err);
5240 assert!(msg.contains("no longer valid"), "got: {msg}");
5241 assert_eq!(counter.get(), 1);
5242 }
5243}