Allow registering and creating custom userdata types that don't necessary implement the `UserData` trait.
This is useful to register 3rd party types that cannot implement `UserData` due to Rust orphan rules. See #206
This commit is contained in:
parent
8339621f9c
commit
47c8300ccf
137
src/lua.rs
137
src/lua.rs
|
@ -30,7 +30,7 @@ use crate::types::{
|
|||
Number, RegistryKey,
|
||||
};
|
||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataCell};
|
||||
use crate::userdata_impl::{StaticUserDataFields, StaticUserDataMethods, UserDataProxy};
|
||||
use crate::userdata_impl::{UserDataProxy, UserDataRegistrar};
|
||||
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,
|
||||
|
@ -1732,18 +1732,18 @@ impl Lua {
|
|||
false
|
||||
}
|
||||
|
||||
/// Create a Lua userdata object from a custom userdata type.
|
||||
/// Creates a Lua userdata object from a custom userdata type.
|
||||
///
|
||||
/// All userdata instances of type `T` shares the same metatable.
|
||||
/// All userdata instances of the same type `T` shares the same metatable.
|
||||
#[inline]
|
||||
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: 'static + MaybeSend + UserData,
|
||||
T: UserData + MaybeSend + 'static,
|
||||
{
|
||||
unsafe { self.make_userdata(UserDataCell::new(data)) }
|
||||
}
|
||||
|
||||
/// Create a Lua userdata object from a custom serializable userdata type.
|
||||
/// Creates a Lua userdata object from a custom serializable userdata type.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
#[cfg(feature = "serialize")]
|
||||
|
@ -1751,11 +1751,60 @@ impl Lua {
|
|||
#[inline]
|
||||
pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: 'static + MaybeSend + UserData + Serialize,
|
||||
T: UserData + Serialize + MaybeSend + 'static,
|
||||
{
|
||||
unsafe { self.make_userdata(UserDataCell::new_ser(data)) }
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a custom Rust type.
|
||||
///
|
||||
/// You can register the type using [`Lua::register_userdata_type()`] to add fields or methods
|
||||
/// _before_ calling this method.
|
||||
/// Otherwise, the userdata object will have an empty metatable.
|
||||
///
|
||||
/// All userdata instances of the same type `T` shares the same metatable.
|
||||
pub fn create_any_userdata<T>(&self, data: T) -> Result<AnyUserData>
|
||||
where
|
||||
T: MaybeSend + 'static,
|
||||
{
|
||||
unsafe {
|
||||
self.make_userdata_with_metatable(UserDataCell::new(data), || {
|
||||
// Check if userdata/metatable is already registered
|
||||
let type_id = TypeId::of::<T>();
|
||||
if let Some(&table_id) = (*self.extra.get()).registered_userdata.get(&type_id) {
|
||||
return Ok(table_id as Integer);
|
||||
}
|
||||
|
||||
// Create empty metatable
|
||||
let registry = UserDataRegistrar::new();
|
||||
self.register_userdata_metatable::<T>(registry)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers a custom Rust type in Lua to use in userdata objects.
|
||||
///
|
||||
/// This methods provides a way to add fields or methods to userdata objects of a type `T`.
|
||||
pub fn register_userdata_type<T: 'static>(
|
||||
&self,
|
||||
f: impl FnOnce(&mut UserDataRegistrar<T>),
|
||||
) -> Result<()> {
|
||||
let mut registry = UserDataRegistrar::new();
|
||||
f(&mut registry);
|
||||
|
||||
unsafe {
|
||||
// Deregister the type if it already registered
|
||||
let type_id = TypeId::of::<T>();
|
||||
if let Some(&table_id) = (*self.extra.get()).registered_userdata.get(&type_id) {
|
||||
ffi::luaL_unref(self.state(), ffi::LUA_REGISTRYINDEX, table_id);
|
||||
}
|
||||
|
||||
// Register the type
|
||||
self.register_userdata_metatable(registry)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a Lua userdata "proxy" object from a custom userdata type.
|
||||
///
|
||||
/// Proxy object is an empty userdata object that has `T` metatable attached.
|
||||
|
@ -2490,38 +2539,29 @@ impl Lua {
|
|||
LuaRef::new(self, index)
|
||||
}
|
||||
|
||||
unsafe fn push_userdata_metatable<T: UserData + 'static>(&self) -> Result<()> {
|
||||
unsafe fn register_userdata_metatable<'lua, T: 'static>(
|
||||
&'lua self,
|
||||
registry: UserDataRegistrar<'lua, T>,
|
||||
) -> Result<Integer> {
|
||||
let state = self.state();
|
||||
|
||||
let type_id = TypeId::of::<T>();
|
||||
if let Some(&table_id) = (*self.extra.get()).registered_userdata.get(&type_id) {
|
||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, table_id as Integer);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let _sg = StackGuard::new_extra(state, 1);
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 13)?;
|
||||
|
||||
let mut fields = StaticUserDataFields::default();
|
||||
let mut methods = StaticUserDataMethods::default();
|
||||
T::add_fields(&mut fields);
|
||||
T::add_methods(&mut methods);
|
||||
|
||||
// Prepare metatable, add meta methods first and then meta fields
|
||||
let metatable_nrec = methods.meta_methods.len() + fields.meta_fields.len();
|
||||
let metatable_nrec = registry.meta_methods.len() + registry.meta_fields.len();
|
||||
#[cfg(feature = "async")]
|
||||
let metatable_nrec = metatable_nrec + methods.async_meta_methods.len();
|
||||
let metatable_nrec = metatable_nrec + registry.async_meta_methods.len();
|
||||
push_table(state, 0, metatable_nrec as c_int, true)?;
|
||||
for (k, m) in methods.meta_methods {
|
||||
for (k, m) in registry.meta_methods {
|
||||
self.push_value(Value::Function(self.create_callback(m)?))?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
#[cfg(feature = "async")]
|
||||
for (k, m) in methods.async_meta_methods {
|
||||
for (k, m) in registry.async_meta_methods {
|
||||
self.push_value(Value::Function(self.create_async_callback(m)?))?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
for (k, f) in fields.meta_fields {
|
||||
for (k, f) in registry.meta_fields {
|
||||
self.push_value(f(self)?)?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
|
@ -2530,10 +2570,10 @@ impl Lua {
|
|||
let mut extra_tables_count = 0;
|
||||
|
||||
let mut field_getters_index = None;
|
||||
let field_getters_nrec = fields.field_getters.len();
|
||||
let field_getters_nrec = registry.field_getters.len();
|
||||
if field_getters_nrec > 0 {
|
||||
push_table(state, 0, field_getters_nrec as c_int, true)?;
|
||||
for (k, m) in fields.field_getters {
|
||||
for (k, m) in registry.field_getters {
|
||||
self.push_value(Value::Function(self.create_callback(m)?))?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
|
@ -2542,10 +2582,10 @@ impl Lua {
|
|||
}
|
||||
|
||||
let mut field_setters_index = None;
|
||||
let field_setters_nrec = fields.field_setters.len();
|
||||
let field_setters_nrec = registry.field_setters.len();
|
||||
if field_setters_nrec > 0 {
|
||||
push_table(state, 0, field_setters_nrec as c_int, true)?;
|
||||
for (k, m) in fields.field_setters {
|
||||
for (k, m) in registry.field_setters {
|
||||
self.push_value(Value::Function(self.create_callback(m)?))?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
|
@ -2554,17 +2594,17 @@ impl Lua {
|
|||
}
|
||||
|
||||
let mut methods_index = None;
|
||||
let methods_nrec = methods.methods.len();
|
||||
let methods_nrec = registry.methods.len();
|
||||
#[cfg(feature = "async")]
|
||||
let methods_nrec = methods_nrec + methods.async_methods.len();
|
||||
let methods_nrec = methods_nrec + registry.async_methods.len();
|
||||
if methods_nrec > 0 {
|
||||
push_table(state, 0, methods_nrec as c_int, true)?;
|
||||
for (k, m) in methods.methods {
|
||||
for (k, m) in registry.methods {
|
||||
self.push_value(Value::Function(self.create_callback(m)?))?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
#[cfg(feature = "async")]
|
||||
for (k, m) in methods.async_methods {
|
||||
for (k, m) in registry.async_methods {
|
||||
self.push_value(Value::Function(self.create_async_callback(m)?))?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
|
@ -2584,21 +2624,21 @@ impl Lua {
|
|||
ffi::lua_pop(state, extra_tables_count);
|
||||
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pushvalue(state, -1);
|
||||
let id = protect_lua!(state, 1, 0, |state| {
|
||||
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
|
||||
})?;
|
||||
|
||||
let type_id = TypeId::of::<T>();
|
||||
(*self.extra.get()).registered_userdata.insert(type_id, id);
|
||||
(*self.extra.get())
|
||||
.registered_userdata_mt
|
||||
.insert(mt_ptr, Some(type_id));
|
||||
|
||||
Ok(())
|
||||
Ok(id as Integer)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) unsafe fn register_userdata_metatable(
|
||||
pub(crate) unsafe fn register_raw_userdata_metatable(
|
||||
&self,
|
||||
ptr: *const c_void,
|
||||
type_id: Option<TypeId>,
|
||||
|
@ -2609,7 +2649,7 @@ impl Lua {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) unsafe fn deregister_userdata_metatable(&self, ptr: *const c_void) {
|
||||
pub(crate) unsafe fn deregister_raw_userdata_metatable(&self, ptr: *const c_void) {
|
||||
(*self.extra.get()).registered_userdata_mt.remove(&ptr);
|
||||
}
|
||||
|
||||
|
@ -2905,13 +2945,34 @@ impl Lua {
|
|||
where
|
||||
T: UserData + 'static,
|
||||
{
|
||||
self.make_userdata_with_metatable(data, || {
|
||||
// Check if userdata/metatable is already registered
|
||||
let type_id = TypeId::of::<T>();
|
||||
if let Some(&table_id) = (*self.extra.get()).registered_userdata.get(&type_id) {
|
||||
return Ok(table_id as Integer);
|
||||
}
|
||||
|
||||
// Create new metatable from UserData definition
|
||||
let mut registry = UserDataRegistrar::new();
|
||||
T::add_fields(&mut registry);
|
||||
T::add_methods(&mut registry);
|
||||
|
||||
self.register_userdata_metatable(registry)
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn make_userdata_with_metatable<T>(
|
||||
&self,
|
||||
data: UserDataCell<T>,
|
||||
get_metatable_id: impl FnOnce() -> Result<Integer>,
|
||||
) -> Result<AnyUserData> {
|
||||
let state = self.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
// We push metatable first to ensure having correct metatable with `__gc` method
|
||||
ffi::lua_pushnil(state);
|
||||
self.push_userdata_metatable::<T>()?;
|
||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, get_metatable_id()?);
|
||||
let protect = !self.unlikely_memory_error();
|
||||
#[cfg(not(feature = "lua54"))]
|
||||
push_userdata(state, data, protect)?;
|
||||
|
|
|
@ -435,7 +435,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||
std::ptr::write(ud_ptr as _, UserDataCell::new(data));
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
let ud = AnyUserData(lua.pop_ref());
|
||||
lua.register_userdata_metatable(mt_ptr, None);
|
||||
lua.register_raw_userdata_metatable(mt_ptr, None);
|
||||
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
let newtable = lua.create_table()?;
|
||||
|
@ -453,7 +453,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||
ffi::lua_getmetatable(state, -1);
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
ud.lua.deregister_userdata_metatable(mt_ptr);
|
||||
ud.lua.deregister_raw_userdata_metatable(mt_ptr);
|
||||
|
||||
// Clear associated user values
|
||||
#[cfg(feature = "lua54")]
|
||||
|
|
|
@ -234,7 +234,7 @@ impl AsRef<str> for MetaMethod {
|
|||
/// Method registry for [`UserData`] implementors.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
pub trait UserDataMethods<'lua, T: UserData> {
|
||||
pub trait UserDataMethods<'lua, T> {
|
||||
/// Add a regular method which accepts a `&T` as the first parameter.
|
||||
///
|
||||
/// Regular methods are implemented by overriding the `__index` metamethod and returning the
|
||||
|
@ -427,7 +427,7 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
|||
/// Field registry for [`UserData`] implementors.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
pub trait UserDataFields<'lua, T: UserData> {
|
||||
pub trait UserDataFields<'lua, T> {
|
||||
/// Add a regular field getter as a method which accepts a `&T` as the parameter.
|
||||
///
|
||||
/// Regular field getters are implemented by overriding the `__index` metamethod and returning the
|
||||
|
@ -503,8 +503,8 @@ pub trait UserDataFields<'lua, T: UserData> {
|
|||
/// Trait for custom userdata types.
|
||||
///
|
||||
/// By implementing this trait, a struct becomes eligible for use inside Lua code.
|
||||
/// Implementation of [`IntoLua`] is automatically provided, [`FromLua`] is implemented
|
||||
/// only for `T: UserData + Clone`.
|
||||
/// Implementation of [`IntoLua`] is automatically provided, [`FromLua`] needs to be implemented
|
||||
/// manually.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -725,7 +725,7 @@ impl OwnedAnyUserData {
|
|||
|
||||
impl<'lua> AnyUserData<'lua> {
|
||||
/// Checks whether the type of this userdata is `T`.
|
||||
pub fn is<T: UserData + 'static>(&self) -> bool {
|
||||
pub fn is<T: 'static>(&self) -> bool {
|
||||
match self.inspect(|_: &UserDataCell<T>| Ok(())) {
|
||||
Ok(()) => true,
|
||||
Err(Error::UserDataTypeMismatch) => false,
|
||||
|
@ -740,7 +740,7 @@ impl<'lua> AnyUserData<'lua> {
|
|||
/// Returns a `UserDataBorrowError` if the userdata is already mutably borrowed. Returns a
|
||||
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
#[inline]
|
||||
pub fn borrow<T: UserData + 'static>(&self) -> Result<Ref<T>> {
|
||||
pub fn borrow<T: 'static>(&self) -> Result<Ref<T>> {
|
||||
self.inspect(|cell| cell.try_borrow())
|
||||
}
|
||||
|
||||
|
@ -751,7 +751,7 @@ impl<'lua> AnyUserData<'lua> {
|
|||
/// Returns a `UserDataBorrowMutError` if the userdata cannot be mutably borrowed.
|
||||
/// Returns a `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
#[inline]
|
||||
pub fn borrow_mut<T: UserData + 'static>(&self) -> Result<RefMut<T>> {
|
||||
pub fn borrow_mut<T: 'static>(&self) -> Result<RefMut<T>> {
|
||||
self.inspect(|cell| cell.try_borrow_mut())
|
||||
}
|
||||
|
||||
|
@ -759,7 +759,7 @@ impl<'lua> AnyUserData<'lua> {
|
|||
/// Sets the special "destructed" metatable that prevents any further operations with this userdata.
|
||||
///
|
||||
/// Keeps associated user values unchanged (they will be collected by Lua's GC).
|
||||
pub fn take<T: UserData + 'static>(&self) -> Result<T> {
|
||||
pub fn take<T: 'static>(&self) -> Result<T> {
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
|
@ -980,7 +980,7 @@ impl<'lua> AnyUserData<'lua> {
|
|||
/// Returned [`UserDataMetatable`] object wraps the original metatable and
|
||||
/// provides safe access to its methods.
|
||||
///
|
||||
/// For `T: UserData + 'static` returned metatable is shared among all instances of type `T`.
|
||||
/// For `T: 'static` returned metatable is shared among all instances of type `T`.
|
||||
///
|
||||
/// [`UserDataMetatable`]: crate::UserDataMetatable
|
||||
#[inline]
|
||||
|
@ -1052,7 +1052,7 @@ impl<'lua> AnyUserData<'lua> {
|
|||
|
||||
fn inspect<'a, T, F, R>(&'a self, func: F) -> Result<R>
|
||||
where
|
||||
T: UserData + 'static,
|
||||
T: 'static,
|
||||
F: FnOnce(&'a UserDataCell<T>) -> Result<R>,
|
||||
{
|
||||
let lua = self.0.lua;
|
||||
|
|
|
@ -23,19 +23,33 @@ use {
|
|||
std::future::Future,
|
||||
};
|
||||
|
||||
pub(crate) struct StaticUserDataMethods<'lua, T: UserData + 'static> {
|
||||
pub struct UserDataRegistrar<'lua, T: 'static> {
|
||||
// Fields
|
||||
pub(crate) field_getters: Vec<(String, Callback<'lua, 'static>)>,
|
||||
pub(crate) field_setters: Vec<(String, Callback<'lua, 'static>)>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) meta_fields: Vec<(
|
||||
String,
|
||||
Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>> + 'static>,
|
||||
)>,
|
||||
|
||||
// Methods
|
||||
pub(crate) methods: Vec<(String, Callback<'lua, 'static>)>,
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) async_methods: Vec<(String, AsyncCallback<'lua, 'static>)>,
|
||||
pub(crate) meta_methods: Vec<(String, Callback<'lua, 'static>)>,
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) async_meta_methods: Vec<(String, AsyncCallback<'lua, 'static>)>,
|
||||
|
||||
_type: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData + 'static> Default for StaticUserDataMethods<'lua, T> {
|
||||
fn default() -> StaticUserDataMethods<'lua, T> {
|
||||
StaticUserDataMethods {
|
||||
impl<'lua, T: 'static> UserDataRegistrar<'lua, T> {
|
||||
pub(crate) const fn new() -> Self {
|
||||
UserDataRegistrar {
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_methods: Vec::new(),
|
||||
|
@ -45,161 +59,7 @@ impl<'lua, T: UserData + 'static> Default for StaticUserDataMethods<'lua, T> {
|
|||
_type: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData + 'static> UserDataMethods<'lua, T> for StaticUserDataMethods<'lua, T> {
|
||||
fn add_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.methods
|
||||
.push((name.as_ref().into(), Self::box_method(method)));
|
||||
}
|
||||
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.methods
|
||||
.push((name.as_ref().into(), Self::box_method_mut(method)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
T: Clone,
|
||||
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
MR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.async_methods
|
||||
.push((name.as_ref().into(), Self::box_async_method(method)));
|
||||
}
|
||||
|
||||
fn add_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.methods
|
||||
.push((name.as_ref().into(), Self::box_function(function)));
|
||||
}
|
||||
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.methods
|
||||
.push((name.as_ref().into(), Self::box_function_mut(function)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.async_methods
|
||||
.push((name.as_ref().into(), Self::box_async_function(function)));
|
||||
}
|
||||
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.meta_methods
|
||||
.push((name.as_ref().into(), Self::box_method(method)));
|
||||
}
|
||||
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.meta_methods
|
||||
.push((name.as_ref().into(), Self::box_method_mut(method)));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
T: Clone,
|
||||
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
MR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.async_meta_methods
|
||||
.push((name.as_ref().into(), Self::box_async_method(method)));
|
||||
}
|
||||
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.meta_methods
|
||||
.push((name.as_ref().into(), Self::box_function(function)));
|
||||
}
|
||||
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.meta_methods
|
||||
.push((name.as_ref().into(), Self::box_function_mut(function)));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.async_meta_methods
|
||||
.push((name.as_ref().into(), Self::box_async_function(function)));
|
||||
}
|
||||
|
||||
// Below are internal methods used in generated code
|
||||
|
||||
fn add_callback(&mut self, name: String, callback: Callback<'lua, 'static>) {
|
||||
self.methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_callback(&mut self, name: String, callback: AsyncCallback<'lua, 'static>) {
|
||||
self.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_callback(&mut self, name: String, callback: Callback<'lua, 'static>) {
|
||||
self.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_meta_callback(&mut self, meta: String, callback: AsyncCallback<'lua, 'static>) {
|
||||
self.async_meta_methods.push((meta, callback))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData + 'static> StaticUserDataMethods<'lua, T> {
|
||||
fn box_method<M, A, R>(method: M) -> Callback<'lua, 'static>
|
||||
where
|
||||
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
|
@ -446,35 +306,13 @@ impl<'lua, T: UserData + 'static> StaticUserDataMethods<'lua, T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct StaticUserDataFields<'lua, T: UserData + 'static> {
|
||||
pub(crate) field_getters: Vec<(String, Callback<'lua, 'static>)>,
|
||||
pub(crate) field_setters: Vec<(String, Callback<'lua, 'static>)>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(crate) meta_fields: Vec<(
|
||||
String,
|
||||
Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>> + 'static>,
|
||||
)>,
|
||||
_type: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData + 'static> 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: UserData + 'static> UserDataFields<'lua, T> for StaticUserDataFields<'lua, T> {
|
||||
impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistrar<'lua, T> {
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let method = StaticUserDataMethods::box_method(move |lua, data, ()| method(lua, data));
|
||||
let method = Self::box_method(move |lua, data, ()| method(lua, data));
|
||||
self.field_getters.push((name.as_ref().into(), method));
|
||||
}
|
||||
|
||||
|
@ -483,7 +321,7 @@ impl<'lua, T: UserData + 'static> UserDataFields<'lua, T> for StaticUserDataFiel
|
|||
M: FnMut(&'lua Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>,
|
||||
{
|
||||
let method = StaticUserDataMethods::box_method_mut(method);
|
||||
let method = Self::box_method_mut(method);
|
||||
self.field_setters.push((name.as_ref().into(), method));
|
||||
}
|
||||
|
||||
|
@ -492,7 +330,7 @@ impl<'lua, T: UserData + 'static> UserDataFields<'lua, T> for StaticUserDataFiel
|
|||
F: Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let func = StaticUserDataMethods::<T>::box_function(function);
|
||||
let func = Self::box_function(function);
|
||||
self.field_getters.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
|
@ -501,9 +339,7 @@ impl<'lua, T: UserData + 'static> UserDataFields<'lua, T> for StaticUserDataFiel
|
|||
F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>,
|
||||
{
|
||||
let func = StaticUserDataMethods::<T>::box_function_mut(move |lua, (data, val)| {
|
||||
function(lua, data, val)
|
||||
});
|
||||
let func = Self::box_function_mut(move |lua, (data, val)| function(lua, data, val));
|
||||
self.field_setters.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
|
@ -545,6 +381,158 @@ impl<'lua, T: UserData + 'static> UserDataFields<'lua, T> for StaticUserDataFiel
|
|||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
|
||||
fn add_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.methods
|
||||
.push((name.as_ref().into(), Self::box_method(method)));
|
||||
}
|
||||
|
||||
fn add_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.methods
|
||||
.push((name.as_ref().into(), Self::box_method_mut(method)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_method<M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
T: Clone,
|
||||
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
MR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.async_methods
|
||||
.push((name.as_ref().into(), Self::box_async_method(method)));
|
||||
}
|
||||
|
||||
fn add_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.methods
|
||||
.push((name.as_ref().into(), Self::box_function(function)));
|
||||
}
|
||||
|
||||
fn add_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.methods
|
||||
.push((name.as_ref().into(), Self::box_function_mut(function)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_function<F, A, FR, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.async_methods
|
||||
.push((name.as_ref().into(), Self::box_async_function(function)));
|
||||
}
|
||||
|
||||
fn add_meta_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.meta_methods
|
||||
.push((name.as_ref().into(), Self::box_method(method)));
|
||||
}
|
||||
|
||||
fn add_meta_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.meta_methods
|
||||
.push((name.as_ref().into(), Self::box_method_mut(method)));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
|
||||
where
|
||||
T: Clone,
|
||||
M: Fn(&'lua Lua, T, A) -> MR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
MR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.async_meta_methods
|
||||
.push((name.as_ref().into(), Self::box_async_method(method)));
|
||||
}
|
||||
|
||||
fn add_meta_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.meta_methods
|
||||
.push((name.as_ref().into(), Self::box_function(function)));
|
||||
}
|
||||
|
||||
fn add_meta_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.meta_methods
|
||||
.push((name.as_ref().into(), Self::box_function_mut(function)));
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
||||
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl AsRef<str>, function: F)
|
||||
where
|
||||
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
|
||||
A: FromLuaMulti<'lua>,
|
||||
FR: Future<Output = Result<R>> + 'lua,
|
||||
R: IntoLuaMulti<'lua>,
|
||||
{
|
||||
self.async_meta_methods
|
||||
.push((name.as_ref().into(), Self::box_async_function(function)));
|
||||
}
|
||||
|
||||
// Below are internal methods used in generated code
|
||||
|
||||
fn add_callback(&mut self, name: String, callback: Callback<'lua, 'static>) {
|
||||
self.methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_callback(&mut self, name: String, callback: AsyncCallback<'lua, 'static>) {
|
||||
self.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_callback(&mut self, name: String, callback: Callback<'lua, 'static>) {
|
||||
self.meta_methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_meta_callback(&mut self, meta: String, callback: AsyncCallback<'lua, 'static>) {
|
||||
self.async_meta_methods.push((meta, callback))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn get_userdata_ref<'a, T>(state: *mut ffi::lua_State) -> Result<Ref<'a, T>> {
|
||||
(*get_userdata::<UserDataCell<T>>(state, -1)).try_borrow()
|
||||
|
@ -559,7 +547,7 @@ macro_rules! lua_userdata_impl {
|
|||
($type:ty) => {
|
||||
impl<T: UserData + 'static> UserData for $type {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
let mut orig_fields = StaticUserDataFields::default();
|
||||
let mut orig_fields = UserDataRegistrar::new();
|
||||
T::add_fields(&mut orig_fields);
|
||||
for (name, callback) in orig_fields.field_getters {
|
||||
fields.add_field_getter(name, callback);
|
||||
|
@ -570,7 +558,7 @@ macro_rules! lua_userdata_impl {
|
|||
}
|
||||
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
let mut orig_methods = StaticUserDataMethods::default();
|
||||
let mut orig_methods = UserDataRegistrar::new();
|
||||
T::add_methods(&mut orig_methods);
|
||||
for (name, callback) in orig_methods.methods {
|
||||
methods.add_callback(name, callback);
|
||||
|
|
21
src/util.rs
21
src/util.rs
|
@ -47,7 +47,6 @@ pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<(
|
|||
pub struct StackGuard {
|
||||
state: *mut ffi::lua_State,
|
||||
top: c_int,
|
||||
extra: c_int,
|
||||
}
|
||||
|
||||
impl StackGuard {
|
||||
|
@ -59,17 +58,6 @@ impl StackGuard {
|
|||
StackGuard {
|
||||
state,
|
||||
top: ffi::lua_gettop(state),
|
||||
extra: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Similar to `new`, but checks and keeps `extra` elements from top of the stack on Drop.
|
||||
#[inline]
|
||||
pub unsafe fn new_extra(state: *mut ffi::lua_State, extra: c_int) -> StackGuard {
|
||||
StackGuard {
|
||||
state,
|
||||
top: ffi::lua_gettop(state),
|
||||
extra,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -78,14 +66,11 @@ impl Drop for StackGuard {
|
|||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let top = ffi::lua_gettop(self.state);
|
||||
if top < self.top + self.extra {
|
||||
if top < self.top {
|
||||
mlua_panic!("{} too many stack values popped", self.top - top)
|
||||
}
|
||||
if top > self.top + self.extra {
|
||||
if self.extra > 0 {
|
||||
ffi::lua_rotate(self.state, self.top + 1, self.extra);
|
||||
}
|
||||
ffi::lua_settop(self.state, self.top + self.extra);
|
||||
if top > self.top {
|
||||
ffi::lua_settop(self.state, self.top);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
#[cfg(not(feature = "parking_lot"))]
|
||||
use std::sync::{Mutex, RwLock};
|
||||
|
@ -17,7 +18,7 @@ use mlua::{
|
|||
};
|
||||
|
||||
#[test]
|
||||
fn test_user_data() -> Result<()> {
|
||||
fn test_userdata() -> Result<()> {
|
||||
struct UserData1(i64);
|
||||
struct UserData2(Box<i64>);
|
||||
|
||||
|
@ -710,3 +711,32 @@ fn test_userdata_proxy() -> Result<()> {
|
|||
)
|
||||
.exec()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_any_userdata() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.register_userdata_type::<StdString>(|reg| {
|
||||
reg.add_method("get", |_, this, ()| Ok(this.clone()));
|
||||
reg.add_method_mut("concat", |_, this, s: String| {
|
||||
this.push_str(&s.to_string_lossy());
|
||||
Ok(())
|
||||
});
|
||||
})?;
|
||||
|
||||
let ud = lua.create_any_userdata("hello".to_string())?;
|
||||
assert_eq!(&*ud.borrow::<StdString>()?, "hello");
|
||||
|
||||
lua.globals().set("ud", ud)?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(ud:get() == "hello")
|
||||
ud:concat(", world")
|
||||
assert(ud:get() == "hello, world")
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue