#![allow(clippy::await_holding_refcell_ref, clippy::await_holding_lock)] use std::any::TypeId; use std::cell::RefCell; use std::marker::PhantomData; use std::os::raw::c_void; use std::string::String as StdString; use crate::error::{Error, Result}; use crate::state::{Lua, LuaGuard}; use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti}; use crate::types::{Callback, MaybeSend}; use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataStorage}; use crate::util::{get_userdata, short_type_name}; use crate::value::Value; #[cfg(feature = "async")] use { crate::types::AsyncCallback, crate::userdata::{UserDataRef, UserDataRefMut}, std::future::{self, Future}, }; #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] use std::rc::Rc; #[cfg(feature = "userdata-wrappers")] use std::sync::{Arc, Mutex, RwLock}; #[derive(Clone, Copy)] enum UserDataTypeId { Shared(TypeId), Unique(*mut c_void), #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] Rc(TypeId), #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] RcRefCell(TypeId), #[cfg(feature = "userdata-wrappers")] Arc(TypeId), #[cfg(feature = "userdata-wrappers")] ArcMutex(TypeId), #[cfg(feature = "userdata-wrappers")] ArcRwLock(TypeId), #[cfg(feature = "userdata-wrappers")] ArcParkingLotMutex(TypeId), #[cfg(feature = "userdata-wrappers")] ArcParkingLotRwLock(TypeId), } /// Handle to registry for userdata methods and metamethods. pub struct UserDataRegistry { lua: LuaGuard, raw: RawUserDataRegistry, ud_type_id: UserDataTypeId, _type: PhantomData, } pub(crate) struct RawUserDataRegistry { // Fields pub(crate) fields: Vec<(String, Result)>, pub(crate) field_getters: Vec<(String, Callback)>, pub(crate) field_setters: Vec<(String, Callback)>, pub(crate) meta_fields: Vec<(String, Result)>, // Methods pub(crate) methods: Vec<(String, Callback)>, #[cfg(feature = "async")] pub(crate) async_methods: Vec<(String, AsyncCallback)>, pub(crate) meta_methods: Vec<(String, Callback)>, #[cfg(feature = "async")] pub(crate) async_meta_methods: Vec<(String, AsyncCallback)>, pub(crate) destructor: ffi::lua_CFunction, pub(crate) type_id: Option, pub(crate) type_name: StdString, } impl UserDataTypeId { #[inline] pub(crate) fn type_id(self) -> Option { match self { UserDataTypeId::Shared(type_id) => Some(type_id), UserDataTypeId::Unique(_) => None, #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] UserDataTypeId::Rc(type_id) => Some(type_id), #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] UserDataTypeId::RcRefCell(type_id) => Some(type_id), #[cfg(feature = "userdata-wrappers")] UserDataTypeId::Arc(type_id) => Some(type_id), #[cfg(feature = "userdata-wrappers")] UserDataTypeId::ArcMutex(type_id) => Some(type_id), #[cfg(feature = "userdata-wrappers")] UserDataTypeId::ArcRwLock(type_id) => Some(type_id), #[cfg(feature = "userdata-wrappers")] UserDataTypeId::ArcParkingLotMutex(type_id) => Some(type_id), #[cfg(feature = "userdata-wrappers")] UserDataTypeId::ArcParkingLotRwLock(type_id) => Some(type_id), } } } #[cfg(feature = "send")] unsafe impl Send for UserDataTypeId {} impl UserDataRegistry { #[inline(always)] pub(crate) fn new(lua: &Lua, type_id: TypeId) -> Self { Self::with_type_id(lua, UserDataTypeId::Shared(type_id)) } #[inline(always)] pub(crate) fn new_unique(lua: &Lua, ud_ptr: *mut c_void) -> Self { Self::with_type_id(lua, UserDataTypeId::Unique(ud_ptr)) } #[inline(always)] fn with_type_id(lua: &Lua, ud_type_id: UserDataTypeId) -> Self { let raw = RawUserDataRegistry { fields: Vec::new(), field_getters: Vec::new(), field_setters: Vec::new(), meta_fields: Vec::new(), methods: Vec::new(), #[cfg(feature = "async")] async_methods: Vec::new(), meta_methods: Vec::new(), #[cfg(feature = "async")] async_meta_methods: Vec::new(), destructor: super::util::userdata_destructor::, type_id: ud_type_id.type_id(), type_name: short_type_name::(), }; UserDataRegistry { lua: lua.lock_arc(), raw, ud_type_id, _type: PhantomData, } } fn box_method(&self, name: &str, method: M) -> Callback where M: Fn(&Lua, &T, A) -> Result + MaybeSend + 'static, A: FromLuaMulti, R: IntoLuaMulti, { let name = get_function_name::(name); macro_rules! try_self_arg { ($res:expr) => { $res.map_err(|err| Error::bad_self_argument(&name, err))? }; } let target_type_id = self.ud_type_id; Box::new(move |rawlua, nargs| unsafe { if nargs == 0 { let err = Error::from_lua_conversion("missing argument", "userdata", None); try_self_arg!(Err(err)); } let state = rawlua.state(); // Find absolute "self" index before processing args let self_index = ffi::lua_absindex(state, -nargs); // Self was at position 1, so we pass 2 here let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua); match target_type_id { #[rustfmt::skip] UserDataTypeId::Shared(target_type_id) if try_self_arg!(rawlua.get_userdata_type_id::(self_index)) == Some(target_type_id) => { let ud = get_userdata::>(state, self_index); try_self_arg!((*ud).try_borrow_scoped(|ud| { method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua) })) } #[rustfmt::skip] UserDataTypeId::Unique(target_ptr) if get_userdata::>(state, self_index) as *mut c_void == target_ptr => { let ud = target_ptr as *mut UserDataStorage; try_self_arg!((*ud).try_borrow_scoped(|ud| { method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua) })) } #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] #[rustfmt::skip] UserDataTypeId::Rc(target_type_id) if try_self_arg!(rawlua.get_userdata_type_id::>(self_index)) == Some(target_type_id) => { let ud = get_userdata::>>(state, self_index); try_self_arg!((*ud).try_borrow_scoped(|ud| { method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua) })) } #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] #[rustfmt::skip] UserDataTypeId::RcRefCell(target_type_id) if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) == Some(target_type_id) => { let ud = get_userdata::>>>(state, self_index); try_self_arg!((*ud).try_borrow_scoped(|ud| { let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?; method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua) })) } #[cfg(feature = "userdata-wrappers")] #[rustfmt::skip] UserDataTypeId::Arc(target_type_id) if try_self_arg!(rawlua.get_userdata_type_id::>(self_index)) == Some(target_type_id) => { let ud = get_userdata::>>(state, self_index); try_self_arg!((*ud).try_borrow_scoped(|ud| { method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua) })) } #[cfg(feature = "userdata-wrappers")] #[rustfmt::skip] UserDataTypeId::ArcMutex(target_type_id) if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) == Some(target_type_id) => { let ud = get_userdata::>>>(state, self_index); try_self_arg!((*ud).try_borrow_scoped(|ud| { let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?; method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua) })) } #[cfg(feature = "userdata-wrappers")] #[rustfmt::skip] UserDataTypeId::ArcRwLock(target_type_id) if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) == Some(target_type_id) => { let ud = get_userdata::>>>(state, self_index); try_self_arg!((*ud).try_borrow_scoped(|ud| { let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?; method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua) })) } #[cfg(feature = "userdata-wrappers")] #[rustfmt::skip] UserDataTypeId::ArcParkingLotMutex(target_type_id) if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) == Some(target_type_id) => { let ud = get_userdata::>>>(state, self_index); try_self_arg!((*ud).try_borrow_scoped(|ud| { let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?; method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua) })) } #[cfg(feature = "userdata-wrappers")] #[rustfmt::skip] UserDataTypeId::ArcParkingLotRwLock(target_type_id) if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) == Some(target_type_id) => { let ud = get_userdata::>>>(state, self_index); try_self_arg!((*ud).try_borrow_scoped(|ud| { let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?; method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua) })) } _ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)), } }) } fn box_method_mut(&self, name: &str, method: M) -> Callback where M: FnMut(&Lua, &mut T, A) -> Result + MaybeSend + 'static, A: FromLuaMulti, R: IntoLuaMulti, { let name = get_function_name::(name); macro_rules! try_self_arg { ($res:expr) => { $res.map_err(|err| Error::bad_self_argument(&name, err))? }; } let method = RefCell::new(method); let target_type_id = self.ud_type_id; Box::new(move |rawlua, nargs| unsafe { let mut method = method.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?; if nargs == 0 { let err = Error::from_lua_conversion("missing argument", "userdata", None); try_self_arg!(Err(err)); } let state = rawlua.state(); // Find absolute "self" index before processing args let self_index = ffi::lua_absindex(state, -nargs); // Self was at position 1, so we pass 2 here let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua); match target_type_id { #[rustfmt::skip] UserDataTypeId::Shared(target_type_id) if try_self_arg!(rawlua.get_userdata_type_id::(self_index)) == Some(target_type_id) => { let ud = get_userdata::>(state, self_index); try_self_arg!((*ud).try_borrow_scoped_mut(|ud| { method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua) })) } #[rustfmt::skip] UserDataTypeId::Unique(target_ptr) if get_userdata::>(state, self_index) as *mut c_void == target_ptr => { let ud = target_ptr as *mut UserDataStorage; try_self_arg!((*ud).try_borrow_scoped_mut(|ud| { method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua) })) } #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] #[rustfmt::skip] UserDataTypeId::Rc(target_type_id) if try_self_arg!(rawlua.get_userdata_type_id::>(self_index)) == Some(target_type_id) => { Err(Error::UserDataBorrowMutError) }, #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] #[rustfmt::skip] UserDataTypeId::RcRefCell(target_type_id) if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) == Some(target_type_id) => { let ud = get_userdata::>>>(state, self_index); try_self_arg!((*ud).try_borrow_scoped(|ud| { let mut ud = ud.try_borrow_mut().map_err(|_| Error::UserDataBorrowMutError)?; method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua) })) } #[cfg(feature = "userdata-wrappers")] #[rustfmt::skip] UserDataTypeId::Arc(target_type_id) if try_self_arg!(rawlua.get_userdata_type_id::>(self_index)) == Some(target_type_id) => { Err(Error::UserDataBorrowMutError) }, #[cfg(feature = "userdata-wrappers")] #[rustfmt::skip] UserDataTypeId::ArcMutex(target_type_id) if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) == Some(target_type_id) => { let ud = get_userdata::>>>(state, self_index); try_self_arg!((*ud).try_borrow_scoped(|ud| { let mut ud = ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?; method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua) })) } #[cfg(feature = "userdata-wrappers")] #[rustfmt::skip] UserDataTypeId::ArcRwLock(target_type_id) if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) == Some(target_type_id) => { let ud = get_userdata::>>>(state, self_index); try_self_arg!((*ud).try_borrow_scoped(|ud| { let mut ud = ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?; method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua) })) } #[cfg(feature = "userdata-wrappers")] #[rustfmt::skip] UserDataTypeId::ArcParkingLotMutex(target_type_id) if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) == Some(target_type_id) => { let ud = get_userdata::>>>(state, self_index); try_self_arg!((*ud).try_borrow_scoped(|ud| { let mut ud = ud.try_lock().ok_or(Error::UserDataBorrowMutError)?; method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua) })) } #[cfg(feature = "userdata-wrappers")] #[rustfmt::skip] UserDataTypeId::ArcParkingLotRwLock(target_type_id) if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) == Some(target_type_id) => { let ud = get_userdata::>>>(state, self_index); try_self_arg!((*ud).try_borrow_scoped(|ud| { let mut ud = ud.try_write().ok_or(Error::UserDataBorrowMutError)?; method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua) })) } _ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)), } }) } #[cfg(feature = "async")] fn box_async_method(&self, name: &str, method: M) -> AsyncCallback where T: 'static, M: Fn(Lua, UserDataRef, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, MR: Future> + MaybeSend + 'static, R: IntoLuaMulti, { let name = get_function_name::(name); macro_rules! try_self_arg { ($res:expr) => { match $res { Ok(res) => res, Err(err) => return Box::pin(future::ready(Err(Error::bad_self_argument(&name, err)))), } }; } Box::new(move |rawlua, nargs| unsafe { if nargs == 0 { let err = Error::from_lua_conversion("missing argument", "userdata", None); try_self_arg!(Err(err)); } // Stack will be empty when polling the future, keep `self` on the ref thread let self_ud = try_self_arg!(AnyUserData::from_stack(-nargs, rawlua)); let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua); let self_ud = try_self_arg!(self_ud.borrow()); let args = match args { Ok(args) => args, Err(e) => return Box::pin(future::ready(Err(e))), }; let lua = rawlua.lua(); let fut = method(lua.clone(), self_ud, args); // Lua is locked when the future is polled Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) }) }) } #[cfg(feature = "async")] fn box_async_method_mut(&self, name: &str, method: M) -> AsyncCallback where T: 'static, M: Fn(Lua, UserDataRefMut, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, MR: Future> + MaybeSend + 'static, R: IntoLuaMulti, { let name = get_function_name::(name); macro_rules! try_self_arg { ($res:expr) => { match $res { Ok(res) => res, Err(err) => return Box::pin(future::ready(Err(Error::bad_self_argument(&name, err)))), } }; } Box::new(move |rawlua, nargs| unsafe { if nargs == 0 { let err = Error::from_lua_conversion("missing argument", "userdata", None); try_self_arg!(Err(err)); } // Stack will be empty when polling the future, keep `self` on the ref thread let self_ud = try_self_arg!(AnyUserData::from_stack(-nargs, rawlua)); let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua); let self_ud = try_self_arg!(self_ud.borrow_mut()); let args = match args { Ok(args) => args, Err(e) => return Box::pin(future::ready(Err(e))), }; let lua = rawlua.lua(); let fut = method(lua.clone(), self_ud, args); // Lua is locked when the future is polled Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) }) }) } fn box_function(&self, name: &str, function: F) -> Callback where F: Fn(&Lua, A) -> Result + MaybeSend + 'static, A: FromLuaMulti, R: IntoLuaMulti, { let name = get_function_name::(name); Box::new(move |lua, nargs| unsafe { let args = A::from_stack_args(nargs, 1, Some(&name), lua)?; function(lua.lua(), args)?.push_into_stack_multi(lua) }) } fn box_function_mut(&self, name: &str, function: F) -> Callback where F: FnMut(&Lua, A) -> Result + MaybeSend + 'static, A: FromLuaMulti, R: IntoLuaMulti, { let name = get_function_name::(name); let function = RefCell::new(function); Box::new(move |lua, nargs| unsafe { let function = &mut *function .try_borrow_mut() .map_err(|_| Error::RecursiveMutCallback)?; let args = A::from_stack_args(nargs, 1, Some(&name), lua)?; function(lua.lua(), args)?.push_into_stack_multi(lua) }) } #[cfg(feature = "async")] fn box_async_function(&self, name: &str, function: F) -> AsyncCallback where F: Fn(Lua, A) -> FR + MaybeSend + 'static, A: FromLuaMulti, FR: Future> + MaybeSend + 'static, R: IntoLuaMulti, { let name = get_function_name::(name); Box::new(move |rawlua, nargs| unsafe { let args = match A::from_stack_args(nargs, 1, Some(&name), rawlua) { Ok(args) => args, Err(e) => return Box::pin(future::ready(Err(e))), }; let lua = rawlua.lua(); let fut = function(lua.clone(), args); Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) }) }) } pub(crate) fn check_meta_field(lua: &Lua, name: &str, value: impl IntoLua) -> Result { let value = value.into_lua(lua)?; if name == MetaMethod::Index || name == MetaMethod::NewIndex { match value { Value::Nil | Value::Table(_) | Value::Function(_) => {} _ => { return Err(Error::MetaMethodTypeError { method: name.to_string(), type_name: value.type_name(), message: Some("expected nil, table or function".to_string()), }) } } } value.into_lua(lua) } #[inline(always)] pub(crate) fn into_raw(self) -> RawUserDataRegistry { self.raw } } // Returns function name for the type `T`, without the module path fn get_function_name(name: &str) -> StdString { format!("{}.{name}", short_type_name::()) } impl UserDataFields for UserDataRegistry { fn add_field(&mut self, name: impl ToString, value: V) where V: IntoLua + 'static, { let name = name.to_string(); self.raw.fields.push((name, value.into_lua(self.lua.lua()))); } fn add_field_method_get(&mut self, name: impl ToString, method: M) where M: Fn(&Lua, &T) -> Result + MaybeSend + 'static, R: IntoLua, { let name = name.to_string(); let callback = self.box_method(&name, move |lua, data, ()| method(lua, data)); self.raw.field_getters.push((name, callback)); } fn add_field_method_set(&mut self, name: impl ToString, method: M) where M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static, A: FromLua, { let name = name.to_string(); let callback = self.box_method_mut(&name, method); self.raw.field_setters.push((name, callback)); } fn add_field_function_get(&mut self, name: impl ToString, function: F) where F: Fn(&Lua, AnyUserData) -> Result + MaybeSend + 'static, R: IntoLua, { let name = name.to_string(); let callback = self.box_function(&name, function); self.raw.field_getters.push((name, callback)); } fn add_field_function_set(&mut self, name: impl ToString, mut function: F) where F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static, A: FromLua, { let name = name.to_string(); let callback = self.box_function_mut(&name, move |lua, (data, val)| function(lua, data, val)); self.raw.field_setters.push((name, callback)); } fn add_meta_field(&mut self, name: impl ToString, value: V) where V: IntoLua + 'static, { let lua = self.lua.lua(); let name = name.to_string(); let field = Self::check_meta_field(lua, &name, value).and_then(|v| v.into_lua(lua)); self.raw.meta_fields.push((name, field)); } fn add_meta_field_with(&mut self, name: impl ToString, f: F) where F: FnOnce(&Lua) -> Result + 'static, R: IntoLua, { let lua = self.lua.lua(); let name = name.to_string(); let field = f(lua).and_then(|v| Self::check_meta_field(lua, &name, v).and_then(|v| v.into_lua(lua))); self.raw.meta_fields.push((name, field)); } } impl UserDataMethods for UserDataRegistry { fn add_method(&mut self, name: impl ToString, method: M) where M: Fn(&Lua, &T, A) -> Result + MaybeSend + 'static, A: FromLuaMulti, R: IntoLuaMulti, { let name = name.to_string(); let callback = self.box_method(&name, method); self.raw.methods.push((name, callback)); } fn add_method_mut(&mut self, name: impl ToString, method: M) where M: FnMut(&Lua, &mut T, A) -> Result + MaybeSend + 'static, A: FromLuaMulti, R: IntoLuaMulti, { let name = name.to_string(); let callback = self.box_method_mut(&name, method); self.raw.methods.push((name, callback)); } #[cfg(feature = "async")] fn add_async_method(&mut self, name: impl ToString, method: M) where T: 'static, M: Fn(Lua, UserDataRef, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, MR: Future> + MaybeSend + 'static, R: IntoLuaMulti, { let name = name.to_string(); let callback = self.box_async_method(&name, method); self.raw.async_methods.push((name, callback)); } #[cfg(feature = "async")] fn add_async_method_mut(&mut self, name: impl ToString, method: M) where T: 'static, M: Fn(Lua, UserDataRefMut, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, MR: Future> + MaybeSend + 'static, R: IntoLuaMulti, { let name = name.to_string(); let callback = self.box_async_method_mut(&name, method); self.raw.async_methods.push((name, callback)); } fn add_function(&mut self, name: impl ToString, function: F) where F: Fn(&Lua, A) -> Result + MaybeSend + 'static, A: FromLuaMulti, R: IntoLuaMulti, { let name = name.to_string(); let callback = self.box_function(&name, function); self.raw.methods.push((name, callback)); } fn add_function_mut(&mut self, name: impl ToString, function: F) where F: FnMut(&Lua, A) -> Result + MaybeSend + 'static, A: FromLuaMulti, R: IntoLuaMulti, { let name = name.to_string(); let callback = self.box_function_mut(&name, function); self.raw.methods.push((name, callback)); } #[cfg(feature = "async")] fn add_async_function(&mut self, name: impl ToString, function: F) where F: Fn(Lua, A) -> FR + MaybeSend + 'static, A: FromLuaMulti, FR: Future> + MaybeSend + 'static, R: IntoLuaMulti, { let name = name.to_string(); let callback = self.box_async_function(&name, function); self.raw.async_methods.push((name, callback)); } fn add_meta_method(&mut self, name: impl ToString, method: M) where M: Fn(&Lua, &T, A) -> Result + MaybeSend + 'static, A: FromLuaMulti, R: IntoLuaMulti, { let name = name.to_string(); let callback = self.box_method(&name, method); self.raw.meta_methods.push((name, callback)); } fn add_meta_method_mut(&mut self, name: impl ToString, method: M) where M: FnMut(&Lua, &mut T, A) -> Result + MaybeSend + 'static, A: FromLuaMulti, R: IntoLuaMulti, { let name = name.to_string(); let callback = self.box_method_mut(&name, method); self.raw.meta_methods.push((name, callback)); } #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] fn add_async_meta_method(&mut self, name: impl ToString, method: M) where T: 'static, M: Fn(Lua, UserDataRef, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, MR: Future> + MaybeSend + 'static, R: IntoLuaMulti, { let name = name.to_string(); let callback = self.box_async_method(&name, method); self.raw.async_meta_methods.push((name, callback)); } #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] fn add_async_meta_method_mut(&mut self, name: impl ToString, method: M) where T: 'static, M: Fn(Lua, UserDataRefMut, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, MR: Future> + MaybeSend + 'static, R: IntoLuaMulti, { let name = name.to_string(); let callback = self.box_async_method_mut(&name, method); self.raw.async_meta_methods.push((name, callback)); } fn add_meta_function(&mut self, name: impl ToString, function: F) where F: Fn(&Lua, A) -> Result + MaybeSend + 'static, A: FromLuaMulti, R: IntoLuaMulti, { let name = name.to_string(); let callback = self.box_function(&name, function); self.raw.meta_methods.push((name, callback)); } fn add_meta_function_mut(&mut self, name: impl ToString, function: F) where F: FnMut(&Lua, A) -> Result + MaybeSend + 'static, A: FromLuaMulti, R: IntoLuaMulti, { let name = name.to_string(); let callback = self.box_function_mut(&name, function); self.raw.meta_methods.push((name, callback)); } #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] fn add_async_meta_function(&mut self, name: impl ToString, function: F) where F: Fn(Lua, A) -> FR + MaybeSend + 'static, A: FromLuaMulti, FR: Future> + MaybeSend + 'static, R: IntoLuaMulti, { let name = name.to_string(); let callback = self.box_async_function(&name, function); self.raw.async_meta_methods.push((name, callback)); } } macro_rules! lua_userdata_impl { ($type:ty => $type_variant:tt) => { lua_userdata_impl!($type, UserDataTypeId::$type_variant(TypeId::of::<$type>())); }; ($type:ty, $type_id:expr) => { impl UserData for $type { fn register(registry: &mut UserDataRegistry) { let mut orig_registry = UserDataRegistry::with_type_id(registry.lua.lua(), $type_id); T::register(&mut orig_registry); // Copy all fields, methods, etc. from the original registry (registry.raw.fields).extend(orig_registry.raw.fields); (registry.raw.field_getters).extend(orig_registry.raw.field_getters); (registry.raw.field_setters).extend(orig_registry.raw.field_setters); (registry.raw.meta_fields).extend(orig_registry.raw.meta_fields); (registry.raw.methods).extend(orig_registry.raw.methods); #[cfg(feature = "async")] (registry.raw.async_methods).extend(orig_registry.raw.async_methods); (registry.raw.meta_methods).extend(orig_registry.raw.meta_methods); #[cfg(feature = "async")] (registry.raw.async_meta_methods).extend(orig_registry.raw.async_meta_methods); } } }; } // A special proxy object for UserData pub(crate) struct UserDataProxy(pub(crate) PhantomData); lua_userdata_impl!(UserDataProxy, UserDataTypeId::Shared(TypeId::of::())); #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] lua_userdata_impl!(Rc => Rc); #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] lua_userdata_impl!(Rc> => RcRefCell); #[cfg(feature = "userdata-wrappers")] lua_userdata_impl!(Arc => Arc); #[cfg(feature = "userdata-wrappers")] lua_userdata_impl!(Arc> => ArcMutex); #[cfg(feature = "userdata-wrappers")] lua_userdata_impl!(Arc> => ArcRwLock); #[cfg(feature = "userdata-wrappers")] lua_userdata_impl!(Arc> => ArcParkingLotMutex); #[cfg(feature = "userdata-wrappers")] lua_userdata_impl!(Arc> => ArcParkingLotRwLock); #[cfg(test)] mod assertions { #[cfg(feature = "send")] static_assertions::assert_impl_all!(super::RawUserDataRegistry: Send); }