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