1use std::any::{Any, TypeId};
66use std::cell::{Cell, Ref, RefCell, RefMut};
67use std::collections::HashMap;
68use std::ffi::c_void;
69use std::fmt;
70use std::hash::Hash;
71use std::ops::{Deref, DerefMut};
72use std::panic::{catch_unwind, AssertUnwindSafe};
73use std::rc::Rc;
74
75use lua_stdlib::auxlib::load_buffer;
76use lua_stdlib::init::open_libs;
77use lua_types::closure::{LuaCClosure as RawLuaCClosure, LuaClosure as RawLuaClosure, LuaLClosure};
78use lua_types::gc::GcRef;
79use lua_types::string::LuaString as RawLuaString;
80use lua_types::upval::UpVal;
81use lua_types::userdata::LuaUserData as RawLuaUserData;
82use lua_types::value::{LuaTable as RawLuaTable, LuaValue as RawLuaValue};
83use lua_vm::state::{
84 new_state, CpuClockHook, DynLibLoadHook, DynLibSymbolHook, DynLibUnloadHook, EntropyHook,
85 EnvHook, ExternalRootKey, FileLoaderHook, FileOpenHook, FileRemoveHook, FileRenameHook,
86 InputHook, LuaCallable, LuaRustFunction, LuaState, OsExecuteHook, OutputHook, PopenHook,
87 TempNameHook, UnixTimeHook,
88};
89
90pub use lua_types::{LuaError, LuaFileHandle};
91pub use lua_vm::state::{DynLibId, DynamicSymbol, OsExecuteReason, OsExecuteResult};
92
93#[cfg(feature = "derive")]
94pub use lua_rs_derive::{lua_methods, LuaUserData};
95
96pub type Error = LuaError;
97pub type Result<T> = std::result::Result<T, Error>;
98
99#[derive(Clone, Copy, Default)]
107pub struct HostHooks {
108 pub file_loader_hook: Option<FileLoaderHook>,
109 pub file_open_hook: Option<FileOpenHook>,
110 pub stdin_hook: Option<InputHook>,
111 pub stdout_hook: Option<OutputHook>,
112 pub stderr_hook: Option<OutputHook>,
113 pub env_hook: Option<EnvHook>,
114 pub unix_time_hook: Option<UnixTimeHook>,
115 pub cpu_clock_hook: Option<CpuClockHook>,
116 pub entropy_hook: Option<EntropyHook>,
117 pub temp_name_hook: Option<TempNameHook>,
118 pub popen_hook: Option<PopenHook>,
119 pub file_remove_hook: Option<FileRemoveHook>,
120 pub file_rename_hook: Option<FileRenameHook>,
121 pub os_execute_hook: Option<OsExecuteHook>,
122 pub dynlib_load_hook: Option<DynLibLoadHook>,
123 pub dynlib_symbol_hook: Option<DynLibSymbolHook>,
124 pub dynlib_unload_hook: Option<DynLibUnloadHook>,
125}
126
127impl HostHooks {
128 pub fn new() -> Self {
129 Self::default()
130 }
131
132 pub fn file_loader(mut self, hook: FileLoaderHook) -> Self {
133 self.file_loader_hook = Some(hook);
134 self
135 }
136
137 pub fn file_open(mut self, hook: FileOpenHook) -> Self {
138 self.file_open_hook = Some(hook);
139 self
140 }
141
142 pub fn stdin(mut self, hook: InputHook) -> Self {
143 self.stdin_hook = Some(hook);
144 self
145 }
146
147 pub fn stdout(mut self, hook: OutputHook) -> Self {
148 self.stdout_hook = Some(hook);
149 self
150 }
151
152 pub fn stderr(mut self, hook: OutputHook) -> Self {
153 self.stderr_hook = Some(hook);
154 self
155 }
156
157 pub fn env(mut self, hook: EnvHook) -> Self {
158 self.env_hook = Some(hook);
159 self
160 }
161
162 pub fn unix_time(mut self, hook: UnixTimeHook) -> Self {
163 self.unix_time_hook = Some(hook);
164 self
165 }
166
167 pub fn cpu_clock(mut self, hook: CpuClockHook) -> Self {
168 self.cpu_clock_hook = Some(hook);
169 self
170 }
171
172 pub fn entropy(mut self, hook: EntropyHook) -> Self {
173 self.entropy_hook = Some(hook);
174 self
175 }
176
177 pub fn temp_name(mut self, hook: TempNameHook) -> Self {
178 self.temp_name_hook = Some(hook);
179 self
180 }
181
182 pub fn popen(mut self, hook: PopenHook) -> Self {
183 self.popen_hook = Some(hook);
184 self
185 }
186
187 pub fn file_remove(mut self, hook: FileRemoveHook) -> Self {
188 self.file_remove_hook = Some(hook);
189 self
190 }
191
192 pub fn file_rename(mut self, hook: FileRenameHook) -> Self {
193 self.file_rename_hook = Some(hook);
194 self
195 }
196
197 pub fn os_execute(mut self, hook: OsExecuteHook) -> Self {
198 self.os_execute_hook = Some(hook);
199 self
200 }
201
202 pub fn dynlib_load(mut self, hook: DynLibLoadHook) -> Self {
203 self.dynlib_load_hook = Some(hook);
204 self
205 }
206
207 pub fn dynlib_symbol(mut self, hook: DynLibSymbolHook) -> Self {
208 self.dynlib_symbol_hook = Some(hook);
209 self
210 }
211
212 pub fn dynlib_unload(mut self, hook: DynLibUnloadHook) -> Self {
213 self.dynlib_unload_hook = Some(hook);
214 self
215 }
216
217 pub fn install(self, state: &mut LuaState) {
218 let global = &mut *state.global_mut();
219 global.file_loader_hook = self.file_loader_hook;
220 global.file_open_hook = self.file_open_hook;
221 global.stdin_hook = self.stdin_hook;
222 global.stdout_hook = self.stdout_hook;
223 global.stderr_hook = self.stderr_hook;
224 global.env_hook = self.env_hook;
225 global.unix_time_hook = self.unix_time_hook;
226 global.cpu_clock_hook = self.cpu_clock_hook;
227 global.entropy_hook = self.entropy_hook;
228 global.temp_name_hook = self.temp_name_hook;
229 global.popen_hook = self.popen_hook;
230 global.file_remove_hook = self.file_remove_hook;
231 global.file_rename_hook = self.file_rename_hook;
232 global.os_execute_hook = self.os_execute_hook;
233 global.dynlib_load_hook = self.dynlib_load_hook;
234 global.dynlib_symbol_hook = self.dynlib_symbol_hook;
235 global.dynlib_unload_hook = self.dynlib_unload_hook;
236 }
237}
238
239#[derive(Clone)]
246pub struct Lua {
247 inner: Rc<LuaInner>,
248}
249
250struct LuaInner {
251 state: RefCell<LuaState>,
252 active_state: Cell<*mut LuaState>,
253 pending_external_unroots: RefCell<Vec<ExternalRootKey>>,
254 userdata_metatables: RefCell<HashMap<TypeId, GcRef<RawLuaTable>>>,
259}
260
261struct UserDataCell<T> {
262 value: RefCell<T>,
263}
264
265struct RustCallbackCell {
266 function: LuaRustFunction,
267}
268
269struct ActiveStateGuard<'a> {
270 inner: &'a LuaInner,
271 previous: *mut LuaState,
272}
273
274impl Drop for ActiveStateGuard<'_> {
275 fn drop(&mut self) {
276 self.inner.active_state.set(self.previous);
277 }
278}
279
280impl LuaInner {
281 fn enter_active(&self, state: *mut LuaState) -> ActiveStateGuard<'_> {
282 let previous = self.active_state.replace(state);
283 ActiveStateGuard {
284 inner: self,
285 previous,
286 }
287 }
288
289 fn flush_pending_external_unroots(&self, state: &mut LuaState) {
290 let pending = self.pending_external_unroots.replace(Vec::new());
291 if pending.is_empty() {
292 return;
293 }
294
295 let mut still_pending = Vec::new();
296 for key in pending {
297 if state.try_external_unroot_value(key).is_err() {
298 still_pending.push(key);
299 }
300 }
301
302 if !still_pending.is_empty() {
303 self.pending_external_unroots
304 .borrow_mut()
305 .extend(still_pending);
306 }
307 }
308}
309
310impl fmt::Debug for Lua {
311 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312 f.debug_struct("Lua").finish_non_exhaustive()
313 }
314}
315
316impl Lua {
317 pub fn new() -> Self {
319 Self::try_new().expect("Lua runtime should initialize")
320 }
321
322 pub fn try_new() -> Result<Self> {
324 Self::with_hooks(HostHooks::default())
325 }
326
327 pub fn with_hooks(hooks: HostHooks) -> Result<Self> {
329 let mut state = new_state().ok_or(LuaError::Memory)?;
330 install_parser_hook(&mut state);
331 hooks.install(&mut state);
332 open_libs(&mut state)?;
333 Ok(Self::from_initialized_state(state))
334 }
335
336 fn from_initialized_state(state: LuaState) -> Self {
337 Lua {
338 inner: Rc::new(LuaInner {
339 state: RefCell::new(state),
340 active_state: Cell::new(std::ptr::null_mut()),
341 pending_external_unroots: RefCell::new(Vec::new()),
342 userdata_metatables: RefCell::new(HashMap::new()),
343 }),
344 }
345 }
346
347 fn with_state<R>(&self, f: impl FnOnce(&mut LuaState) -> R) -> R {
348 if let Ok(mut state) = self.inner.state.try_borrow_mut() {
349 let _active = self.inner.enter_active(&mut *state);
350 self.inner.flush_pending_external_unroots(&mut state);
351 let result = f(&mut state);
352 self.inner.flush_pending_external_unroots(&mut state);
353 return result;
354 }
355
356 let state = self
357 .active_state_mut()
358 .expect("re-entrant Lua access without an active state");
359 let result = f(state);
360 self.inner.flush_pending_external_unroots(state);
361 result
362 }
363
364 fn active_state_mut(&self) -> Option<&mut LuaState> {
365 let state = self.inner.active_state.get();
366 if state.is_null() {
367 return None;
368 }
369
370 Some(unsafe { &mut *state })
376 }
377
378 fn unroot_external_key(&self, key: ExternalRootKey) {
379 let removed = if let Ok(mut state) = self.inner.state.try_borrow_mut() {
380 let _active = self.inner.enter_active(&mut *state);
381 self.inner.flush_pending_external_unroots(&mut state);
382 let removed = state.try_external_unroot_value(key).is_ok();
383 self.inner.flush_pending_external_unroots(&mut state);
384 removed
385 } else {
386 if let Some(state) = self.active_state_mut() {
387 let removed = state.try_external_unroot_value(key).is_ok();
388 self.inner.flush_pending_external_unroots(state);
389 removed
390 } else {
391 false
392 }
393 };
394
395 if !removed {
396 self.inner.pending_external_unroots.borrow_mut().push(key);
397 }
398 }
399
400 fn root_raw(&self, value: RawLuaValue) -> RootedValue {
401 let key = self.with_state(|state| state.external_root_value(value));
402 RootedValue {
403 lua: self.clone(),
404 key,
405 }
406 }
407
408 fn root_raw_in_state(&self, state: &mut LuaState, value: RawLuaValue) -> RootedValue {
409 let key = state.external_root_value(value);
410 RootedValue {
411 lua: self.clone(),
412 key,
413 }
414 }
415
416 fn userdata_cell<'a, T: 'static>(
417 &self,
418 userdata: &'a AnyUserData,
419 ) -> Result<&'a UserDataCell<T>> {
420 if !Rc::ptr_eq(&self.inner, &userdata.root.lua.inner) {
421 return Err(LuaError::runtime(format_args!(
422 "Lua userdata belongs to a different state"
423 )));
424 }
425 userdata.host_cell()
426 }
427
428 pub fn load(&self, source: impl AsRef<[u8]>) -> Chunk {
430 Chunk {
431 lua: self.clone(),
432 source: source.as_ref().to_vec(),
433 name: b"chunk".to_vec(),
434 }
435 }
436
437 pub fn globals(&self) -> Table {
439 let raw = self.with_state(|state| state.global().globals.clone());
440 Table {
441 root: self.root_raw(raw),
442 }
443 }
444
445 pub fn create_table(&self) -> Result<Table> {
447 let root = self.with_state(|state| {
448 let _heap_guard = heap_guard(state);
449 let table = state.new_table();
450 let raw = RawLuaValue::Table(table);
451 let key = state.external_root_value(raw);
452 state.gc().check_step();
453 RootedValue {
454 lua: self.clone(),
455 key,
456 }
457 });
458 Ok(Table { root })
459 }
460
461 pub fn create_string(&self, bytes: impl AsRef<[u8]>) -> Result<LuaString> {
463 let bytes = bytes.as_ref();
464 let root = self.with_state(|state| {
465 let _heap_guard = heap_guard(state);
466 let string = state.new_string(bytes)?;
467 let raw = RawLuaValue::Str(string);
468 let key = state.external_root_value(raw);
469 state.gc().check_step();
470 Ok::<_, LuaError>(RootedValue {
471 lua: self.clone(),
472 key,
473 })
474 })?;
475 Ok(LuaString { root })
476 }
477
478 pub fn create_function<A, R, F>(&self, func: F) -> Result<Function>
479 where
480 A: FromLuaMulti + 'static,
481 R: IntoLuaMulti + 'static,
482 F: Fn(&Lua, A) -> Result<R> + 'static,
483 {
484 let lua = self.clone();
485 let callable: LuaRustFunction = Rc::new(move |state| {
486 match catch_unwind(AssertUnwindSafe(|| {
487 let args = callback_args(state, &lua)?;
488 let args = A::from_lua_multi(args, &lua)?;
489 let returns = func(&lua, args)?;
490 let returns = returns.into_lua_multi(&lua)?;
491 push_callback_returns(state, &lua, returns)
492 })) {
493 Ok(result) => result,
494 Err(_) => Err(LuaError::runtime(format_args!("Rust callback panicked"))),
495 }
496 });
497 self.create_registered_function(callable)
498 }
499
500 pub fn create_function_mut<A, R, F>(&self, func: F) -> Result<Function>
501 where
502 A: FromLuaMulti + 'static,
503 R: IntoLuaMulti + 'static,
504 F: FnMut(&Lua, A) -> Result<R> + 'static,
505 {
506 let func = RefCell::new(func);
507 self.create_function(move |lua, args| {
508 let mut func = func.try_borrow_mut().map_err(|_| {
509 LuaError::runtime(format_args!("mutable Rust callback is already borrowed"))
510 })?;
511 func(lua, args)
512 })
513 }
514
515 fn create_registered_function(&self, callable: LuaRustFunction) -> Result<Function> {
516 let root = self.with_state(|state| {
517 let trampoline = rust_callback_trampoline as lua_vm::state::LuaCFunction;
518 let idx = {
519 let mut global = state.global_mut();
520 match global.c_functions.iter().position(|existing| {
521 existing
522 .as_bare()
523 .is_some_and(|existing| std::ptr::fn_addr_eq(existing, trampoline))
524 }) {
525 Some(idx) => idx,
526 None => {
527 let idx = global.c_functions.len();
528 global.c_functions.push(LuaCallable::bare(trampoline));
529 idx
530 }
531 }
532 };
533 let raw = with_heap_guard(state, || {
534 let callback_payload = GcRef::new(RawLuaUserData {
535 data: Box::new([]),
536 uv: Vec::new(),
537 metatable: RefCell::new(None),
538 host_value: RefCell::new(Some(
539 Rc::new(RustCallbackCell { function: callable }) as Rc<dyn Any>,
540 )),
541 });
542 RawLuaValue::Function(RawLuaClosure::C(GcRef::new(RawLuaCClosure {
543 func: idx,
544 upvalues: vec![RawLuaValue::UserData(callback_payload)],
545 })))
546 });
547 let key = state.external_root_value(raw);
548 state.gc().check_step();
549 RootedValue {
550 lua: self.clone(),
551 key,
552 }
553 });
554 Ok(Function { root })
555 }
556
557 fn create_userdata_method<T, A, R, F>(&self, method: F) -> Result<Function>
558 where
559 T: UserData,
560 A: FromLuaMulti + 'static,
561 R: IntoLuaMulti + 'static,
562 F: Fn(&Lua, &T, A) -> Result<R> + 'static,
563 {
564 let lua = self.clone();
565 let callable: LuaRustFunction = Rc::new(move |state| {
566 match catch_unwind(AssertUnwindSafe(|| {
567 let (userdata, args) = callback_userdata_args(state, &lua)?;
568 let args = A::from_lua_multi(args, &lua)?;
569 let cell = lua.userdata_cell::<T>(&userdata)?;
570 let value = cell.value.try_borrow().map_err(|_| {
571 LuaError::runtime(format_args!("userdata is already mutably borrowed"))
572 })?;
573 let returns = method(&lua, &value, args)?;
574 let returns = returns.into_lua_multi(&lua)?;
575 push_callback_returns(state, &lua, returns)
576 })) {
577 Ok(result) => result,
578 Err(_) => Err(LuaError::runtime(format_args!(
579 "Rust userdata method panicked"
580 ))),
581 }
582 });
583 self.create_registered_function(callable)
584 }
585
586 fn create_userdata_method_mut<T, A, R, F>(&self, method: F) -> Result<Function>
587 where
588 T: UserData,
589 A: FromLuaMulti + 'static,
590 R: IntoLuaMulti + 'static,
591 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
592 {
593 let lua = self.clone();
594 let callable: LuaRustFunction = Rc::new(move |state| {
595 match catch_unwind(AssertUnwindSafe(|| {
596 let (userdata, args) = callback_userdata_args(state, &lua)?;
597 let args = A::from_lua_multi(args, &lua)?;
598 let cell = lua.userdata_cell::<T>(&userdata)?;
599 let mut value = cell
600 .value
601 .try_borrow_mut()
602 .map_err(|_| LuaError::runtime(format_args!("userdata is already borrowed")))?;
603 let returns = method(&lua, &mut value, args)?;
604 let returns = returns.into_lua_multi(&lua)?;
605 push_callback_returns(state, &lua, returns)
606 })) {
607 Ok(result) => result,
608 Err(_) => Err(LuaError::runtime(format_args!(
609 "Rust userdata method panicked"
610 ))),
611 }
612 });
613 self.create_registered_function(callable)
614 }
615
616 pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData>
617 where
618 T: UserData,
619 {
620 let type_id = TypeId::of::<T>();
621 let cached = self
622 .inner
623 .userdata_metatables
624 .borrow()
625 .get(&type_id)
626 .cloned();
627 let metatable = match cached {
628 Some(metatable) => metatable,
629 None => {
630 let mut methods = UserDataMethodRegistry::<T>::new(self);
631 T::add_methods(&mut methods);
632 T::add_meta_methods(&mut methods);
633 let metatable = methods.build_metatable()?;
634 self.inner
635 .userdata_metatables
636 .borrow_mut()
637 .insert(type_id, metatable.clone());
638 metatable
639 }
640 };
641 self.attach_userdata(data, metatable)
642 }
643
644 fn attach_userdata<T: UserData>(
648 &self,
649 data: T,
650 metatable: GcRef<RawLuaTable>,
651 ) -> Result<AnyUserData> {
652 let cell: Rc<dyn Any> = Rc::new(UserDataCell {
653 value: RefCell::new(data),
654 });
655 let host_value = cell.clone();
656 let root = self.with_state(|state| {
657 let userdata = with_heap_guard(state, || {
658 GcRef::new(RawLuaUserData {
659 data: Box::new([]),
660 uv: Vec::new(),
661 metatable: RefCell::new(None),
662 host_value: RefCell::new(None),
663 })
664 });
665 userdata.set_metatable(Some(metatable));
666 userdata.set_host_value(Some(cell));
667 let key = state.external_root_value(RawLuaValue::UserData(userdata));
668 RootedValue {
669 lua: self.clone(),
670 key,
671 }
672 });
673 Ok(AnyUserData {
674 root,
675 host_value: Some(host_value),
676 })
677 }
678
679 pub fn gc_collect(&self) {
681 self.with_state(|state| state.gc().full_collect());
682 }
683}
684
685pub struct Chunk {
686 lua: Lua,
687 source: Vec<u8>,
688 name: Vec<u8>,
689}
690
691impl Chunk {
692 pub fn set_name(mut self, name: impl AsRef<[u8]>) -> Self {
693 self.name = name.as_ref().to_vec();
694 self
695 }
696
697 pub fn exec(self) -> Result<()> {
698 self.lua
699 .with_state(|state| exec_state(state, &self.source, &self.name))
700 }
701
702 pub fn eval<T: FromLuaMulti>(self) -> Result<T> {
703 let raws = self.lua.with_state(|state| {
704 let saved_top = state.top_idx();
705 let status = load_buffer(state, &self.source, &self.name)?;
706 if status != 0 {
707 let err = state.pop();
708 state.set_top_idx(saved_top);
709 return Err(LuaError::from_value(err));
710 }
711 match lua_vm::api::pcall_k(state, 0, T::NRESULTS, 0, 0, None) {
712 Ok(_) => {
713 let nresults = if T::NRESULTS < 0 {
714 state.top_idx().0.saturating_sub(saved_top.0) as i32
715 } else {
716 T::NRESULTS
717 };
718 let mut values = Vec::with_capacity(nresults as usize);
719 for _ in 0..nresults {
720 values.push(state.pop());
721 }
722 values.reverse();
723 state.set_top_idx(saved_top);
724 Ok(values)
725 }
726 Err(err) => {
727 state.set_top_idx(saved_top);
728 Err(err)
729 }
730 }
731 })?;
732 let values = raws
733 .into_iter()
734 .map(|raw| Value::from_raw(&self.lua, raw))
735 .collect::<Result<Vec<_>>>()?;
736 T::from_lua_multi(values, &self.lua)
737 }
738}
739
740#[derive(Debug)]
741struct RootedValue {
742 lua: Lua,
743 key: ExternalRootKey,
744}
745
746impl RootedValue {
747 fn raw(&self) -> Result<RawLuaValue> {
748 self.lua
749 .with_state(|state| state.external_rooted_value(self.key))
750 .ok_or_else(stale_handle_error)
751 }
752
753 fn raw_for_lua(&self, lua: &Lua, state: &LuaState) -> Result<RawLuaValue> {
754 if !Rc::ptr_eq(&self.lua.inner, &lua.inner) {
755 return Err(LuaError::runtime(format_args!(
756 "Lua handle belongs to a different state"
757 )));
758 }
759 state
760 .external_rooted_value(self.key)
761 .ok_or_else(stale_handle_error)
762 }
763}
764
765impl Clone for RootedValue {
766 fn clone(&self) -> Self {
767 let raw = self.raw().expect("rooted Lua handle should not be stale");
768 self.lua.root_raw(raw)
769 }
770}
771
772impl Drop for RootedValue {
773 fn drop(&mut self) {
774 self.lua.unroot_external_key(self.key);
775 }
776}
777
778#[derive(Debug, Clone)]
780pub enum Value {
781 Nil,
782 Boolean(bool),
783 Integer(i64),
784 Number(f64),
785 String(LuaString),
786 Table(Table),
787 Function(Function),
788 UserData(AnyUserData),
789 LightUserData(*mut c_void),
790 Thread(Thread),
791}
792
793impl Value {
794 fn from_raw(lua: &Lua, raw: RawLuaValue) -> Result<Self> {
795 lua.with_state(|state| Self::from_raw_in_state(lua, state, raw))
796 }
797
798 fn from_raw_in_state(lua: &Lua, state: &mut LuaState, raw: RawLuaValue) -> Result<Self> {
799 Ok(match raw {
800 RawLuaValue::Nil => Value::Nil,
801 RawLuaValue::Bool(v) => Value::Boolean(v),
802 RawLuaValue::Int(v) => Value::Integer(v),
803 RawLuaValue::Float(v) => Value::Number(v),
804 RawLuaValue::Str(v) => Value::String(LuaString {
805 root: lua.root_raw_in_state(state, RawLuaValue::Str(v)),
806 }),
807 RawLuaValue::Table(v) => Value::Table(Table {
808 root: lua.root_raw_in_state(state, RawLuaValue::Table(v)),
809 }),
810 RawLuaValue::Function(v) => Value::Function(Function {
811 root: lua.root_raw_in_state(state, RawLuaValue::Function(v)),
812 }),
813 RawLuaValue::UserData(v) => {
814 let host_value = v.host_value();
815 Value::UserData(AnyUserData {
816 root: lua.root_raw_in_state(state, RawLuaValue::UserData(v)),
817 host_value,
818 })
819 }
820 RawLuaValue::LightUserData(v) => Value::LightUserData(v),
821 RawLuaValue::Thread(v) => Value::Thread(Thread {
822 root: lua.root_raw_in_state(state, RawLuaValue::Thread(v)),
823 }),
824 })
825 }
826
827 fn to_raw_for_lua(&self, lua: &Lua, state: &LuaState) -> Result<RawLuaValue> {
828 match self {
829 Value::Nil => Ok(RawLuaValue::Nil),
830 Value::Boolean(v) => Ok(RawLuaValue::Bool(*v)),
831 Value::Integer(v) => Ok(RawLuaValue::Int(*v)),
832 Value::Number(v) => Ok(RawLuaValue::Float(*v)),
833 Value::String(v) => v.root.raw_for_lua(lua, state),
834 Value::Table(v) => v.root.raw_for_lua(lua, state),
835 Value::Function(v) => v.root.raw_for_lua(lua, state),
836 Value::UserData(v) => v.root.raw_for_lua(lua, state),
837 Value::LightUserData(v) => Ok(RawLuaValue::LightUserData(*v)),
838 Value::Thread(v) => v.root.raw_for_lua(lua, state),
839 }
840 }
841}
842
843#[derive(Debug, Clone)]
844pub struct Table {
845 root: RootedValue,
846}
847
848impl Table {
849 fn raw_table(&self) -> Result<GcRef<RawLuaTable>> {
850 match self.root.raw()? {
851 RawLuaValue::Table(table) => Ok(table),
852 other => Err(type_error_raw(&other, "table")),
853 }
854 }
855
856 pub fn get<K, V>(&self, key: K) -> Result<V>
857 where
858 K: IntoLua,
859 V: FromLua,
860 {
861 let lua = self.root.lua.clone();
862 let key = key.into_lua(&lua)?;
863 let value_raw = lua.with_state(|state| {
864 let key_raw = key.to_raw_for_lua(&lua, state)?;
865 let table_raw = self.root.raw_for_lua(&lua, state)?;
866 state.table_get_with_tm(&table_raw, &key_raw)
867 })?;
868 let value = Value::from_raw(&lua, value_raw)?;
869 V::from_lua(value, &lua)
870 }
871
872 pub fn set<K, V>(&self, key: K, value: V) -> Result<()>
873 where
874 K: IntoLua,
875 V: IntoLua,
876 {
877 let lua = self.root.lua.clone();
878 let key = key.into_lua(&lua)?;
879 let value = value.into_lua(&lua)?;
880 lua.with_state(|state| {
881 let key_raw = key.to_raw_for_lua(&lua, state)?;
882 let value_raw = value.to_raw_for_lua(&lua, state)?;
883 let table_raw = self.root.raw_for_lua(&lua, state)?;
884 state.table_set_with_tm(&table_raw, key_raw, value_raw)
885 })
886 }
887
888 pub fn len(&self) -> Result<u64> {
889 Ok(self.raw_table()?.getn())
890 }
891}
892
893#[derive(Debug, Clone)]
894pub struct Function {
895 root: RootedValue,
896}
897
898impl Function {
899 pub fn call<A, R>(&self, args: A) -> Result<R>
900 where
901 A: IntoLuaMulti,
902 R: FromLuaMulti,
903 {
904 let lua = self.root.lua.clone();
905 let args = args.into_lua_multi(&lua)?;
906 let result_raws = lua.with_state(|state| {
907 let arg_raws = args
908 .iter()
909 .map(|value| value.to_raw_for_lua(&lua, state))
910 .collect::<Result<Vec<_>>>()?;
911 let function_raw = self.root.raw_for_lua(&lua, state)?;
912 let saved_top = state.top_idx();
913 state.push(function_raw);
914 for arg in &arg_raws {
915 state.push(*arg);
916 }
917 match lua_vm::api::pcall_k(state, arg_raws.len() as i32, R::NRESULTS, 0, 0, None) {
918 Ok(_) => {
919 let nresults = if R::NRESULTS < 0 {
920 state.top_idx().0.saturating_sub(saved_top.0) as i32
921 } else {
922 R::NRESULTS
923 };
924 let mut results = Vec::with_capacity(nresults as usize);
925 for _ in 0..nresults {
926 results.push(state.pop());
927 }
928 results.reverse();
929 state.set_top_idx(saved_top);
930 Ok(results)
931 }
932 Err(err) => {
933 state.set_top_idx(saved_top);
934 Err(err)
935 }
936 }
937 })?;
938 let values = result_raws
939 .into_iter()
940 .map(|raw| Value::from_raw(&lua, raw))
941 .collect::<Result<Vec<_>>>()?;
942 R::from_lua_multi(values, &lua)
943 }
944}
945
946#[derive(Debug, Clone)]
947pub struct LuaString {
948 root: RootedValue,
949}
950
951impl LuaString {
952 fn raw_string(&self) -> Result<GcRef<RawLuaString>> {
953 match self.root.raw()? {
954 RawLuaValue::Str(string) => Ok(string),
955 other => Err(type_error_raw(&other, "string")),
956 }
957 }
958
959 pub fn as_bytes(&self) -> Result<Vec<u8>> {
960 Ok(self.raw_string()?.as_bytes().to_vec())
961 }
962
963 pub fn to_str(&self) -> Result<String> {
964 let bytes = self.as_bytes()?;
965 String::from_utf8(bytes)
966 .map_err(|_| LuaError::runtime(format_args!("string is not valid UTF-8")))
967 }
968}
969
970#[derive(Clone)]
971pub struct AnyUserData {
972 root: RootedValue,
973 host_value: Option<Rc<dyn Any>>,
974}
975
976impl fmt::Debug for AnyUserData {
977 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
978 f.debug_struct("AnyUserData")
979 .field("root", &self.root)
980 .field("has_host_value", &self.host_value.is_some())
981 .finish()
982 }
983}
984
985impl AnyUserData {
986 fn host_cell<T: 'static>(&self) -> Result<&UserDataCell<T>> {
987 let host = self
988 .host_value
989 .as_deref()
990 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust userdata payload")))?;
991 host.downcast_ref::<UserDataCell<T>>()
992 .ok_or_else(|| LuaError::runtime(format_args!("userdata type mismatch")))
993 }
994
995 pub fn borrow<T>(&self) -> Result<Ref<'_, T>>
996 where
997 T: 'static,
998 {
999 self.host_cell::<T>()?
1000 .value
1001 .try_borrow()
1002 .map_err(|_| LuaError::runtime(format_args!("userdata is already mutably borrowed")))
1003 }
1004
1005 pub fn borrow_mut<T>(&self) -> Result<RefMut<'_, T>>
1006 where
1007 T: 'static,
1008 {
1009 self.host_cell::<T>()?
1010 .value
1011 .try_borrow_mut()
1012 .map_err(|_| LuaError::runtime(format_args!("userdata is already borrowed")))
1013 }
1014
1015 pub fn with_borrow<T, R>(&self, f: impl FnOnce(&T) -> R) -> Result<R>
1016 where
1017 T: 'static,
1018 {
1019 let value = self.borrow::<T>()?;
1020 Ok(f(&value))
1021 }
1022
1023 pub fn with_borrow_mut<T, R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R>
1024 where
1025 T: 'static,
1026 {
1027 let mut value = self.borrow_mut::<T>()?;
1028 Ok(f(&mut value))
1029 }
1030}
1031
1032#[derive(Debug, Clone)]
1033pub struct Thread {
1034 root: RootedValue,
1035}
1036
1037#[derive(Debug, Clone, Default, PartialEq, Eq)]
1043pub struct Variadic<T>(Vec<T>);
1044
1045impl<T> Variadic<T> {
1046 pub const fn new() -> Self {
1047 Self(Vec::new())
1048 }
1049
1050 pub fn with_capacity(capacity: usize) -> Self {
1051 Self(Vec::with_capacity(capacity))
1052 }
1053
1054 pub fn into_vec(self) -> Vec<T> {
1055 self.0
1056 }
1057}
1058
1059impl<T> Deref for Variadic<T> {
1060 type Target = Vec<T>;
1061
1062 fn deref(&self) -> &Self::Target {
1063 &self.0
1064 }
1065}
1066
1067impl<T> DerefMut for Variadic<T> {
1068 fn deref_mut(&mut self) -> &mut Self::Target {
1069 &mut self.0
1070 }
1071}
1072
1073impl<T> From<Vec<T>> for Variadic<T> {
1074 fn from(value: Vec<T>) -> Self {
1075 Self(value)
1076 }
1077}
1078
1079impl<T> From<Variadic<T>> for Vec<T> {
1080 fn from(value: Variadic<T>) -> Self {
1081 value.0
1082 }
1083}
1084
1085impl<T> FromIterator<T> for Variadic<T> {
1086 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
1087 Self(Vec::from_iter(iter))
1088 }
1089}
1090
1091impl<T> IntoIterator for Variadic<T> {
1092 type Item = T;
1093 type IntoIter = std::vec::IntoIter<T>;
1094
1095 fn into_iter(self) -> Self::IntoIter {
1096 self.0.into_iter()
1097 }
1098}
1099
1100pub trait UserData: 'static {
1101 fn add_methods<M: UserDataMethods<Self>>(_methods: &mut M)
1102 where
1103 Self: Sized,
1104 {
1105 }
1106
1107 fn add_meta_methods<M: UserDataMethods<Self>>(_methods: &mut M)
1108 where
1109 Self: Sized,
1110 {
1111 }
1112}
1113
1114pub trait UserDataMethods<T: UserData> {
1115 fn add_method<A, R, F>(&mut self, name: &str, method: F)
1116 where
1117 A: FromLuaMulti + 'static,
1118 R: IntoLuaMulti + 'static,
1119 F: Fn(&Lua, &T, A) -> Result<R> + 'static;
1120
1121 fn add_method_mut<A, R, F>(&mut self, name: &str, method: F)
1122 where
1123 A: FromLuaMulti + 'static,
1124 R: IntoLuaMulti + 'static,
1125 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static;
1126
1127 fn add_meta_method<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
1128 where
1129 A: FromLuaMulti + 'static,
1130 R: IntoLuaMulti + 'static,
1131 F: Fn(&Lua, &T, A) -> Result<R> + 'static;
1132
1133 fn add_meta_method_mut<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
1134 where
1135 A: FromLuaMulti + 'static,
1136 R: IntoLuaMulti + 'static,
1137 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static;
1138
1139 fn add_field_method_get<R, F>(&mut self, name: &str, getter: F)
1143 where
1144 R: IntoLuaMulti + 'static,
1145 F: Fn(&Lua, &T) -> Result<R> + 'static;
1146
1147 fn add_field_method_set<A, F>(&mut self, name: &str, setter: F)
1150 where
1151 A: FromLuaMulti + 'static,
1152 F: Fn(&Lua, &mut T, A) -> Result<()> + 'static;
1153}
1154
1155#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1156pub enum MetaMethod {
1157 Index,
1158 NewIndex,
1159 Add,
1160 Sub,
1161 Mul,
1162 Div,
1163 Mod,
1164 Pow,
1165 Unm,
1166 Len,
1167 Eq,
1168 Lt,
1169 Le,
1170 Concat,
1171 Call,
1172 ToString,
1173 Pairs,
1174}
1175
1176impl MetaMethod {
1177 fn name(self) -> &'static str {
1178 match self {
1179 MetaMethod::Index => "__index",
1180 MetaMethod::NewIndex => "__newindex",
1181 MetaMethod::Add => "__add",
1182 MetaMethod::Sub => "__sub",
1183 MetaMethod::Mul => "__mul",
1184 MetaMethod::Div => "__div",
1185 MetaMethod::Mod => "__mod",
1186 MetaMethod::Pow => "__pow",
1187 MetaMethod::Unm => "__unm",
1188 MetaMethod::Len => "__len",
1189 MetaMethod::Eq => "__eq",
1190 MetaMethod::Lt => "__lt",
1191 MetaMethod::Le => "__le",
1192 MetaMethod::Concat => "__concat",
1193 MetaMethod::Call => "__call",
1194 MetaMethod::ToString => "__tostring",
1195 MetaMethod::Pairs => "__pairs",
1196 }
1197 }
1198}
1199
1200struct UserDataMethodRegistry<'lua, T: UserData> {
1201 lua: &'lua Lua,
1202 methods: Vec<(String, Function)>,
1203 meta_methods: Vec<(MetaMethod, Function)>,
1204 fields_get: Vec<(String, Function)>,
1205 fields_set: Vec<(String, Function)>,
1206 error: Option<LuaError>,
1207 _marker: std::marker::PhantomData<T>,
1208}
1209
1210impl<'lua, T: UserData> UserDataMethodRegistry<'lua, T> {
1211 fn new(lua: &'lua Lua) -> Self {
1212 Self {
1213 lua,
1214 methods: Vec::new(),
1215 meta_methods: Vec::new(),
1216 fields_get: Vec::new(),
1217 fields_set: Vec::new(),
1218 error: None,
1219 _marker: std::marker::PhantomData,
1220 }
1221 }
1222
1223 fn record(&mut self, result: Result<Function>, insert: impl FnOnce(&mut Self, Function)) {
1224 if self.error.is_some() {
1225 return;
1226 }
1227 match result {
1228 Ok(function) => insert(self, function),
1229 Err(err) => self.error = Some(err),
1230 }
1231 }
1232
1233 fn build_metatable(mut self) -> Result<GcRef<RawLuaTable>> {
1237 if let Some(err) = self.error.take() {
1238 return Err(err);
1239 }
1240
1241 let lua = self.lua;
1242
1243 let method_table = lua.create_table()?;
1244 for (name, function) in &self.methods {
1245 method_table.set(name.as_str(), function)?;
1246 }
1247
1248 let field_getters = lua.create_table()?;
1249 for (name, function) in &self.fields_get {
1250 field_getters.set(name.as_str(), function)?;
1251 }
1252 let field_setters = lua.create_table()?;
1253 for (name, function) in &self.fields_set {
1254 field_setters.set(name.as_str(), function)?;
1255 }
1256
1257 let metatable = lua.create_table()?;
1260 let mut raw_index: Option<Function> = None;
1261 let mut raw_newindex: Option<Function> = None;
1262 for (metamethod, function) in &self.meta_methods {
1263 match metamethod {
1264 MetaMethod::Index => raw_index = Some(function.clone()),
1265 MetaMethod::NewIndex => raw_newindex = Some(function.clone()),
1266 other => {
1267 metatable.set(other.name(), function)?;
1268 }
1269 }
1270 }
1271
1272 if !self.fields_get.is_empty() || raw_index.is_some() {
1276 let getters = field_getters.clone();
1277 let methods = method_table.clone();
1278 let raw = raw_index.clone();
1279 let index_fn = lua.create_function(move |_lua, (ud, key): (Value, Value)| {
1280 if let Value::Function(getter) = getters.get::<_, Value>(key.clone())? {
1281 return getter.call::<_, Value>(ud);
1282 }
1283 let method = methods.get::<_, Value>(key.clone())?;
1284 if !matches!(method, Value::Nil) {
1285 return Ok(method);
1286 }
1287 if let Some(raw) = &raw {
1288 return raw.call::<_, Value>((ud, key));
1289 }
1290 Ok(Value::Nil)
1291 })?;
1292 metatable.set(MetaMethod::Index.name(), &index_fn)?;
1293 } else {
1294 metatable.set(MetaMethod::Index.name(), &method_table)?;
1295 }
1296
1297 if !self.fields_set.is_empty() || raw_newindex.is_some() {
1299 let setters = field_setters.clone();
1300 let raw = raw_newindex.clone();
1301 let newindex_fn =
1302 lua.create_function(move |_lua, (ud, key, value): (Value, Value, Value)| {
1303 if let Value::Function(setter) = setters.get::<_, Value>(key.clone())? {
1304 return setter.call::<_, Value>((ud, value));
1305 }
1306 if let Some(raw) = &raw {
1307 return raw.call::<_, Value>((ud, key, value));
1308 }
1309 Err(LuaError::runtime(format_args!(
1310 "cannot assign to unknown or read-only userdata field"
1311 )))
1312 })?;
1313 metatable.set(MetaMethod::NewIndex.name(), &newindex_fn)?;
1314 }
1315
1316 self.lua.with_state(|state| {
1317 let metatable_raw = metatable.root.raw_for_lua(self.lua, state)?;
1318 let RawLuaValue::Table(metatable) = metatable_raw else {
1319 return Err(type_error_raw(&metatable_raw, "table"));
1320 };
1321 let _key = state.external_root_value(RawLuaValue::Table(metatable.clone()));
1325 Ok(metatable)
1326 })
1327 }
1328}
1329
1330impl<T: UserData> UserDataMethods<T> for UserDataMethodRegistry<'_, T> {
1331 fn add_method<A, R, F>(&mut self, name: &str, method: F)
1332 where
1333 A: FromLuaMulti + 'static,
1334 R: IntoLuaMulti + 'static,
1335 F: Fn(&Lua, &T, A) -> Result<R> + 'static,
1336 {
1337 let name = name.to_string();
1338 let result = self.lua.create_userdata_method(method);
1339 self.record(result, move |this, function| {
1340 this.methods.push((name, function));
1341 });
1342 }
1343
1344 fn add_method_mut<A, R, F>(&mut self, name: &str, method: F)
1345 where
1346 A: FromLuaMulti + 'static,
1347 R: IntoLuaMulti + 'static,
1348 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
1349 {
1350 let name = name.to_string();
1351 let result = self.lua.create_userdata_method_mut(method);
1352 self.record(result, move |this, function| {
1353 this.methods.push((name, function));
1354 });
1355 }
1356
1357 fn add_meta_method<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
1358 where
1359 A: FromLuaMulti + 'static,
1360 R: IntoLuaMulti + 'static,
1361 F: Fn(&Lua, &T, A) -> Result<R> + 'static,
1362 {
1363 let result = self.lua.create_userdata_method(method);
1364 self.record(result, move |this, function| {
1365 this.meta_methods.push((metamethod, function));
1366 });
1367 }
1368
1369 fn add_meta_method_mut<A, R, F>(&mut self, metamethod: MetaMethod, method: F)
1370 where
1371 A: FromLuaMulti + 'static,
1372 R: IntoLuaMulti + 'static,
1373 F: Fn(&Lua, &mut T, A) -> Result<R> + 'static,
1374 {
1375 let result = self.lua.create_userdata_method_mut(method);
1376 self.record(result, move |this, function| {
1377 this.meta_methods.push((metamethod, function));
1378 });
1379 }
1380
1381 fn add_field_method_get<R, F>(&mut self, name: &str, getter: F)
1382 where
1383 R: IntoLuaMulti + 'static,
1384 F: Fn(&Lua, &T) -> Result<R> + 'static,
1385 {
1386 let name = name.to_string();
1387 let result = self
1388 .lua
1389 .create_userdata_method(move |lua, this, ()| getter(lua, this));
1390 self.record(result, move |this, function| {
1391 this.fields_get.push((name, function));
1392 });
1393 }
1394
1395 fn add_field_method_set<A, F>(&mut self, name: &str, setter: F)
1396 where
1397 A: FromLuaMulti + 'static,
1398 F: Fn(&Lua, &mut T, A) -> Result<()> + 'static,
1399 {
1400 let name = name.to_string();
1401 let result = self
1402 .lua
1403 .create_userdata_method_mut(move |lua, this, arg: A| setter(lua, this, arg));
1404 self.record(result, move |this, function| {
1405 this.fields_set.push((name, function));
1406 });
1407 }
1408}
1409
1410pub trait IntoLua {
1411 fn into_lua(self, lua: &Lua) -> Result<Value>;
1412}
1413
1414pub trait FromLua: Sized {
1415 fn from_lua(value: Value, lua: &Lua) -> Result<Self>;
1416}
1417
1418pub trait IntoLuaMulti {
1419 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>>;
1420}
1421
1422pub trait FromLuaMulti: Sized {
1423 const NRESULTS: i32;
1424
1425 fn from_lua_multi(values: Vec<Value>, lua: &Lua) -> Result<Self>;
1426}
1427
1428impl IntoLua for Value {
1429 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1430 Ok(self)
1431 }
1432}
1433
1434impl IntoLua for &Value {
1435 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1436 Ok(self.clone())
1437 }
1438}
1439
1440impl FromLua for Value {
1441 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1442 Ok(value)
1443 }
1444}
1445
1446impl IntoLua for bool {
1447 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1448 Ok(Value::Boolean(self))
1449 }
1450}
1451
1452impl FromLua for bool {
1453 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1454 match value {
1455 Value::Boolean(v) => Ok(v),
1456 other => Err(type_error_value(&other, "boolean")),
1457 }
1458 }
1459}
1460
1461impl IntoLua for i64 {
1462 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1463 Ok(Value::Integer(self))
1464 }
1465}
1466
1467impl FromLua for i64 {
1468 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1469 match value {
1470 Value::Integer(v) => Ok(v),
1471 Value::Number(v) if v.fract() == 0.0 && v.is_finite() => Ok(v as i64),
1472 other => Err(type_error_value(&other, "integer")),
1473 }
1474 }
1475}
1476
1477impl IntoLua for i32 {
1478 fn into_lua(self, lua: &Lua) -> Result<Value> {
1479 i64::from(self).into_lua(lua)
1480 }
1481}
1482
1483impl FromLua for i32 {
1484 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1485 let v = i64::from_lua(value, lua)?;
1486 i32::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
1487 }
1488}
1489
1490impl IntoLua for usize {
1491 fn into_lua(self, lua: &Lua) -> Result<Value> {
1492 let v = i64::try_from(self)
1493 .map_err(|_| LuaError::runtime(format_args!("integer out of range")))?;
1494 v.into_lua(lua)
1495 }
1496}
1497
1498impl FromLua for usize {
1499 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1500 let v = i64::from_lua(value, lua)?;
1501 usize::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
1502 }
1503}
1504
1505impl IntoLua for u64 {
1506 fn into_lua(self, lua: &Lua) -> Result<Value> {
1507 let v = i64::try_from(self)
1508 .map_err(|_| LuaError::runtime(format_args!("integer out of range")))?;
1509 v.into_lua(lua)
1510 }
1511}
1512
1513impl FromLua for u64 {
1514 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1515 let v = i64::from_lua(value, lua)?;
1516 u64::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
1517 }
1518}
1519
1520impl IntoLua for u32 {
1521 fn into_lua(self, lua: &Lua) -> Result<Value> {
1522 u64::from(self).into_lua(lua)
1523 }
1524}
1525
1526impl FromLua for u32 {
1527 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1528 let v = u64::from_lua(value, lua)?;
1529 u32::try_from(v).map_err(|_| LuaError::runtime(format_args!("integer out of range")))
1530 }
1531}
1532
1533impl IntoLua for f64 {
1534 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1535 Ok(Value::Number(self))
1536 }
1537}
1538
1539impl FromLua for f64 {
1540 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1541 match value {
1542 Value::Integer(v) => Ok(v as f64),
1543 Value::Number(v) => Ok(v),
1544 other => Err(type_error_value(&other, "number")),
1545 }
1546 }
1547}
1548
1549impl IntoLua for &str {
1550 fn into_lua(self, lua: &Lua) -> Result<Value> {
1551 Ok(Value::String(lua.create_string(self.as_bytes())?))
1552 }
1553}
1554
1555impl IntoLua for String {
1556 fn into_lua(self, lua: &Lua) -> Result<Value> {
1557 Ok(Value::String(lua.create_string(self.into_bytes())?))
1558 }
1559}
1560
1561impl FromLua for String {
1562 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1563 match value {
1564 Value::String(s) => s.to_str(),
1565 other => Err(type_error_value(&other, "string")),
1566 }
1567 }
1568}
1569
1570impl IntoLua for &[u8] {
1571 fn into_lua(self, lua: &Lua) -> Result<Value> {
1572 Ok(Value::String(lua.create_string(self)?))
1573 }
1574}
1575
1576impl IntoLua for LuaString {
1577 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1578 Ok(Value::String(self))
1579 }
1580}
1581
1582impl IntoLua for &LuaString {
1583 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1584 Ok(Value::String(self.clone()))
1585 }
1586}
1587
1588impl FromLua for LuaString {
1589 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1590 match value {
1591 Value::String(v) => Ok(v),
1592 other => Err(type_error_value(&other, "string")),
1593 }
1594 }
1595}
1596
1597impl IntoLua for Table {
1598 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1599 Ok(Value::Table(self))
1600 }
1601}
1602
1603impl IntoLua for &Table {
1604 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1605 Ok(Value::Table(self.clone()))
1606 }
1607}
1608
1609impl FromLua for Table {
1610 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1611 match value {
1612 Value::Table(v) => Ok(v),
1613 other => Err(type_error_value(&other, "table")),
1614 }
1615 }
1616}
1617
1618impl IntoLua for Function {
1619 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1620 Ok(Value::Function(self))
1621 }
1622}
1623
1624impl IntoLua for &Function {
1625 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1626 Ok(Value::Function(self.clone()))
1627 }
1628}
1629
1630impl FromLua for Function {
1631 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1632 match value {
1633 Value::Function(v) => Ok(v),
1634 other => Err(type_error_value(&other, "function")),
1635 }
1636 }
1637}
1638
1639impl IntoLua for AnyUserData {
1640 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1641 Ok(Value::UserData(self))
1642 }
1643}
1644
1645impl IntoLua for &AnyUserData {
1646 fn into_lua(self, _lua: &Lua) -> Result<Value> {
1647 Ok(Value::UserData(self.clone()))
1648 }
1649}
1650
1651impl FromLua for AnyUserData {
1652 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
1653 match value {
1654 Value::UserData(v) => Ok(v),
1655 other => Err(type_error_value(&other, "userdata")),
1656 }
1657 }
1658}
1659
1660impl<T> IntoLua for T
1661where
1662 T: UserData,
1663{
1664 fn into_lua(self, lua: &Lua) -> Result<Value> {
1665 Ok(Value::UserData(lua.create_userdata(self)?))
1666 }
1667}
1668
1669impl<T> IntoLua for Option<T>
1670where
1671 T: IntoLua,
1672{
1673 fn into_lua(self, lua: &Lua) -> Result<Value> {
1674 match self {
1675 Some(value) => value.into_lua(lua),
1676 None => Ok(Value::Nil),
1677 }
1678 }
1679}
1680
1681impl<T> FromLua for Option<T>
1682where
1683 T: FromLua,
1684{
1685 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1686 match value {
1687 Value::Nil => Ok(None),
1688 other => T::from_lua(other, lua).map(Some),
1689 }
1690 }
1691}
1692
1693impl<T> IntoLua for Vec<T>
1694where
1695 T: IntoLua,
1696{
1697 fn into_lua(self, lua: &Lua) -> Result<Value> {
1698 let table = lua.create_table()?;
1699 for (idx, value) in self.into_iter().enumerate() {
1700 table.set((idx + 1) as i64, value)?;
1701 }
1702 Ok(Value::Table(table))
1703 }
1704}
1705
1706impl<T> FromLua for Vec<T>
1707where
1708 T: FromLua,
1709{
1710 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1711 let table = Table::from_lua(value, lua)?;
1712 let raw = table.raw_table()?;
1713 let len = raw.getn();
1714 let mut out = Vec::with_capacity(len as usize);
1715 for idx in 1..=len {
1716 let value = Value::from_raw(lua, raw.get_int(idx as i64))?;
1717 out.push(T::from_lua(value, lua)?);
1718 }
1719 Ok(out)
1720 }
1721}
1722
1723impl<K, V> IntoLua for HashMap<K, V>
1724where
1725 K: IntoLua,
1726 V: IntoLua,
1727{
1728 fn into_lua(self, lua: &Lua) -> Result<Value> {
1729 let table = lua.create_table()?;
1730 for (key, value) in self {
1731 table.set(key, value)?;
1732 }
1733 Ok(Value::Table(table))
1734 }
1735}
1736
1737impl<K, V> FromLua for HashMap<K, V>
1738where
1739 K: FromLua + Eq + Hash,
1740 V: FromLua,
1741{
1742 fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1743 let table = Table::from_lua(value, lua)?;
1744 let raw = table.raw_table()?;
1745 let mut out = HashMap::new();
1746 let mut result = Ok(());
1747 raw.for_each_entry(|key, value| {
1748 if result.is_err() {
1749 return;
1750 }
1751 result = (|| {
1752 let key = Value::from_raw(lua, *key)?;
1753 let value = Value::from_raw(lua, *value)?;
1754 out.insert(K::from_lua(key, lua)?, V::from_lua(value, lua)?);
1755 Ok(())
1756 })();
1757 });
1758 result?;
1759 Ok(out)
1760 }
1761}
1762
1763impl<T> IntoLuaMulti for Variadic<T>
1764where
1765 T: IntoLua,
1766{
1767 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1768 self.into_iter().map(|value| value.into_lua(lua)).collect()
1769 }
1770}
1771
1772impl<T> FromLuaMulti for Variadic<T>
1773where
1774 T: FromLua,
1775{
1776 const NRESULTS: i32 = -1;
1777
1778 fn from_lua_multi(values: Vec<Value>, lua: &Lua) -> Result<Self> {
1779 values
1780 .into_iter()
1781 .map(|value| T::from_lua(value, lua))
1782 .collect()
1783 }
1784}
1785
1786impl IntoLuaMulti for () {
1787 fn into_lua_multi(self, _lua: &Lua) -> Result<Vec<Value>> {
1788 Ok(Vec::new())
1789 }
1790}
1791
1792impl<T> IntoLuaMulti for T
1793where
1794 T: IntoLua,
1795{
1796 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1797 Ok(vec![self.into_lua(lua)?])
1798 }
1799}
1800
1801impl<A, B> IntoLuaMulti for (A, B)
1802where
1803 A: IntoLua,
1804 B: IntoLua,
1805{
1806 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1807 Ok(vec![self.0.into_lua(lua)?, self.1.into_lua(lua)?])
1808 }
1809}
1810
1811impl<A, T> IntoLuaMulti for (A, Variadic<T>)
1812where
1813 A: IntoLua,
1814 T: IntoLua,
1815{
1816 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1817 let mut values = vec![self.0.into_lua(lua)?];
1818 values.extend(self.1.into_lua_multi(lua)?);
1819 Ok(values)
1820 }
1821}
1822
1823impl<A, B, C> IntoLuaMulti for (A, B, C)
1824where
1825 A: IntoLua,
1826 B: IntoLua,
1827 C: IntoLua,
1828{
1829 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1830 Ok(vec![
1831 self.0.into_lua(lua)?,
1832 self.1.into_lua(lua)?,
1833 self.2.into_lua(lua)?,
1834 ])
1835 }
1836}
1837
1838impl<A, B, T> IntoLuaMulti for (A, B, Variadic<T>)
1839where
1840 A: IntoLua,
1841 B: IntoLua,
1842 T: IntoLua,
1843{
1844 fn into_lua_multi(self, lua: &Lua) -> Result<Vec<Value>> {
1845 let mut values = vec![self.0.into_lua(lua)?, self.1.into_lua(lua)?];
1846 values.extend(self.2.into_lua_multi(lua)?);
1847 Ok(values)
1848 }
1849}
1850
1851impl FromLuaMulti for () {
1852 const NRESULTS: i32 = 0;
1853
1854 fn from_lua_multi(_values: Vec<Value>, _lua: &Lua) -> Result<Self> {
1855 Ok(())
1856 }
1857}
1858
1859impl<T> FromLuaMulti for T
1860where
1861 T: FromLua,
1862{
1863 const NRESULTS: i32 = 1;
1864
1865 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
1866 let value = if values.is_empty() {
1867 Value::Nil
1868 } else {
1869 values.remove(0)
1870 };
1871 T::from_lua(value, lua)
1872 }
1873}
1874
1875impl<A, B> FromLuaMulti for (A, B)
1876where
1877 A: FromLua,
1878 B: FromLua,
1879{
1880 const NRESULTS: i32 = 2;
1881
1882 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
1883 let first = if values.is_empty() {
1884 Value::Nil
1885 } else {
1886 values.remove(0)
1887 };
1888 let second = if values.is_empty() {
1889 Value::Nil
1890 } else {
1891 values.remove(0)
1892 };
1893 Ok((A::from_lua(first, lua)?, B::from_lua(second, lua)?))
1894 }
1895}
1896
1897impl<A, T> FromLuaMulti for (A, Variadic<T>)
1898where
1899 A: FromLua,
1900 T: FromLua,
1901{
1902 const NRESULTS: i32 = -1;
1903
1904 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
1905 let first = if values.is_empty() {
1906 Value::Nil
1907 } else {
1908 values.remove(0)
1909 };
1910 Ok((
1911 A::from_lua(first, lua)?,
1912 Variadic::from_lua_multi(values, lua)?,
1913 ))
1914 }
1915}
1916
1917impl<A, B, C> FromLuaMulti for (A, B, C)
1918where
1919 A: FromLua,
1920 B: FromLua,
1921 C: FromLua,
1922{
1923 const NRESULTS: i32 = 3;
1924
1925 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
1926 let first = if values.is_empty() {
1927 Value::Nil
1928 } else {
1929 values.remove(0)
1930 };
1931 let second = if values.is_empty() {
1932 Value::Nil
1933 } else {
1934 values.remove(0)
1935 };
1936 let third = if values.is_empty() {
1937 Value::Nil
1938 } else {
1939 values.remove(0)
1940 };
1941 Ok((
1942 A::from_lua(first, lua)?,
1943 B::from_lua(second, lua)?,
1944 C::from_lua(third, lua)?,
1945 ))
1946 }
1947}
1948
1949impl<A, B, T> FromLuaMulti for (A, B, Variadic<T>)
1950where
1951 A: FromLua,
1952 B: FromLua,
1953 T: FromLua,
1954{
1955 const NRESULTS: i32 = -1;
1956
1957 fn from_lua_multi(mut values: Vec<Value>, lua: &Lua) -> Result<Self> {
1958 let first = if values.is_empty() {
1959 Value::Nil
1960 } else {
1961 values.remove(0)
1962 };
1963 let second = if values.is_empty() {
1964 Value::Nil
1965 } else {
1966 values.remove(0)
1967 };
1968 Ok((
1969 A::from_lua(first, lua)?,
1970 B::from_lua(second, lua)?,
1971 Variadic::from_lua_multi(values, lua)?,
1972 ))
1973 }
1974}
1975
1976fn rust_callback_trampoline(state: &mut LuaState) -> Result<usize> {
1977 let func_idx = state.current_call_info().func;
1978 let callback = match state.get_at(func_idx) {
1979 RawLuaValue::Function(RawLuaClosure::C(closure)) => {
1980 let Some(RawLuaValue::UserData(userdata)) = closure.upvalues.first() else {
1981 return Err(LuaError::runtime(format_args!(
1982 "missing Rust callback payload"
1983 )));
1984 };
1985 let host = userdata
1986 .host_value()
1987 .ok_or_else(|| LuaError::runtime(format_args!("missing Rust callback payload")))?;
1988 host.downcast::<RustCallbackCell>().map_err(|_| {
1989 LuaError::runtime(format_args!("Rust callback payload type mismatch"))
1990 })?
1991 }
1992 _ => {
1993 return Err(LuaError::runtime(format_args!(
1994 "Rust callback trampoline called without C closure"
1995 )));
1996 }
1997 };
1998 (callback.function)(state)
1999}
2000
2001fn with_heap_guard<R>(state: &LuaState, f: impl FnOnce() -> R) -> R {
2002 let _heap_guard = heap_guard(state);
2003 f()
2004}
2005
2006fn heap_guard(state: &LuaState) -> lua_gc::HeapGuard {
2007 let global = state.global();
2008 lua_gc::HeapGuard::push(&global.heap)
2009}
2010
2011fn callback_args(state: &mut LuaState, lua: &Lua) -> Result<Vec<Value>> {
2012 let func_idx = state.current_call_info().func;
2013 let nargs = state.top_idx().0.saturating_sub(func_idx.0 + 1);
2014 let mut args = Vec::with_capacity(nargs as usize);
2015 for i in 0..nargs {
2016 let raw = state.get_at(func_idx + 1 + i as i32);
2017 args.push(Value::from_raw_in_state(lua, state, raw)?);
2018 }
2019 Ok(args)
2020}
2021
2022fn callback_userdata_args(state: &mut LuaState, lua: &Lua) -> Result<(AnyUserData, Vec<Value>)> {
2023 let mut args = callback_args(state, lua)?;
2024 if args.is_empty() {
2025 return Err(LuaError::runtime(format_args!(
2026 "userdata method missing self argument"
2027 )));
2028 }
2029 let userdata = AnyUserData::from_lua(args.remove(0), lua)?;
2030 Ok((userdata, args))
2031}
2032
2033fn push_callback_returns(state: &mut LuaState, lua: &Lua, returns: Vec<Value>) -> Result<usize> {
2034 let mut count = 0usize;
2035 for value in returns {
2036 let raw = value.to_raw_for_lua(lua, state)?;
2037 state.push(raw);
2038 count += 1;
2039 }
2040 Ok(count)
2041}
2042
2043fn stale_handle_error() -> LuaError {
2044 LuaError::runtime(format_args!("stale Lua handle"))
2045}
2046
2047fn type_error_raw(value: &RawLuaValue, expected: &str) -> LuaError {
2048 LuaError::runtime(format_args!(
2049 "{} expected, got {}",
2050 expected,
2051 value.type_name()
2052 ))
2053}
2054
2055fn type_error_value(value: &Value, expected: &str) -> LuaError {
2056 let got = match value {
2057 Value::Nil => "nil",
2058 Value::Boolean(_) => "boolean",
2059 Value::Integer(_) | Value::Number(_) => "number",
2060 Value::String(_) => "string",
2061 Value::Table(_) => "table",
2062 Value::Function(_) => "function",
2063 Value::UserData(_) | Value::LightUserData(_) => "userdata",
2064 Value::Thread(_) => "thread",
2065 };
2066 LuaError::runtime(format_args!("{} expected, got {}", expected, got))
2067}
2068
2069pub struct LuaRuntime {
2071 state: LuaState,
2072}
2073
2074impl LuaRuntime {
2075 pub fn new() -> Result<Self> {
2081 Self::with_hooks(HostHooks::default())
2082 }
2083
2084 pub fn with_hooks(hooks: HostHooks) -> Result<Self> {
2086 let mut state = new_state().ok_or(LuaError::Memory)?;
2087 install_parser_hook(&mut state);
2088 hooks.install(&mut state);
2089 open_libs(&mut state)?;
2090 Ok(Self { state })
2091 }
2092
2093 pub fn state(&self) -> &LuaState {
2094 &self.state
2095 }
2096
2097 pub fn state_mut(&mut self) -> &mut LuaState {
2098 &mut self.state
2099 }
2100
2101 pub fn into_state(self) -> LuaState {
2102 self.state
2103 }
2104
2105 pub fn into_lua(self) -> Lua {
2106 Lua::from_initialized_state(self.state)
2107 }
2108
2109 pub fn exec(&mut self, source: &[u8], name: &[u8]) -> Result<()> {
2111 exec_state(&mut self.state, source, name)
2112 }
2113}
2114
2115fn exec_state(state: &mut LuaState, source: &[u8], name: &[u8]) -> Result<()> {
2116 let status = load_buffer(state, source, name)?;
2117 if status != 0 {
2118 let err = state.pop();
2119 return Err(LuaError::from_value(err));
2120 }
2121 lua_vm::api::pcall_k(state, 0, 0, 0, 0, None)?;
2122 Ok(())
2123}
2124
2125pub fn install_parser_hook(state: &mut LuaState) {
2126 state.global_mut().parser_hook = Some(parser_hook);
2127}
2128
2129fn parser_hook(
2130 state: &mut LuaState,
2131 source: &[u8],
2132 name: &[u8],
2133 firstchar: i32,
2134) -> Result<GcRef<LuaLClosure>> {
2135 let _heap_guard = heap_guard(state);
2136 let proto = lua_parse::parse(
2137 state,
2138 lua_parse::DynData::default(),
2139 source,
2140 name,
2141 firstchar,
2142 )?;
2143 let nupvals = proto.upvalues.len();
2144 let mut upvals = Vec::with_capacity(nupvals);
2145 for _ in 0..nupvals {
2146 upvals.push(std::cell::Cell::new(GcRef::new(UpVal::closed(
2147 RawLuaValue::Nil,
2148 ))));
2149 }
2150 Ok(GcRef::new(LuaLClosure {
2151 proto: GcRef::new(*proto),
2152 upvals,
2153 }))
2154}
2155
2156#[cfg(test)]
2157mod tests {
2158 use super::*;
2159 use std::cell::Cell;
2160
2161 fn external_root_count(lua: &Lua) -> usize {
2162 lua.with_state(|state| state.global().external_roots.len())
2163 }
2164
2165 struct Counter {
2166 value: i64,
2167 }
2168
2169 impl UserData for Counter {
2170 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
2171 methods.add_method("get", |_lua, this, ()| Ok(this.value));
2172 methods.add_method_mut("inc", |_lua, this, delta: i64| {
2173 this.value += delta;
2174 Ok(this.value)
2175 });
2176 }
2177 }
2178
2179 struct PropertyBag {
2180 value: i64,
2181 }
2182
2183 impl UserData for PropertyBag {
2184 fn add_meta_methods<M: UserDataMethods<Self>>(methods: &mut M) {
2185 methods.add_meta_method(MetaMethod::Index, |_lua, this, key: String| {
2186 if key == "value" {
2187 Ok(Value::Integer(this.value))
2188 } else {
2189 Ok(Value::Nil)
2190 }
2191 });
2192 methods.add_meta_method_mut(
2193 MetaMethod::NewIndex,
2194 |_lua, this, (key, value): (String, i64)| {
2195 if key != "value" {
2196 return Err(LuaError::runtime(format_args!("unknown property")));
2197 }
2198 this.value = value;
2199 Ok(())
2200 },
2201 );
2202 }
2203 }
2204
2205 #[test]
2206 fn rooted_table_clone_and_drop_manage_root_slots() {
2207 let lua = Lua::new();
2208 assert_eq!(external_root_count(&lua), 0);
2209
2210 let table = lua.create_table().expect("table should allocate");
2211 assert_eq!(external_root_count(&lua), 1);
2212
2213 let cloned = table.clone();
2214 assert_eq!(external_root_count(&lua), 2);
2215
2216 drop(table);
2217 assert_eq!(external_root_count(&lua), 1);
2218
2219 cloned.set("answer", 42_i64).expect("set should succeed");
2220 lua.gc_collect();
2221 assert_eq!(
2222 cloned.get::<_, i64>("answer").expect("get should succeed"),
2223 42
2224 );
2225
2226 drop(cloned);
2227 assert_eq!(external_root_count(&lua), 0);
2228 }
2229
2230 #[test]
2231 fn table_values_survive_forced_collection_between_operations() {
2232 let lua = Lua::new();
2233 let table = lua.create_table().expect("table should allocate");
2234
2235 lua.gc_collect();
2236 table.set("k", "v").expect("set should succeed");
2237 table.set(1_i64, "array").expect("array set should succeed");
2238 lua.gc_collect();
2239
2240 let value: String = table.get("k").expect("get should succeed");
2241 assert_eq!(value, "v");
2242 assert_eq!(table.len().expect("len should succeed"), 1);
2243 }
2244
2245 #[test]
2246 fn chunk_exec_eval_and_function_call_use_rooted_handles() {
2247 let lua = Lua::new();
2248 lua.load("function add(a, b) return a + b end")
2249 .set_name("test")
2250 .exec()
2251 .expect("chunk should execute");
2252
2253 let globals = lua.globals();
2254 let add: Function = globals.get("add").expect("function should exist");
2255 let result: i64 = add.call((20_i64, 22_i64)).expect("call should work");
2256 assert_eq!(result, 42);
2257
2258 let eval_result: i64 = lua
2259 .load("return add(1, 2)")
2260 .eval()
2261 .expect("eval should work");
2262 assert_eq!(eval_result, 3);
2263 }
2264
2265 #[test]
2266 fn rust_callback_captures_state_and_reenters_lua() {
2267 let lua = Lua::new();
2268 lua.load("function twice(v) return v * 2 end")
2269 .exec()
2270 .expect("chunk should execute");
2271
2272 let globals = lua.globals();
2273 let twice: Function = globals.get("twice").expect("function should exist");
2274 let calls = Rc::new(Cell::new(0));
2275 let calls_for_callback = calls.clone();
2276
2277 let callback = lua
2278 .create_function(move |_lua, value: i64| {
2279 calls_for_callback.set(calls_for_callback.get() + 1);
2280 let doubled: i64 = twice.call(value)?;
2281 Ok(doubled + 1)
2282 })
2283 .expect("callback should create");
2284 globals
2285 .set("from_rust", callback)
2286 .expect("callback should register");
2287
2288 let result: i64 = lua
2289 .load("return from_rust(20)")
2290 .eval()
2291 .expect("callback should run");
2292 assert_eq!(result, 41);
2293 assert_eq!(calls.get(), 1);
2294 }
2295
2296 #[test]
2297 fn rust_callback_accepts_and_returns_collectable_values() {
2298 let lua = Lua::new();
2299 let globals = lua.globals();
2300 let callback = lua
2301 .create_function(|lua, name: String| {
2302 let table = lua.create_table()?;
2303 table.set("name", name)?;
2304 Ok(table)
2305 })
2306 .expect("callback should create");
2307 globals
2308 .set("make_record", callback)
2309 .expect("callback should register");
2310
2311 let result: String = lua
2312 .load("return make_record('lua-rs').name")
2313 .eval()
2314 .expect("callback should return table");
2315 assert_eq!(result, "lua-rs");
2316 }
2317
2318 #[test]
2319 fn rust_callback_mut_tracks_state() {
2320 let lua = Lua::new();
2321 let globals = lua.globals();
2322 let mut next = 0_i64;
2323 let callback = lua
2324 .create_function_mut(move |_lua, delta: i64| {
2325 next += delta;
2326 Ok(next)
2327 })
2328 .expect("callback should create");
2329 globals
2330 .set("next", callback)
2331 .expect("callback should register");
2332
2333 let result: (i64, i64) = lua
2334 .load("return next(2), next(5)")
2335 .eval()
2336 .expect("callback should run");
2337 assert_eq!(result, (2, 7));
2338 }
2339
2340 #[test]
2341 fn dropped_rust_callback_releases_captured_handles_after_gc() {
2342 let lua = Lua::new();
2343 let table = lua.create_table().expect("table should allocate");
2344 table.set("value", 42_i64).expect("set should succeed");
2345 assert_eq!(external_root_count(&lua), 1);
2346
2347 let callback = {
2348 let captured = table.clone();
2349 lua.create_function(move |_lua, ()| captured.get::<_, i64>("value"))
2350 .expect("callback should create")
2351 };
2352 assert_eq!(external_root_count(&lua), 3);
2353
2354 drop(callback);
2355 lua.gc_collect();
2356 assert_eq!(external_root_count(&lua), 1);
2357 assert_eq!(table.get::<_, i64>("value").expect("table should live"), 42);
2358 }
2359
2360 #[test]
2361 fn metatable_is_built_once_per_type() {
2362 use std::sync::atomic::{AtomicUsize, Ordering};
2363 static BUILDS: AtomicUsize = AtomicUsize::new(0);
2364
2365 struct Widget {
2366 n: i64,
2367 }
2368 impl UserData for Widget {
2369 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
2370 BUILDS.fetch_add(1, Ordering::SeqCst);
2371 methods.add_method("n", |_lua, this, ()| Ok(this.n));
2372 }
2373 }
2374
2375 let lua = Lua::new();
2376 let a = lua.create_userdata(Widget { n: 1 }).expect("first");
2377 let b = lua.create_userdata(Widget { n: 2 }).expect("second");
2378 let c = lua.create_userdata(Widget { n: 3 }).expect("third");
2379
2380 assert_eq!(BUILDS.load(Ordering::SeqCst), 1);
2382
2383 let globals = lua.globals();
2385 globals.set("a", &a).unwrap();
2386 globals.set("b", &b).unwrap();
2387 globals.set("c", &c).unwrap();
2388 let sum: i64 = lua.load("return a:n() + b:n() + c:n()").eval().unwrap();
2389 assert_eq!(sum, 6);
2390 }
2391
2392 #[test]
2393 fn fields_and_methods_coexist() {
2394 struct Vec2 {
2395 x: f64,
2396 y: f64,
2397 }
2398 impl UserData for Vec2 {
2399 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
2400 m.add_field_method_get("x", |_, this| Ok(this.x));
2401 m.add_field_method_get("y", |_, this| Ok(this.y));
2402 m.add_field_method_set("x", |_, this, v: f64| {
2403 this.x = v;
2404 Ok(())
2405 });
2406 m.add_field_method_set("y", |_, this, v: f64| {
2407 this.y = v;
2408 Ok(())
2409 });
2410 m.add_method("length", |_, this, ()| {
2411 Ok((this.x * this.x + this.y * this.y).sqrt())
2412 });
2413 m.add_method_mut("scale", |_, this, k: f64| {
2414 this.x *= k;
2415 this.y *= k;
2416 Ok(())
2417 });
2418 }
2419 }
2420
2421 let lua = Lua::new();
2422 let v = lua.create_userdata(Vec2 { x: 3.0, y: 4.0 }).unwrap();
2423 lua.globals().set("v", &v).unwrap();
2424
2425 assert_eq!(lua.load("return v:length()").eval::<f64>().unwrap(), 5.0);
2427 assert_eq!(lua.load("return v.x + v.y").eval::<f64>().unwrap(), 7.0);
2428
2429 lua.load("v.x = 6").exec().unwrap();
2431 assert_eq!(lua.load("return v.x").eval::<f64>().unwrap(), 6.0);
2432
2433 lua.load("v:scale(2)").exec().unwrap();
2435 assert_eq!(lua.load("return v.x").eval::<f64>().unwrap(), 12.0);
2436 assert_eq!(lua.load("return v.y").eval::<f64>().unwrap(), 8.0);
2437
2438 assert!(lua.load("v.z = 1").exec().is_err());
2440 }
2441
2442 #[test]
2443 fn userdata_methods_dispatch_and_track_borrows() {
2444 let lua = Lua::new();
2445 let globals = lua.globals();
2446 let counter = lua
2447 .create_userdata(Counter { value: 1 })
2448 .expect("userdata should create");
2449 globals
2450 .set("counter", &counter)
2451 .expect("userdata should register");
2452
2453 let result: i64 = lua
2454 .load("counter:inc(5); return counter:get()")
2455 .eval()
2456 .expect("methods should dispatch");
2457 assert_eq!(result, 6);
2458 assert_eq!(
2459 counter
2460 .with_borrow::<Counter, _>(|counter| counter.value)
2461 .expect("borrow should work"),
2462 6
2463 );
2464
2465 {
2466 let borrowed = counter
2467 .borrow::<Counter>()
2468 .expect("borrow guard should work");
2469 assert_eq!(borrowed.value, 6);
2470 }
2471
2472 {
2473 let mut borrowed = counter
2474 .borrow_mut::<Counter>()
2475 .expect("mutable borrow guard should work");
2476 borrowed.value = 9;
2477 }
2478
2479 assert_eq!(
2480 lua.load("return counter:get()")
2481 .eval::<i64>()
2482 .expect("method should see guard mutation"),
2483 9
2484 );
2485 }
2486
2487 #[test]
2488 fn userdata_payload_survives_gc_while_lua_holds_userdata() {
2489 let lua = Lua::new();
2490 let globals = lua.globals();
2491 let counter = lua
2492 .create_userdata(Counter { value: 10 })
2493 .expect("userdata should create");
2494 globals
2495 .set("counter", counter)
2496 .expect("userdata should register");
2497
2498 lua.gc_collect();
2499 let result: i64 = lua
2500 .load("counter:inc(2); collectgarbage('collect'); return counter:get()")
2501 .eval()
2502 .expect("userdata should survive collection");
2503 assert_eq!(result, 12);
2504 }
2505
2506 #[test]
2507 fn userdata_runtime_borrow_conflict_returns_lua_error() {
2508 let lua = Lua::new();
2509 let globals = lua.globals();
2510 let counter = lua
2511 .create_userdata(Counter { value: 1 })
2512 .expect("userdata should create");
2513 globals
2514 .set("counter", &counter)
2515 .expect("userdata should register");
2516
2517 let failed = counter
2518 .with_borrow::<Counter, _>(|_| lua.load("return counter:inc(1)").eval::<i64>().is_err())
2519 .expect("outer borrow should succeed");
2520 assert!(
2521 failed,
2522 "mutable method should fail while immutable borrow is held"
2523 );
2524 assert_eq!(
2525 counter
2526 .with_borrow::<Counter, _>(|counter| counter.value)
2527 .expect("borrow should work"),
2528 1
2529 );
2530 }
2531
2532 #[test]
2533 fn userdata_index_and_newindex_metamethods_dispatch() {
2534 let lua = Lua::new();
2535 let globals = lua.globals();
2536 let bag = lua
2537 .create_userdata(PropertyBag { value: 7 })
2538 .expect("userdata should create");
2539 globals.set("bag", &bag).expect("userdata should register");
2540
2541 let result: i64 = lua
2542 .load("bag.value = 42; return bag.value")
2543 .eval()
2544 .expect("metamethods should dispatch");
2545 assert_eq!(result, 42);
2546 assert_eq!(
2547 bag.with_borrow::<PropertyBag, _>(|bag| bag.value)
2548 .expect("borrow should work"),
2549 42
2550 );
2551 }
2552
2553 #[test]
2554 fn userdata_values_convert_directly_with_into_lua() {
2555 let lua = Lua::new();
2556 let globals = lua.globals();
2557 globals
2558 .set("counter", Counter { value: 3 })
2559 .expect("userdata should convert through IntoLua");
2560
2561 let result: i64 = lua
2562 .load("counter:inc(4); return counter:get()")
2563 .eval()
2564 .expect("converted userdata should dispatch methods");
2565 assert_eq!(result, 7);
2566 }
2567
2568 #[test]
2569 fn variadic_args_and_returns_convert_all_values() {
2570 let lua = Lua::new();
2571 let globals = lua.globals();
2572
2573 let sum = lua
2574 .create_function(|_lua, values: Variadic<i64>| Ok(values.iter().sum::<i64>()))
2575 .expect("variadic callback should create");
2576 globals.set("sum", sum).expect("callback should register");
2577 let result: i64 = lua
2578 .load("return sum(3, 2, 5)")
2579 .eval()
2580 .expect("variadic callback should run");
2581 assert_eq!(result, 10);
2582
2583 let echo = lua
2584 .create_function(|_lua, values: Variadic<Value>| Ok(values))
2585 .expect("variadic return callback should create");
2586 globals.set("echo", echo).expect("callback should register");
2587 let result: (i64, i64, i64) = lua
2588 .load("return echo(1, 2, 3)")
2589 .eval()
2590 .expect("variadic returns should stay separate");
2591 assert_eq!(result, (1, 2, 3));
2592
2593 let values: Variadic<i64> = lua
2594 .load("return 4, 5, 6")
2595 .eval()
2596 .expect("variadic eval should collect all returns");
2597 assert_eq!(values.into_vec(), vec![4, 5, 6]);
2598 }
2599
2600 #[test]
2601 fn vectors_maps_and_triple_returns_convert_through_tables() {
2602 let lua = Lua::new();
2603 let globals = lua.globals();
2604
2605 globals
2606 .set("list", vec![1_i64, 2, 3])
2607 .expect("vector should convert to table");
2608 let second: i64 = lua
2609 .load("return list[2]")
2610 .eval()
2611 .expect("table should be readable from Lua");
2612 assert_eq!(second, 2);
2613
2614 let list: Vec<i64> = lua
2615 .load("return {4, 5, 6}")
2616 .eval()
2617 .expect("table should convert to vector");
2618 assert_eq!(list, vec![4, 5, 6]);
2619
2620 let mut map = HashMap::new();
2621 map.insert("left".to_string(), 10_i64);
2622 map.insert("right".to_string(), 20_i64);
2623 globals
2624 .set("map", map)
2625 .expect("map should convert to table");
2626 let sum: i64 = lua
2627 .load("return map.left + map.right")
2628 .eval()
2629 .expect("map table should be readable from Lua");
2630 assert_eq!(sum, 30);
2631
2632 let map: HashMap<String, i64> = lua
2633 .load("return {alpha = 3, beta = 9}")
2634 .eval()
2635 .expect("table should convert to map");
2636 assert_eq!(map.get("alpha"), Some(&3));
2637 assert_eq!(map.get("beta"), Some(&9));
2638
2639 let triple: (i64, i64, i64) = lua
2640 .load("return 1, 2, 3")
2641 .eval()
2642 .expect("triple returns should convert");
2643 assert_eq!(triple, (1, 2, 3));
2644 }
2645}