1use std::any::TypeId;
26use std::cell::RefCell;
27use std::marker::PhantomData;
28
29use crate::callback::{create_callback_function, BoxedCallback};
30use crate::error::{Error, Result};
31use crate::state::{Lua, LuaRef};
32use crate::sync::{MaybeSend, MaybeSync, NotSync, XRc, NOT_SYNC};
33use crate::sys::*;
34use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
35use crate::value::Value;
36
37pub trait UserData: Sized {
42 fn add_fields<F: UserDataFields<Self>>(_fields: &mut F) {}
44
45 fn add_methods<M: UserDataMethods<Self>>(_methods: &mut M) {}
47}
48
49pub trait UserDataMethods<T> {
53 fn add_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
55 where
56 M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
57 A: FromLuaMulti,
58 R: IntoLuaMulti;
59
60 fn add_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
62 where
63 M: Fn(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
64 A: FromLuaMulti,
65 R: IntoLuaMulti;
66
67 fn add_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
69 where
70 F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
71 A: FromLuaMulti,
72 R: IntoLuaMulti;
73
74 fn add_meta_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
77 where
78 M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
79 A: FromLuaMulti,
80 R: IntoLuaMulti;
81
82 fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
84 where
85 M: Fn(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
86 A: FromLuaMulti,
87 R: IntoLuaMulti;
88}
89
90pub trait UserDataFields<T> {
95 fn add_field<V>(&mut self, name: impl Into<String>, value: V)
97 where
98 V: IntoLua + Clone + MaybeSend + 'static;
99
100 fn add_field_method_get<M, R>(&mut self, name: impl Into<String>, method: M)
102 where
103 M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
104 R: IntoLua;
105
106 fn add_field_method_set<M, A>(&mut self, name: impl Into<String>, method: M)
108 where
109 M: Fn(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
110 A: FromLua;
111
112 fn add_field_function_get<F, R>(&mut self, name: impl Into<String>, function: F)
114 where
115 F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
116 R: IntoLua;
117
118 fn add_field_function_set<F, A>(&mut self, name: impl Into<String>, function: F)
121 where
122 F: Fn(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
123 A: FromLua;
124}
125
126#[derive(Clone)]
133pub struct AnyUserData {
134 pub(crate) reference: XRc<LuaRef>,
135 pub(crate) _not_sync: NotSync,
136}
137
138impl AnyUserData {
139 pub(crate) fn from_ref(reference: LuaRef) -> AnyUserData {
140 AnyUserData {
141 reference: XRc::new(reference),
142 _not_sync: NOT_SYNC,
143 }
144 }
145
146 pub(crate) unsafe fn push_to_stack(&self) {
147 self.reference.push();
148 }
149
150 pub fn lua(&self) -> Lua {
152 self.reference.lua()
153 }
154
155 pub fn to_pointer(&self) -> *const c_void {
158 let state = self.reference.state();
159 unsafe {
160 self.reference.push();
161 let p = lua_topointer(state, -1);
162 lua_pop(state, 1);
163 p
164 }
165 }
166
167 pub fn equals(&self, other: &AnyUserData) -> Result<bool> {
170 let lua = self.lua();
171 let state = lua.state();
172 unsafe {
173 self.reference.push();
174 other.reference.push();
175 let eq = lua_equal(state, -2, -1);
176 lua_pop(state, 2);
177 Ok(eq != 0)
178 }
179 }
180
181 fn cell<T: 'static>(&self) -> Result<&UserDataCell<T>> {
185 let state = self.reference.state();
186 unsafe {
187 self.reference.push();
188 let ptr = lua_touserdata(state, -1);
189 lua_pop(state, 1);
190 if ptr.is_null() {
191 return Err(Error::UserDataTypeMismatch);
192 }
193 let header = &*(ptr as *const UserDataHeader);
195 if header.type_id != TypeId::of::<T>() {
196 return Err(Error::UserDataTypeMismatch);
197 }
198 Ok(&*(ptr as *const UserDataCell<T>))
199 }
200 }
201
202 pub fn is<T: 'static>(&self) -> bool {
205 match self.cell::<T>() {
206 Ok(cell) => cell.cell.borrow().is_some(),
207 Err(_) => false,
208 }
209 }
210
211 pub fn type_id(&self) -> Option<TypeId> {
215 let state = self.reference.state();
216 unsafe {
217 self.reference.push();
218 let ptr = lua_touserdata(state, -1);
219 lua_pop(state, 1);
220 if ptr.is_null() {
221 return None;
222 }
223 let header = &*(ptr as *const UserDataHeader);
226 Some(header.type_id)
227 }
228 }
229
230 pub fn borrow<T: 'static>(&self) -> Result<UserDataRef<'_, T>> {
235 let cell = self.cell::<T>()?;
236 let guard = cell
237 .cell
238 .try_borrow()
239 .map_err(|_| Error::UserDataBorrowError)?;
240 if guard.is_none() {
241 return Err(Error::UserDataDestructed);
242 }
243 Ok(UserDataRef {
244 guard,
245 _marker: PhantomData,
246 })
247 }
248
249 pub fn borrow_mut<T: 'static>(&self) -> Result<UserDataRefMut<'_, T>> {
252 let cell = self.cell::<T>()?;
253 let guard = cell
254 .cell
255 .try_borrow_mut()
256 .map_err(|_| Error::UserDataBorrowMutError)?;
257 if guard.is_none() {
258 return Err(Error::UserDataDestructed);
259 }
260 Ok(UserDataRefMut {
261 guard,
262 _marker: PhantomData,
263 })
264 }
265
266 pub fn take<T: 'static>(&self) -> Result<T> {
271 let cell = self.cell::<T>()?;
272 let mut guard = cell
273 .cell
274 .try_borrow_mut()
275 .map_err(|_| Error::UserDataBorrowMutError)?;
276 guard.take().ok_or(Error::UserDataDestructed)
277 }
278}
279
280pub struct UserDataRef<'a, T> {
283 guard: std::cell::Ref<'a, Option<T>>,
284 _marker: PhantomData<T>,
285}
286
287impl<T> std::ops::Deref for UserDataRef<'_, T> {
288 type Target = T;
289 fn deref(&self) -> &T {
290 self.guard.as_ref().expect("userdata destructed")
292 }
293}
294
295impl<T: std::fmt::Debug> std::fmt::Debug for UserDataRef<'_, T> {
296 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
297 std::fmt::Debug::fmt(&**self, f)
298 }
299}
300
301impl<T: std::fmt::Display> std::fmt::Display for UserDataRef<'_, T> {
302 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
303 std::fmt::Display::fmt(&**self, f)
304 }
305}
306
307pub struct UserDataRefMut<'a, T> {
310 guard: std::cell::RefMut<'a, Option<T>>,
311 _marker: PhantomData<T>,
312}
313
314impl<T> std::ops::Deref for UserDataRefMut<'_, T> {
315 type Target = T;
316 fn deref(&self) -> &T {
317 self.guard.as_ref().expect("userdata destructed")
318 }
319}
320
321impl<T> std::ops::DerefMut for UserDataRefMut<'_, T> {
322 fn deref_mut(&mut self) -> &mut T {
323 self.guard.as_mut().expect("userdata destructed")
324 }
325}
326
327impl<T: std::fmt::Debug> std::fmt::Debug for UserDataRefMut<'_, T> {
328 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
329 std::fmt::Debug::fmt(&**self, f)
330 }
331}
332
333impl<T: std::fmt::Display> std::fmt::Display for UserDataRefMut<'_, T> {
334 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
335 std::fmt::Display::fmt(&**self, f)
336 }
337}
338
339impl std::fmt::Debug for AnyUserData {
340 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
341 write!(f, "UserData")
342 }
343}
344
345impl PartialEq for AnyUserData {
346 fn eq(&self, other: &Self) -> bool {
347 self.to_pointer() == other.to_pointer()
349 }
350}
351
352impl<T: UserData + crate::sync::MaybeSend + crate::sync::MaybeSync + 'static> IntoLua for T {
359 fn into_lua(self, lua: &Lua) -> Result<Value> {
360 Ok(Value::UserData(lua.create_userdata(self)?))
361 }
362}
363
364impl IntoLua for AnyUserData {
365 fn into_lua(self, _lua: &Lua) -> Result<Value> {
366 Ok(Value::UserData(self))
367 }
368}
369
370impl IntoLua for &AnyUserData {
371 fn into_lua(self, _lua: &Lua) -> Result<Value> {
372 Ok(Value::UserData(self.clone()))
373 }
374}
375
376impl FromLua for AnyUserData {
377 fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
378 match value {
379 Value::UserData(ud) => Ok(ud),
380 other => Err(Error::FromLuaConversionError {
381 from: other.type_name(),
382 to: "AnyUserData".to_string(),
383 message: None,
384 }),
385 }
386 }
387}
388
389#[repr(C)]
398struct UserDataHeader {
399 type_id: TypeId,
400}
401
402#[repr(C)]
404struct UserDataCell<T> {
405 type_id: TypeId,
406 cell: RefCell<Option<T>>,
407}
408
409fn recover_cell<'a, T: 'static>(lua: &Lua, value: &Value) -> Result<&'a UserDataCell<T>> {
411 match value {
412 Value::UserData(ud) => {
413 let state = lua.state();
414 unsafe {
415 ud.reference.push();
416 let ptr = lua_touserdata(state, -1);
417 lua_pop(state, 1);
418 if ptr.is_null() {
419 return Err(Error::UserDataTypeMismatch);
420 }
421 let header = &*(ptr as *const UserDataHeader);
422 if header.type_id != TypeId::of::<T>() {
423 return Err(Error::UserDataTypeMismatch);
424 }
425 Ok(&*(ptr as *const UserDataCell<T>))
426 }
427 }
428 _ => Err(Error::UserDataTypeMismatch),
429 }
430}
431
432struct Registered {
439 name: String,
440 is_meta: bool,
441 callback: BoxedCallback,
442}
443
444struct FieldEntry {
446 name: String,
447 is_get: bool,
449 callback: BoxedCallback,
450}
451
452struct Collector<T> {
455 methods: Vec<Registered>,
456 fields: Vec<FieldEntry>,
457 _phantom: PhantomData<T>,
458}
459
460impl<T> Collector<T> {
461 fn new() -> Self {
462 Collector {
463 methods: Vec::new(),
464 fields: Vec::new(),
465 _phantom: PhantomData,
466 }
467 }
468}
469
470impl<T: 'static> UserDataMethods<T> for Collector<T> {
471 fn add_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
472 where
473 M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
474 A: FromLuaMulti,
475 R: IntoLuaMulti,
476 {
477 let callback: BoxedCallback = Box::new(move |lua, mut args| {
478 let this = args.pop_front().unwrap_or(Value::Nil);
479 let cell = recover_cell::<T>(lua, &this)?;
480 let a = A::from_lua_multi(args, lua)?;
481 let borrowed = cell
482 .cell
483 .try_borrow()
484 .map_err(|_| Error::UserDataBorrowError)?;
485 let data = borrowed.as_ref().ok_or(Error::UserDataDestructed)?;
486 let r = method(lua, data, a)?;
487 r.into_lua_multi(lua)
488 });
489 self.methods.push(Registered {
490 name: name.into(),
491 is_meta: false,
492 callback,
493 });
494 }
495
496 fn add_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
497 where
498 M: Fn(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
499 A: FromLuaMulti,
500 R: IntoLuaMulti,
501 {
502 let callback: BoxedCallback = Box::new(move |lua, mut args| {
503 let this = args.pop_front().unwrap_or(Value::Nil);
504 let cell = recover_cell::<T>(lua, &this)?;
505 let a = A::from_lua_multi(args, lua)?;
506 let mut borrowed = cell
507 .cell
508 .try_borrow_mut()
509 .map_err(|_| Error::UserDataBorrowMutError)?;
510 let data = borrowed.as_mut().ok_or(Error::UserDataDestructed)?;
511 let r = method(lua, data, a)?;
512 r.into_lua_multi(lua)
513 });
514 self.methods.push(Registered {
515 name: name.into(),
516 is_meta: false,
517 callback,
518 });
519 }
520
521 fn add_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
522 where
523 F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
524 A: FromLuaMulti,
525 R: IntoLuaMulti,
526 {
527 let callback: BoxedCallback = Box::new(move |lua, args| {
528 let a = A::from_lua_multi(args, lua)?;
529 let r = function(lua, a)?;
530 r.into_lua_multi(lua)
531 });
532 self.methods.push(Registered {
533 name: name.into(),
534 is_meta: false,
535 callback,
536 });
537 }
538
539 fn add_meta_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
540 where
541 M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
542 A: FromLuaMulti,
543 R: IntoLuaMulti,
544 {
545 let callback: BoxedCallback = Box::new(move |lua, mut args| {
546 let this = args.pop_front().unwrap_or(Value::Nil);
547 let cell = recover_cell::<T>(lua, &this)?;
548 let a = A::from_lua_multi(args, lua)?;
549 let borrowed = cell
550 .cell
551 .try_borrow()
552 .map_err(|_| Error::UserDataBorrowError)?;
553 let data = borrowed.as_ref().ok_or(Error::UserDataDestructed)?;
554 let r = method(lua, data, a)?;
555 r.into_lua_multi(lua)
556 });
557 self.methods.push(Registered {
558 name: name.into(),
559 is_meta: true,
560 callback,
561 });
562 }
563
564 fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
565 where
566 M: Fn(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
567 A: FromLuaMulti,
568 R: IntoLuaMulti,
569 {
570 let callback: BoxedCallback = Box::new(move |lua, mut args| {
571 let this = args.pop_front().unwrap_or(Value::Nil);
572 let cell = recover_cell::<T>(lua, &this)?;
573 let a = A::from_lua_multi(args, lua)?;
574 let mut borrowed = cell
575 .cell
576 .try_borrow_mut()
577 .map_err(|_| Error::UserDataBorrowMutError)?;
578 let data = borrowed.as_mut().ok_or(Error::UserDataDestructed)?;
579 let r = method(lua, data, a)?;
580 r.into_lua_multi(lua)
581 });
582 self.methods.push(Registered {
583 name: name.into(),
584 is_meta: true,
585 callback,
586 });
587 }
588}
589
590impl<T: 'static> UserDataFields<T> for Collector<T> {
591 fn add_field<V>(&mut self, name: impl Into<String>, value: V)
592 where
593 V: IntoLua + Clone + MaybeSend + 'static,
594 {
595 let callback: BoxedCallback = Box::new(move |lua, _args| {
596 let v = value.clone().into_lua(lua)?;
597 v.into_lua_multi(lua)
598 });
599 self.fields.push(FieldEntry {
600 name: name.into(),
601 is_get: true,
602 callback,
603 });
604 }
605
606 fn add_field_method_get<M, R>(&mut self, name: impl Into<String>, method: M)
607 where
608 M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
609 R: IntoLua,
610 {
611 let callback: BoxedCallback = Box::new(move |lua, mut args| {
612 let this = args.pop_front().unwrap_or(Value::Nil);
613 let cell = recover_cell::<T>(lua, &this)?;
614 let borrowed = cell
615 .cell
616 .try_borrow()
617 .map_err(|_| Error::UserDataBorrowError)?;
618 let data = borrowed.as_ref().ok_or(Error::UserDataDestructed)?;
619 let r = method(lua, data)?;
620 r.into_lua_multi(lua)
621 });
622 self.fields.push(FieldEntry {
623 name: name.into(),
624 is_get: true,
625 callback,
626 });
627 }
628
629 fn add_field_method_set<M, A>(&mut self, name: impl Into<String>, method: M)
630 where
631 M: Fn(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
632 A: FromLua,
633 {
634 let callback: BoxedCallback = Box::new(move |lua, mut args| {
635 let this = args.pop_front().unwrap_or(Value::Nil);
636 let cell = recover_cell::<T>(lua, &this)?;
637 let val = A::from_lua(args.pop_front().unwrap_or(Value::Nil), lua)?;
638 let mut borrowed = cell
639 .cell
640 .try_borrow_mut()
641 .map_err(|_| Error::UserDataBorrowMutError)?;
642 let data = borrowed.as_mut().ok_or(Error::UserDataDestructed)?;
643 method(lua, data, val)?;
644 ().into_lua_multi(lua)
645 });
646 self.fields.push(FieldEntry {
647 name: name.into(),
648 is_get: false,
649 callback,
650 });
651 }
652
653 fn add_field_function_get<F, R>(&mut self, name: impl Into<String>, function: F)
654 where
655 F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
656 R: IntoLua,
657 {
658 let callback: BoxedCallback = Box::new(move |lua, mut args| {
659 let this = args.pop_front().unwrap_or(Value::Nil);
660 let ud = AnyUserData::from_lua(this, lua)?;
661 let r = function(lua, ud)?;
662 r.into_lua_multi(lua)
663 });
664 self.fields.push(FieldEntry {
665 name: name.into(),
666 is_get: true,
667 callback,
668 });
669 }
670
671 fn add_field_function_set<F, A>(&mut self, name: impl Into<String>, function: F)
672 where
673 F: Fn(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
674 A: FromLua,
675 {
676 let callback: BoxedCallback = Box::new(move |lua, mut args| {
677 let this = args.pop_front().unwrap_or(Value::Nil);
678 let ud = AnyUserData::from_lua(this, lua)?;
679 let val = A::from_lua(args.pop_front().unwrap_or(Value::Nil), lua)?;
680 function(lua, ud, val)?;
681 ().into_lua_multi(lua)
682 });
683 self.fields.push(FieldEntry {
684 name: name.into(),
685 is_get: false,
686 callback,
687 });
688 }
689}
690
691unsafe extern "C" fn userdata_dtor<T>(ptr: *mut c_void) {
693 if !ptr.is_null() {
694 unsafe { core::ptr::drop_in_place(ptr as *mut UserDataCell<T>) };
695 }
696}
697
698use std::sync::atomic::{AtomicU64, Ordering};
721
722static SCOPED_MARKER: AtomicU64 = AtomicU64::new(1);
724
725fn next_scoped_marker() -> u64 {
726 SCOPED_MARKER.fetch_add(1, Ordering::Relaxed)
727}
728
729#[repr(C)]
732struct ScopedHeader {
733 marker: u64,
734}
735
736#[repr(C)]
739struct ScopedCell<T> {
740 marker: u64,
741 cell: RefCell<Option<T>>,
742}
743
744unsafe extern "C" fn scoped_userdata_dtor<T>(ptr: *mut c_void) {
746 if !ptr.is_null() {
747 unsafe { core::ptr::drop_in_place(ptr as *mut ScopedCell<T>) };
748 }
749}
750
751unsafe fn recover_scoped_cell<'a, T>(
760 lua: &Lua,
761 value: &Value,
762 marker: u64,
763) -> Result<&'a ScopedCell<T>> {
764 match value {
765 Value::UserData(ud) => {
766 let state = lua.state();
767 unsafe {
768 ud.reference.push();
769 let ptr = lua_touserdata(state, -1);
770 lua_pop(state, 1);
771 if ptr.is_null() {
772 return Err(Error::UserDataTypeMismatch);
773 }
774 let header = &*(ptr as *const ScopedHeader);
775 if header.marker != marker {
776 return Err(Error::UserDataTypeMismatch);
777 }
778 Ok(&*(ptr as *const ScopedCell<T>))
779 }
780 }
781 _ => Err(Error::UserDataTypeMismatch),
782 }
783}
784
785struct ScopedCollector<T> {
790 marker: u64,
791 methods: Vec<Registered>,
792 fields: Vec<FieldEntry>,
793 _phantom: PhantomData<T>,
794}
795
796impl<T> ScopedCollector<T> {
797 fn new(marker: u64) -> Self {
798 ScopedCollector {
799 marker,
800 methods: Vec::new(),
801 fields: Vec::new(),
802 _phantom: PhantomData,
803 }
804 }
805}
806
807impl<T> UserDataMethods<T> for ScopedCollector<T> {
808 fn add_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
809 where
810 M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
811 A: FromLuaMulti,
812 R: IntoLuaMulti,
813 {
814 let marker = self.marker;
815 let callback: BoxedCallback = Box::new(move |lua, mut args| {
816 let this = args.pop_front().unwrap_or(Value::Nil);
817 let cell = unsafe { recover_scoped_cell::<T>(lua, &this, marker)? };
818 let a = A::from_lua_multi(args, lua)?;
819 let borrowed = cell
820 .cell
821 .try_borrow()
822 .map_err(|_| Error::UserDataBorrowError)?;
823 let data = borrowed.as_ref().ok_or(Error::UserDataDestructed)?;
824 let r = method(lua, data, a)?;
825 r.into_lua_multi(lua)
826 });
827 self.methods.push(Registered {
828 name: name.into(),
829 is_meta: false,
830 callback,
831 });
832 }
833
834 fn add_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
835 where
836 M: Fn(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
837 A: FromLuaMulti,
838 R: IntoLuaMulti,
839 {
840 let marker = self.marker;
841 let callback: BoxedCallback = Box::new(move |lua, mut args| {
842 let this = args.pop_front().unwrap_or(Value::Nil);
843 let cell = unsafe { recover_scoped_cell::<T>(lua, &this, marker)? };
844 let a = A::from_lua_multi(args, lua)?;
845 let mut borrowed = cell
846 .cell
847 .try_borrow_mut()
848 .map_err(|_| Error::UserDataBorrowMutError)?;
849 let data = borrowed.as_mut().ok_or(Error::UserDataDestructed)?;
850 let r = method(lua, data, a)?;
851 r.into_lua_multi(lua)
852 });
853 self.methods.push(Registered {
854 name: name.into(),
855 is_meta: false,
856 callback,
857 });
858 }
859
860 fn add_function<F, A, R>(&mut self, name: impl Into<String>, function: F)
861 where
862 F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
863 A: FromLuaMulti,
864 R: IntoLuaMulti,
865 {
866 let callback: BoxedCallback = Box::new(move |lua, args| {
867 let a = A::from_lua_multi(args, lua)?;
868 let r = function(lua, a)?;
869 r.into_lua_multi(lua)
870 });
871 self.methods.push(Registered {
872 name: name.into(),
873 is_meta: false,
874 callback,
875 });
876 }
877
878 fn add_meta_method<M, A, R>(&mut self, name: impl Into<String>, method: M)
879 where
880 M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
881 A: FromLuaMulti,
882 R: IntoLuaMulti,
883 {
884 let marker = self.marker;
885 let callback: BoxedCallback = Box::new(move |lua, mut args| {
886 let this = args.pop_front().unwrap_or(Value::Nil);
887 let cell = unsafe { recover_scoped_cell::<T>(lua, &this, marker)? };
888 let a = A::from_lua_multi(args, lua)?;
889 let borrowed = cell
890 .cell
891 .try_borrow()
892 .map_err(|_| Error::UserDataBorrowError)?;
893 let data = borrowed.as_ref().ok_or(Error::UserDataDestructed)?;
894 let r = method(lua, data, a)?;
895 r.into_lua_multi(lua)
896 });
897 self.methods.push(Registered {
898 name: name.into(),
899 is_meta: true,
900 callback,
901 });
902 }
903
904 fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<String>, method: M)
905 where
906 M: Fn(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
907 A: FromLuaMulti,
908 R: IntoLuaMulti,
909 {
910 let marker = self.marker;
911 let callback: BoxedCallback = Box::new(move |lua, mut args| {
912 let this = args.pop_front().unwrap_or(Value::Nil);
913 let cell = unsafe { recover_scoped_cell::<T>(lua, &this, marker)? };
914 let a = A::from_lua_multi(args, lua)?;
915 let mut borrowed = cell
916 .cell
917 .try_borrow_mut()
918 .map_err(|_| Error::UserDataBorrowMutError)?;
919 let data = borrowed.as_mut().ok_or(Error::UserDataDestructed)?;
920 let r = method(lua, data, a)?;
921 r.into_lua_multi(lua)
922 });
923 self.methods.push(Registered {
924 name: name.into(),
925 is_meta: true,
926 callback,
927 });
928 }
929}
930
931impl<T> UserDataFields<T> for ScopedCollector<T> {
932 fn add_field<V>(&mut self, name: impl Into<String>, value: V)
933 where
934 V: IntoLua + Clone + MaybeSend + 'static,
935 {
936 let callback: BoxedCallback = Box::new(move |lua, _args| {
937 let v = value.clone().into_lua(lua)?;
938 v.into_lua_multi(lua)
939 });
940 self.fields.push(FieldEntry {
941 name: name.into(),
942 is_get: true,
943 callback,
944 });
945 }
946
947 fn add_field_method_get<M, R>(&mut self, name: impl Into<String>, method: M)
948 where
949 M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
950 R: IntoLua,
951 {
952 let marker = self.marker;
953 let callback: BoxedCallback = Box::new(move |lua, mut args| {
954 let this = args.pop_front().unwrap_or(Value::Nil);
955 let cell = unsafe { recover_scoped_cell::<T>(lua, &this, marker)? };
956 let borrowed = cell
957 .cell
958 .try_borrow()
959 .map_err(|_| Error::UserDataBorrowError)?;
960 let data = borrowed.as_ref().ok_or(Error::UserDataDestructed)?;
961 let r = method(lua, data)?;
962 r.into_lua_multi(lua)
963 });
964 self.fields.push(FieldEntry {
965 name: name.into(),
966 is_get: true,
967 callback,
968 });
969 }
970
971 fn add_field_method_set<M, A>(&mut self, name: impl Into<String>, method: M)
972 where
973 M: Fn(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
974 A: FromLua,
975 {
976 let marker = self.marker;
977 let callback: BoxedCallback = Box::new(move |lua, mut args| {
978 let this = args.pop_front().unwrap_or(Value::Nil);
979 let cell = unsafe { recover_scoped_cell::<T>(lua, &this, marker)? };
980 let val = A::from_lua(args.pop_front().unwrap_or(Value::Nil), lua)?;
981 let mut borrowed = cell
982 .cell
983 .try_borrow_mut()
984 .map_err(|_| Error::UserDataBorrowMutError)?;
985 let data = borrowed.as_mut().ok_or(Error::UserDataDestructed)?;
986 method(lua, data, val)?;
987 ().into_lua_multi(lua)
988 });
989 self.fields.push(FieldEntry {
990 name: name.into(),
991 is_get: false,
992 callback,
993 });
994 }
995
996 fn add_field_function_get<F, R>(&mut self, name: impl Into<String>, function: F)
997 where
998 F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
999 R: IntoLua,
1000 {
1001 let callback: BoxedCallback = Box::new(move |lua, mut args| {
1002 let this = args.pop_front().unwrap_or(Value::Nil);
1003 let ud = AnyUserData::from_lua(this, lua)?;
1004 let r = function(lua, ud)?;
1005 r.into_lua_multi(lua)
1006 });
1007 self.fields.push(FieldEntry {
1008 name: name.into(),
1009 is_get: true,
1010 callback,
1011 });
1012 }
1013
1014 fn add_field_function_set<F, A>(&mut self, name: impl Into<String>, function: F)
1015 where
1016 F: Fn(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
1017 A: FromLua,
1018 {
1019 let callback: BoxedCallback = Box::new(move |lua, mut args| {
1020 let this = args.pop_front().unwrap_or(Value::Nil);
1021 let ud = AnyUserData::from_lua(this, lua)?;
1022 let val = A::from_lua(args.pop_front().unwrap_or(Value::Nil), lua)?;
1023 function(lua, ud, val)?;
1024 ().into_lua_multi(lua)
1025 });
1026 self.fields.push(FieldEntry {
1027 name: name.into(),
1028 is_get: false,
1029 callback,
1030 });
1031 }
1032}
1033
1034pub(crate) fn create_scoped_userdata<T: UserData>(
1047 lua: &Lua,
1048 data: T,
1049) -> Result<(AnyUserData, Box<dyn FnOnce()>)> {
1050 let state = lua.state();
1051 let marker = next_scoped_marker();
1052
1053 let mut collector = ScopedCollector::<T>::new(marker);
1055 T::add_fields(&mut collector);
1056 T::add_methods(&mut collector);
1057
1058 let method_table = lua.create_table();
1060 let metatable = lua.create_table();
1061 for item in collector.methods {
1062 let func = create_callback_function(lua, item.callback)?;
1063 if item.is_meta {
1064 metatable.set(item.name, func)?;
1065 } else {
1066 method_table.set(item.name, func)?;
1067 }
1068 }
1069
1070 let has_fields = !collector.fields.is_empty();
1071 let getters = lua.create_table();
1072 let setters = lua.create_table();
1073 for field in collector.fields {
1074 let func = create_callback_function(lua, field.callback)?;
1075 if field.is_get {
1076 getters.set(field.name, func)?;
1077 } else {
1078 setters.set(field.name, func)?;
1079 }
1080 }
1081
1082 if has_fields {
1083 let getters_c = getters.clone();
1084 let methods_c = method_table.clone();
1085 let index_fn = lua.create_function(move |_, (ud, key): (Value, Value)| {
1086 let getter: Value = getters_c.get(key.clone())?;
1087 if let Value::Function(f) = getter {
1088 return f.call::<Value>(ud);
1089 }
1090 let m: Value = methods_c.get(key)?;
1091 Ok(m)
1092 })?;
1093 metatable.set("__index", index_fn)?;
1094
1095 let setters_c = setters.clone();
1096 let newindex_fn =
1097 lua.create_function(move |_, (ud, key, val): (Value, Value, Value)| {
1098 let setter: Value = setters_c.get(key.clone())?;
1099 if let Value::Function(f) = setter {
1100 f.call::<()>((ud, val))?;
1101 return Ok(());
1102 }
1103 let name = key.to_string().unwrap_or_default();
1104 Err(Error::RuntimeError(format!(
1105 "attempt to set unknown field '{name}' on userdata"
1106 )))
1107 })?;
1108 metatable.set("__newindex", newindex_fn)?;
1109 } else {
1110 metatable.set("__index", method_table)?;
1111 }
1112
1113 let ud = unsafe {
1115 let storage = lua_newuserdatadtor(
1116 state,
1117 core::mem::size_of::<ScopedCell<T>>(),
1118 Some(scoped_userdata_dtor::<T>),
1119 );
1120 if storage.is_null() {
1121 return Err(Error::runtime(
1122 "luaur-rt: failed to allocate scoped userdata",
1123 ));
1124 }
1125 core::ptr::write(
1126 storage as *mut ScopedCell<T>,
1127 ScopedCell {
1128 marker,
1129 cell: RefCell::new(Some(data)),
1130 },
1131 );
1132 metatable.push_to_stack();
1133 lua_setmetatable(state, -2);
1134 AnyUserData::from_ref(lua.pop_ref())
1135 };
1136
1137 let ud_for_dtor = ud.clone();
1140 let neutralise: Box<dyn FnOnce()> = Box::new(move || {
1141 let state = ud_for_dtor.reference.state();
1142 unsafe {
1143 ud_for_dtor.reference.push();
1144 let ptr = lua_touserdata(state, -1);
1145 lua_pop(state, 1);
1146 if ptr.is_null() {
1147 return;
1148 }
1149 let cell = &*(ptr as *const ScopedCell<T>);
1150 if let Ok(mut guard) = cell.cell.try_borrow_mut() {
1154 let _ = guard.take();
1155 }
1156 }
1157 });
1158
1159 Ok((ud, neutralise))
1160}
1161
1162pub(crate) fn create_userdata<T: UserData + MaybeSend + MaybeSync + 'static>(
1165 lua: &Lua,
1166 data: T,
1167) -> Result<AnyUserData> {
1168 let state = lua.state();
1169
1170 let mut collector = Collector::<T>::new();
1172 T::add_fields(&mut collector);
1173 T::add_methods(&mut collector);
1174
1175 let method_table = lua.create_table();
1178 let metatable = lua.create_table();
1179 for item in collector.methods {
1180 let func = create_callback_function(lua, item.callback)?;
1181 if item.is_meta {
1182 metatable.set(item.name, func)?;
1183 } else {
1184 method_table.set(item.name, func)?;
1185 }
1186 }
1187
1188 let has_fields = !collector.fields.is_empty();
1189 let getters = lua.create_table();
1190 let setters = lua.create_table();
1191 for field in collector.fields {
1192 let func = create_callback_function(lua, field.callback)?;
1193 if field.is_get {
1194 getters.set(field.name, func)?;
1195 } else {
1196 setters.set(field.name, func)?;
1197 }
1198 }
1199
1200 if has_fields {
1201 let getters_c = getters.clone();
1203 let methods_c = method_table.clone();
1204 let index_fn = lua.create_function(move |_, (ud, key): (Value, Value)| {
1205 let getter: Value = getters_c.get(key.clone())?;
1206 if let Value::Function(f) = getter {
1207 return f.call::<Value>(ud);
1208 }
1209 let m: Value = methods_c.get(key)?;
1211 Ok(m)
1212 })?;
1213 metatable.set("__index", index_fn)?;
1214
1215 let setters_c = setters.clone();
1217 let newindex_fn =
1218 lua.create_function(move |_, (ud, key, val): (Value, Value, Value)| {
1219 let setter: Value = setters_c.get(key.clone())?;
1220 if let Value::Function(f) = setter {
1221 f.call::<()>((ud, val))?;
1222 return Ok(());
1223 }
1224 let name = key.to_string().unwrap_or_default();
1225 Err(Error::RuntimeError(format!(
1226 "attempt to set unknown field '{name}' on userdata"
1227 )))
1228 })?;
1229 metatable.set("__newindex", newindex_fn)?;
1230 } else {
1231 metatable.set("__index", method_table)?;
1233 }
1234
1235 unsafe {
1237 let storage = lua_newuserdatadtor(
1238 state,
1239 core::mem::size_of::<UserDataCell<T>>(),
1240 Some(userdata_dtor::<T>),
1241 );
1242 if storage.is_null() {
1243 return Err(Error::runtime("luaur-rt: failed to allocate userdata"));
1244 }
1245 core::ptr::write(
1246 storage as *mut UserDataCell<T>,
1247 UserDataCell {
1248 type_id: TypeId::of::<T>(),
1249 cell: RefCell::new(Some(data)),
1250 },
1251 );
1252
1253 metatable.push_to_stack();
1255 lua_setmetatable(state, -2);
1256
1257 Ok(AnyUserData::from_ref(lua.pop_ref()))
1259 }
1260}