diff --git a/src/lib.rs b/src/lib.rs index dbae8ae..4a0d5ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,6 +96,7 @@ mod table; mod thread; mod types; mod userdata; +mod userdata_impl; mod util; mod value; diff --git a/src/lua.rs b/src/lua.rs index 08a4770..8cfc967 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -7,7 +7,7 @@ use std::fmt; use std::marker::PhantomData; use std::os::raw::{c_char, c_int, c_void}; use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe, Location}; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, Mutex}; use std::{mem, ptr, str}; use rustc_hash::FxHashMap; @@ -26,9 +26,8 @@ use crate::types::{ Callback, CallbackUpvalue, DestructedUserdataMT, Integer, LightUserData, LuaRef, MaybeSend, Number, RegistryKey, }; -use crate::userdata::{ - AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods, -}; +use crate::userdata::{AnyUserData, UserData, UserDataCell}; +use crate::userdata_impl::{StaticUserDataFields, StaticUserDataMethods}; use crate::util::{ self, assert_stack, callback_error, check_stack, get_destructed_userdata_metatable, get_gc_metatable, get_gc_userdata, get_main_state, get_userdata, init_error_registry, @@ -48,9 +47,6 @@ use { #[cfg(not(feature = "luau"))] use crate::{hook::HookTriggers, types::HookCallback}; -#[cfg(not(feature = "send"))] -use std::rc::Rc; - #[cfg(feature = "async")] use { crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue}, @@ -2133,7 +2129,7 @@ impl Lua { f(ref_thread) } - pub(crate) unsafe fn push_userdata_metatable(&self) -> Result<()> { + unsafe fn push_userdata_metatable(&self) -> Result<()> { let extra = &mut *self.extra.get(); let type_id = TypeId::of::(); @@ -2272,16 +2268,6 @@ impl Lua { } } - #[inline] - unsafe fn get_userdata_ref(&self) -> Result> { - (*get_userdata::>(self.state, -1)).try_borrow() - } - - #[inline] - unsafe fn get_userdata_mut(&self) -> Result> { - (*get_userdata::>(self.state, -1)).try_borrow_mut() - } - // Creates a Function out of a Callback containing a 'static Fn. This is safe ONLY because the // Fn is 'static, otherwise it could capture 'callback arguments improperly. Without ATCs, we // cannot easily deal with the "correct" callback type of: @@ -2980,552 +2966,3 @@ unsafe fn ref_stack_pop(extra: &mut ExtraData) -> c_int { extra.ref_stack_top += 1; extra.ref_stack_top } - -struct StaticUserDataMethods<'lua, T: 'static + UserData> { - methods: Vec<(Vec, Callback<'lua, 'static>)>, - #[cfg(feature = "async")] - async_methods: Vec<(Vec, AsyncCallback<'lua, 'static>)>, - meta_methods: Vec<(MetaMethod, Callback<'lua, 'static>)>, - #[cfg(feature = "async")] - async_meta_methods: Vec<(MetaMethod, AsyncCallback<'lua, 'static>)>, - _type: PhantomData, -} - -impl<'lua, T: 'static + UserData> Default for StaticUserDataMethods<'lua, T> { - fn default() -> StaticUserDataMethods<'lua, T> { - StaticUserDataMethods { - methods: Vec::new(), - #[cfg(feature = "async")] - async_methods: Vec::new(), - meta_methods: Vec::new(), - #[cfg(feature = "async")] - async_meta_methods: Vec::new(), - _type: PhantomData, - } - } -} - -impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMethods<'lua, T> { - fn add_method(&mut self, name: &S, method: M) - where - S: AsRef<[u8]> + ?Sized, - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result, - { - self.methods - .push((name.as_ref().to_vec(), Self::box_method(method))); - } - - fn add_method_mut(&mut self, name: &S, method: M) - where - S: AsRef<[u8]> + ?Sized, - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result, - { - self.methods - .push((name.as_ref().to_vec(), Self::box_method_mut(method))); - } - - #[cfg(feature = "async")] - fn add_async_method(&mut self, name: &S, method: M) - where - T: Clone, - S: AsRef<[u8]> + ?Sized, - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR, - MR: 'lua + Future>, - { - self.async_methods - .push((name.as_ref().to_vec(), Self::box_async_method(method))); - } - - fn add_function(&mut self, name: &S, function: F) - where - S: AsRef<[u8]> + ?Sized, - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result, - { - self.methods - .push((name.as_ref().to_vec(), Self::box_function(function))); - } - - fn add_function_mut(&mut self, name: &S, function: F) - where - S: AsRef<[u8]> + ?Sized, - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result, - { - self.methods - .push((name.as_ref().to_vec(), Self::box_function_mut(function))); - } - - #[cfg(feature = "async")] - fn add_async_function(&mut self, name: &S, function: F) - where - S: AsRef<[u8]> + ?Sized, - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR, - FR: 'lua + Future>, - { - self.async_methods - .push((name.as_ref().to_vec(), Self::box_async_function(function))); - } - - fn add_meta_method(&mut self, meta: S, method: M) - where - S: Into, - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result, - { - self.meta_methods - .push((meta.into(), Self::box_method(method))); - } - - fn add_meta_method_mut(&mut self, meta: S, method: M) - where - S: Into, - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result, - { - self.meta_methods - .push((meta.into(), Self::box_method_mut(method))); - } - - #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] - fn add_async_meta_method(&mut self, meta: S, method: M) - where - T: Clone, - S: Into, - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR, - MR: 'lua + Future>, - { - self.async_meta_methods - .push((meta.into(), Self::box_async_method(method))); - } - - fn add_meta_function(&mut self, meta: S, function: F) - where - S: Into, - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result, - { - self.meta_methods - .push((meta.into(), Self::box_function(function))); - } - - fn add_meta_function_mut(&mut self, meta: S, function: F) - where - S: Into, - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result, - { - self.meta_methods - .push((meta.into(), Self::box_function_mut(function))); - } - - #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] - fn add_async_meta_function(&mut self, meta: S, function: F) - where - S: Into, - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR, - FR: 'lua + Future>, - { - self.async_meta_methods - .push((meta.into(), Self::box_async_function(function))); - } - - // Below are internal methods used in generated code - - fn add_callback(&mut self, name: Vec, callback: Callback<'lua, 'static>) { - self.methods.push((name, callback)); - } - - #[cfg(feature = "async")] - fn add_async_callback(&mut self, name: Vec, callback: AsyncCallback<'lua, 'static>) { - self.async_methods.push((name, callback)); - } - - fn add_meta_callback(&mut self, meta: MetaMethod, callback: Callback<'lua, 'static>) { - self.meta_methods.push((meta, callback)); - } - - #[cfg(feature = "async")] - fn add_async_meta_callback( - &mut self, - meta: MetaMethod, - callback: AsyncCallback<'lua, 'static>, - ) { - self.async_meta_methods.push((meta, callback)) - } -} - -impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> { - fn box_method(method: M) -> Callback<'lua, 'static> - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result, - { - Box::new(move |lua, mut args| { - if let Some(front) = args.pop_front() { - let userdata = AnyUserData::from_lua(front, lua)?; - unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 2)?; - - let type_id = lua.push_userdata_ref(&userdata.0)?; - match type_id { - Some(id) if id == TypeId::of::() => { - let ud = lua.get_userdata_ref::()?; - method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - } - #[cfg(not(feature = "send"))] - Some(id) if id == TypeId::of::>>() => { - let ud = lua.get_userdata_ref::>>()?; - let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?; - method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - } - Some(id) if id == TypeId::of::>>() => { - let ud = lua.get_userdata_ref::>>()?; - let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?; - method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - } - Some(id) if id == TypeId::of::>>() => { - let ud = lua.get_userdata_ref::>>()?; - let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?; - method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - } - _ => Err(Error::UserDataTypeMismatch), - } - } - } else { - Err(Error::FromLuaConversionError { - from: "missing argument", - to: "userdata", - message: None, - }) - } - }) - } - - fn box_method_mut(method: M) -> Callback<'lua, 'static> - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result, - { - let method = RefCell::new(method); - Box::new(move |lua, mut args| { - if let Some(front) = args.pop_front() { - let userdata = AnyUserData::from_lua(front, lua)?; - let mut method = method - .try_borrow_mut() - .map_err(|_| Error::RecursiveMutCallback)?; - unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 2)?; - - let type_id = lua.push_userdata_ref(&userdata.0)?; - match type_id { - Some(id) if id == TypeId::of::() => { - let mut ud = lua.get_userdata_mut::()?; - method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - } - #[cfg(not(feature = "send"))] - Some(id) if id == TypeId::of::>>() => { - let ud = lua.get_userdata_mut::>>()?; - let mut ud = ud - .try_borrow_mut() - .map_err(|_| Error::UserDataBorrowMutError)?; - method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - } - Some(id) if id == TypeId::of::>>() => { - let ud = lua.get_userdata_mut::>>()?; - let mut ud = - ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?; - method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - } - Some(id) if id == TypeId::of::>>() => { - let ud = lua.get_userdata_mut::>>()?; - let mut ud = - ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?; - method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - } - _ => Err(Error::UserDataTypeMismatch), - } - } - } else { - Err(Error::FromLuaConversionError { - from: "missing argument", - to: "userdata", - message: None, - }) - } - }) - } - - #[cfg(feature = "async")] - fn box_async_method(method: M) -> AsyncCallback<'lua, 'static> - where - T: Clone, - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR, - MR: 'lua + Future>, - { - Box::new(move |lua, mut args| { - let fut_res = || { - if let Some(front) = args.pop_front() { - let userdata = AnyUserData::from_lua(front, lua)?; - unsafe { - let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 2)?; - - let type_id = lua.push_userdata_ref(&userdata.0)?; - match type_id { - Some(id) if id == TypeId::of::() => { - let ud = lua.get_userdata_ref::()?; - Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?)) - } - #[cfg(not(feature = "send"))] - Some(id) if id == TypeId::of::>>() => { - let ud = lua.get_userdata_ref::>>()?; - let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?; - Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?)) - } - Some(id) if id == TypeId::of::>>() => { - let ud = lua.get_userdata_ref::>>()?; - let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?; - Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?)) - } - Some(id) if id == TypeId::of::>>() => { - let ud = lua.get_userdata_ref::>>()?; - let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?; - Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?)) - } - _ => Err(Error::UserDataTypeMismatch), - } - } - } else { - Err(Error::FromLuaConversionError { - from: "missing argument", - to: "userdata", - message: None, - }) - } - }; - match fut_res() { - Ok(fut) => Box::pin(fut.and_then(move |ret| future::ready(ret.to_lua_multi(lua)))), - Err(e) => Box::pin(future::err(e)), - } - }) - } - - fn box_function(function: F) -> Callback<'lua, 'static> - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result, - { - Box::new(move |lua, args| function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)) - } - - fn box_function_mut(function: F) -> Callback<'lua, 'static> - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result, - { - let function = RefCell::new(function); - Box::new(move |lua, args| { - let function = &mut *function - .try_borrow_mut() - .map_err(|_| Error::RecursiveMutCallback)?; - function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - }) - } - - #[cfg(feature = "async")] - fn box_async_function(function: F) -> AsyncCallback<'lua, 'static> - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR, - FR: 'lua + Future>, - { - Box::new(move |lua, args| { - let args = match A::from_lua_multi(args, lua) { - Ok(args) => args, - Err(e) => return Box::pin(future::err(e)), - }; - Box::pin(function(lua, args).and_then(move |ret| future::ready(ret.to_lua_multi(lua)))) - }) - } -} - -struct StaticUserDataFields<'lua, T: 'static + UserData> { - field_getters: Vec<(Vec, Callback<'lua, 'static>)>, - field_setters: Vec<(Vec, Callback<'lua, 'static>)>, - #[allow(clippy::type_complexity)] - meta_fields: Vec<( - MetaMethod, - Box Result> + 'static>, - )>, - _type: PhantomData, -} - -impl<'lua, T: 'static + UserData> Default for StaticUserDataFields<'lua, T> { - fn default() -> StaticUserDataFields<'lua, T> { - StaticUserDataFields { - field_getters: Vec::new(), - field_setters: Vec::new(), - meta_fields: Vec::new(), - _type: PhantomData, - } - } -} - -impl<'lua, T: 'static + UserData> UserDataFields<'lua, T> for StaticUserDataFields<'lua, T> { - fn add_field_method_get(&mut self, name: &S, method: M) - where - S: AsRef<[u8]> + ?Sized, - R: ToLua<'lua>, - M: 'static + MaybeSend + Fn(&'lua Lua, &T) -> Result, - { - self.field_getters.push(( - name.as_ref().to_vec(), - StaticUserDataMethods::box_method(move |lua, data, ()| method(lua, data)), - )); - } - - fn add_field_method_set(&mut self, name: &S, method: M) - where - S: AsRef<[u8]> + ?Sized, - A: FromLua<'lua>, - M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<()>, - { - self.field_setters.push(( - name.as_ref().to_vec(), - StaticUserDataMethods::box_method_mut(method), - )); - } - - fn add_field_function_get(&mut self, name: &S, function: F) - where - S: AsRef<[u8]> + ?Sized, - R: ToLua<'lua>, - F: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>) -> Result, - { - self.field_getters.push(( - name.as_ref().to_vec(), - StaticUserDataMethods::::box_function(function), - )); - } - - fn add_field_function_set(&mut self, name: &S, mut function: F) - where - S: AsRef<[u8]> + ?Sized, - A: FromLua<'lua>, - F: 'static + MaybeSend + FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()>, - { - self.field_setters.push(( - name.as_ref().to_vec(), - StaticUserDataMethods::::box_function_mut(move |lua, (data, val)| { - function(lua, data, val) - }), - )); - } - - fn add_meta_field_with(&mut self, meta: S, f: F) - where - S: Into, - R: ToLua<'lua>, - F: 'static + MaybeSend + Fn(&'lua Lua) -> Result, - { - let meta = meta.into(); - self.meta_fields.push(( - meta.clone(), - Box::new(move |lua| { - let value = f(lua)?.to_lua(lua)?; - if meta == MetaMethod::Index || meta == MetaMethod::NewIndex { - match value { - Value::Nil | Value::Table(_) | Value::Function(_) => {} - _ => { - return Err(Error::MetaMethodTypeError { - method: meta.to_string(), - type_name: value.type_name(), - message: Some("expected nil, table or function".to_string()), - }) - } - } - } - Ok(value) - }), - )); - } - - // Below are internal methods - - fn add_field_getter(&mut self, name: Vec, callback: Callback<'lua, 'static>) { - self.field_getters.push((name, callback)); - } - - fn add_field_setter(&mut self, name: Vec, callback: Callback<'lua, 'static>) { - self.field_setters.push((name, callback)); - } -} - -macro_rules! lua_userdata_impl { - ($type:ty) => { - impl UserData for $type { - fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) { - let mut orig_fields = StaticUserDataFields::default(); - T::add_fields(&mut orig_fields); - for (name, callback) in orig_fields.field_getters { - fields.add_field_getter(name, callback); - } - for (name, callback) in orig_fields.field_setters { - fields.add_field_setter(name, callback); - } - } - - fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { - let mut orig_methods = StaticUserDataMethods::default(); - T::add_methods(&mut orig_methods); - for (name, callback) in orig_methods.methods { - methods.add_callback(name, callback); - } - #[cfg(feature = "async")] - for (name, callback) in orig_methods.async_methods { - methods.add_async_callback(name, callback); - } - for (meta, callback) in orig_methods.meta_methods { - methods.add_meta_callback(meta, callback); - } - #[cfg(feature = "async")] - for (meta, callback) in orig_methods.async_meta_methods { - methods.add_async_meta_callback(meta, callback); - } - } - } - }; -} - -#[cfg(not(feature = "send"))] -lua_userdata_impl!(Rc>); -lua_userdata_impl!(Arc>); -lua_userdata_impl!(Arc>); diff --git a/src/userdata_impl.rs b/src/userdata_impl.rs new file mode 100644 index 0000000..a535245 --- /dev/null +++ b/src/userdata_impl.rs @@ -0,0 +1,583 @@ +use std::any::TypeId; +use std::cell::{Ref, RefCell, RefMut}; +use std::marker::PhantomData; +use std::sync::{Arc, Mutex, RwLock}; + +use crate::error::{Error, Result}; +use crate::ffi; +use crate::lua::Lua; +use crate::types::{Callback, MaybeSend}; +use crate::userdata::{ + AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods, +}; +use crate::util::{check_stack, get_userdata, StackGuard}; +use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti, Value}; + +#[cfg(not(feature = "send"))] +use std::rc::Rc; + +#[cfg(feature = "async")] +use { + crate::types::AsyncCallback, + futures_core::future::Future, + futures_util::future::{self, TryFutureExt}, +}; + +pub(crate) struct StaticUserDataMethods<'lua, T: 'static + UserData> { + pub(crate) methods: Vec<(Vec, Callback<'lua, 'static>)>, + #[cfg(feature = "async")] + pub(crate) async_methods: Vec<(Vec, AsyncCallback<'lua, 'static>)>, + pub(crate) meta_methods: Vec<(MetaMethod, Callback<'lua, 'static>)>, + #[cfg(feature = "async")] + pub(crate) async_meta_methods: Vec<(MetaMethod, AsyncCallback<'lua, 'static>)>, + _type: PhantomData, +} + +impl<'lua, T: 'static + UserData> Default for StaticUserDataMethods<'lua, T> { + fn default() -> StaticUserDataMethods<'lua, T> { + StaticUserDataMethods { + methods: Vec::new(), + #[cfg(feature = "async")] + async_methods: Vec::new(), + meta_methods: Vec::new(), + #[cfg(feature = "async")] + async_meta_methods: Vec::new(), + _type: PhantomData, + } + } +} + +impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMethods<'lua, T> { + fn add_method(&mut self, name: &S, method: M) + where + S: AsRef<[u8]> + ?Sized, + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result, + { + self.methods + .push((name.as_ref().to_vec(), Self::box_method(method))); + } + + fn add_method_mut(&mut self, name: &S, method: M) + where + S: AsRef<[u8]> + ?Sized, + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result, + { + self.methods + .push((name.as_ref().to_vec(), Self::box_method_mut(method))); + } + + #[cfg(feature = "async")] + fn add_async_method(&mut self, name: &S, method: M) + where + T: Clone, + S: AsRef<[u8]> + ?Sized, + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR, + MR: 'lua + Future>, + { + self.async_methods + .push((name.as_ref().to_vec(), Self::box_async_method(method))); + } + + fn add_function(&mut self, name: &S, function: F) + where + S: AsRef<[u8]> + ?Sized, + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result, + { + self.methods + .push((name.as_ref().to_vec(), Self::box_function(function))); + } + + fn add_function_mut(&mut self, name: &S, function: F) + where + S: AsRef<[u8]> + ?Sized, + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result, + { + self.methods + .push((name.as_ref().to_vec(), Self::box_function_mut(function))); + } + + #[cfg(feature = "async")] + fn add_async_function(&mut self, name: &S, function: F) + where + S: AsRef<[u8]> + ?Sized, + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR, + FR: 'lua + Future>, + { + self.async_methods + .push((name.as_ref().to_vec(), Self::box_async_function(function))); + } + + fn add_meta_method(&mut self, meta: S, method: M) + where + S: Into, + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result, + { + self.meta_methods + .push((meta.into(), Self::box_method(method))); + } + + fn add_meta_method_mut(&mut self, meta: S, method: M) + where + S: Into, + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result, + { + self.meta_methods + .push((meta.into(), Self::box_method_mut(method))); + } + + #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] + fn add_async_meta_method(&mut self, meta: S, method: M) + where + T: Clone, + S: Into, + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR, + MR: 'lua + Future>, + { + self.async_meta_methods + .push((meta.into(), Self::box_async_method(method))); + } + + fn add_meta_function(&mut self, meta: S, function: F) + where + S: Into, + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result, + { + self.meta_methods + .push((meta.into(), Self::box_function(function))); + } + + fn add_meta_function_mut(&mut self, meta: S, function: F) + where + S: Into, + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result, + { + self.meta_methods + .push((meta.into(), Self::box_function_mut(function))); + } + + #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] + fn add_async_meta_function(&mut self, meta: S, function: F) + where + S: Into, + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR, + FR: 'lua + Future>, + { + self.async_meta_methods + .push((meta.into(), Self::box_async_function(function))); + } + + // Below are internal methods used in generated code + + fn add_callback(&mut self, name: Vec, callback: Callback<'lua, 'static>) { + self.methods.push((name, callback)); + } + + #[cfg(feature = "async")] + fn add_async_callback(&mut self, name: Vec, callback: AsyncCallback<'lua, 'static>) { + self.async_methods.push((name, callback)); + } + + fn add_meta_callback(&mut self, meta: MetaMethod, callback: Callback<'lua, 'static>) { + self.meta_methods.push((meta, callback)); + } + + #[cfg(feature = "async")] + fn add_async_meta_callback( + &mut self, + meta: MetaMethod, + callback: AsyncCallback<'lua, 'static>, + ) { + self.async_meta_methods.push((meta, callback)) + } +} + +impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> { + fn box_method(method: M) -> Callback<'lua, 'static> + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result, + { + Box::new(move |lua, mut args| { + if let Some(front) = args.pop_front() { + let userdata = AnyUserData::from_lua(front, lua)?; + unsafe { + let _sg = StackGuard::new(lua.state); + check_stack(lua.state, 2)?; + + let type_id = lua.push_userdata_ref(&userdata.0)?; + match type_id { + Some(id) if id == TypeId::of::() => { + let ud = get_userdata_ref::(lua.state)?; + method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } + #[cfg(not(feature = "send"))] + Some(id) if id == TypeId::of::>>() => { + let ud = get_userdata_ref::>>(lua.state)?; + let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?; + method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } + Some(id) if id == TypeId::of::>>() => { + let ud = get_userdata_ref::>>(lua.state)?; + let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?; + method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } + Some(id) if id == TypeId::of::>>() => { + let ud = get_userdata_ref::>>(lua.state)?; + let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?; + method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } + _ => Err(Error::UserDataTypeMismatch), + } + } + } else { + Err(Error::FromLuaConversionError { + from: "missing argument", + to: "userdata", + message: None, + }) + } + }) + } + + fn box_method_mut(method: M) -> Callback<'lua, 'static> + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result, + { + let method = RefCell::new(method); + Box::new(move |lua, mut args| { + if let Some(front) = args.pop_front() { + let userdata = AnyUserData::from_lua(front, lua)?; + let mut method = method + .try_borrow_mut() + .map_err(|_| Error::RecursiveMutCallback)?; + unsafe { + let _sg = StackGuard::new(lua.state); + check_stack(lua.state, 2)?; + + let type_id = lua.push_userdata_ref(&userdata.0)?; + match type_id { + Some(id) if id == TypeId::of::() => { + let mut ud = get_userdata_mut::(lua.state)?; + method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } + #[cfg(not(feature = "send"))] + Some(id) if id == TypeId::of::>>() => { + let ud = get_userdata_mut::>>(lua.state)?; + let mut ud = ud + .try_borrow_mut() + .map_err(|_| Error::UserDataBorrowMutError)?; + method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } + Some(id) if id == TypeId::of::>>() => { + let ud = get_userdata_mut::>>(lua.state)?; + let mut ud = + ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?; + method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } + Some(id) if id == TypeId::of::>>() => { + let ud = get_userdata_mut::>>(lua.state)?; + let mut ud = + ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?; + method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } + _ => Err(Error::UserDataTypeMismatch), + } + } + } else { + Err(Error::FromLuaConversionError { + from: "missing argument", + to: "userdata", + message: None, + }) + } + }) + } + + #[cfg(feature = "async")] + fn box_async_method(method: M) -> AsyncCallback<'lua, 'static> + where + T: Clone, + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR, + MR: 'lua + Future>, + { + Box::new(move |lua, mut args| { + let fut_res = || { + if let Some(front) = args.pop_front() { + let userdata = AnyUserData::from_lua(front, lua)?; + unsafe { + let _sg = StackGuard::new(lua.state); + check_stack(lua.state, 2)?; + + let type_id = lua.push_userdata_ref(&userdata.0)?; + match type_id { + Some(id) if id == TypeId::of::() => { + let ud = get_userdata_ref::(lua.state)?; + Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?)) + } + #[cfg(not(feature = "send"))] + Some(id) if id == TypeId::of::>>() => { + let ud = get_userdata_ref::>>(lua.state)?; + let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?; + Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?)) + } + Some(id) if id == TypeId::of::>>() => { + let ud = get_userdata_ref::>>(lua.state)?; + let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?; + Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?)) + } + Some(id) if id == TypeId::of::>>() => { + let ud = get_userdata_ref::>>(lua.state)?; + let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?; + Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?)) + } + _ => Err(Error::UserDataTypeMismatch), + } + } + } else { + Err(Error::FromLuaConversionError { + from: "missing argument", + to: "userdata", + message: None, + }) + } + }; + match fut_res() { + Ok(fut) => Box::pin(fut.and_then(move |ret| future::ready(ret.to_lua_multi(lua)))), + Err(e) => Box::pin(future::err(e)), + } + }) + } + + fn box_function(function: F) -> Callback<'lua, 'static> + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result, + { + Box::new(move |lua, args| function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)) + } + + fn box_function_mut(function: F) -> Callback<'lua, 'static> + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result, + { + let function = RefCell::new(function); + Box::new(move |lua, args| { + let function = &mut *function + .try_borrow_mut() + .map_err(|_| Error::RecursiveMutCallback)?; + function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + }) + } + + #[cfg(feature = "async")] + fn box_async_function(function: F) -> AsyncCallback<'lua, 'static> + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR, + FR: 'lua + Future>, + { + Box::new(move |lua, args| { + let args = match A::from_lua_multi(args, lua) { + Ok(args) => args, + Err(e) => return Box::pin(future::err(e)), + }; + Box::pin(function(lua, args).and_then(move |ret| future::ready(ret.to_lua_multi(lua)))) + }) + } +} + +pub(crate) struct StaticUserDataFields<'lua, T: 'static + UserData> { + pub(crate) field_getters: Vec<(Vec, Callback<'lua, 'static>)>, + pub(crate) field_setters: Vec<(Vec, Callback<'lua, 'static>)>, + #[allow(clippy::type_complexity)] + pub(crate) meta_fields: Vec<( + MetaMethod, + Box Result> + 'static>, + )>, + _type: PhantomData, +} + +impl<'lua, T: 'static + UserData> Default for StaticUserDataFields<'lua, T> { + fn default() -> StaticUserDataFields<'lua, T> { + StaticUserDataFields { + field_getters: Vec::new(), + field_setters: Vec::new(), + meta_fields: Vec::new(), + _type: PhantomData, + } + } +} + +impl<'lua, T: 'static + UserData> UserDataFields<'lua, T> for StaticUserDataFields<'lua, T> { + fn add_field_method_get(&mut self, name: &S, method: M) + where + S: AsRef<[u8]> + ?Sized, + R: ToLua<'lua>, + M: 'static + MaybeSend + Fn(&'lua Lua, &T) -> Result, + { + self.field_getters.push(( + name.as_ref().to_vec(), + StaticUserDataMethods::box_method(move |lua, data, ()| method(lua, data)), + )); + } + + fn add_field_method_set(&mut self, name: &S, method: M) + where + S: AsRef<[u8]> + ?Sized, + A: FromLua<'lua>, + M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<()>, + { + self.field_setters.push(( + name.as_ref().to_vec(), + StaticUserDataMethods::box_method_mut(method), + )); + } + + fn add_field_function_get(&mut self, name: &S, function: F) + where + S: AsRef<[u8]> + ?Sized, + R: ToLua<'lua>, + F: 'static + MaybeSend + Fn(&'lua Lua, AnyUserData<'lua>) -> Result, + { + self.field_getters.push(( + name.as_ref().to_vec(), + StaticUserDataMethods::::box_function(function), + )); + } + + fn add_field_function_set(&mut self, name: &S, mut function: F) + where + S: AsRef<[u8]> + ?Sized, + A: FromLua<'lua>, + F: 'static + MaybeSend + FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()>, + { + self.field_setters.push(( + name.as_ref().to_vec(), + StaticUserDataMethods::::box_function_mut(move |lua, (data, val)| { + function(lua, data, val) + }), + )); + } + + fn add_meta_field_with(&mut self, meta: S, f: F) + where + S: Into, + R: ToLua<'lua>, + F: 'static + MaybeSend + Fn(&'lua Lua) -> Result, + { + let meta = meta.into(); + self.meta_fields.push(( + meta.clone(), + Box::new(move |lua| { + let value = f(lua)?.to_lua(lua)?; + if meta == MetaMethod::Index || meta == MetaMethod::NewIndex { + match value { + Value::Nil | Value::Table(_) | Value::Function(_) => {} + _ => { + return Err(Error::MetaMethodTypeError { + method: meta.to_string(), + type_name: value.type_name(), + message: Some("expected nil, table or function".to_string()), + }) + } + } + } + Ok(value) + }), + )); + } + + // Below are internal methods + + fn add_field_getter(&mut self, name: Vec, callback: Callback<'lua, 'static>) { + self.field_getters.push((name, callback)); + } + + fn add_field_setter(&mut self, name: Vec, callback: Callback<'lua, 'static>) { + self.field_setters.push((name, callback)); + } +} + +#[inline] +unsafe fn get_userdata_ref<'a, T>(state: *mut ffi::lua_State) -> Result> { + (*get_userdata::>(state, -1)).try_borrow() +} + +#[inline] +unsafe fn get_userdata_mut<'a, T>(state: *mut ffi::lua_State) -> Result> { + (*get_userdata::>(state, -1)).try_borrow_mut() +} + +macro_rules! lua_userdata_impl { + ($type:ty) => { + impl UserData for $type { + fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) { + let mut orig_fields = StaticUserDataFields::default(); + T::add_fields(&mut orig_fields); + for (name, callback) in orig_fields.field_getters { + fields.add_field_getter(name, callback); + } + for (name, callback) in orig_fields.field_setters { + fields.add_field_setter(name, callback); + } + } + + fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { + let mut orig_methods = StaticUserDataMethods::default(); + T::add_methods(&mut orig_methods); + for (name, callback) in orig_methods.methods { + methods.add_callback(name, callback); + } + #[cfg(feature = "async")] + for (name, callback) in orig_methods.async_methods { + methods.add_async_callback(name, callback); + } + for (meta, callback) in orig_methods.meta_methods { + methods.add_meta_callback(meta, callback); + } + #[cfg(feature = "async")] + for (meta, callback) in orig_methods.async_meta_methods { + methods.add_async_meta_callback(meta, callback); + } + } + } + }; +} + +#[cfg(not(feature = "send"))] +lua_userdata_impl!(Rc>); +lua_userdata_impl!(Arc>); +lua_userdata_impl!(Arc>);