Add `UserDataFields::add_field()` method to add static fields to UserData.
Plus `UserDataFields::add_meta_field()` for static meta fields. Fix propagating fields to wrapped UserData types.
This commit is contained in:
parent
e7b712e29f
commit
5a135a331a
60
src/lua.rs
60
src/lua.rs
|
@ -1857,7 +1857,10 @@ impl Lua {
|
|||
pub fn scope<'lua, 'scope, R>(
|
||||
&'lua self,
|
||||
f: impl FnOnce(&Scope<'lua, 'scope>) -> Result<R>,
|
||||
) -> Result<R> {
|
||||
) -> Result<R>
|
||||
where
|
||||
'lua: 'scope,
|
||||
{
|
||||
f(&Scope::new(self))
|
||||
}
|
||||
|
||||
|
@ -2487,7 +2490,7 @@ impl Lua {
|
|||
|
||||
unsafe fn register_userdata_metatable<'lua, T: 'static>(
|
||||
&'lua self,
|
||||
registry: UserDataRegistrar<'lua, T>,
|
||||
mut registry: UserDataRegistrar<'lua, T>,
|
||||
) -> Result<Integer> {
|
||||
let state = self.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
|
@ -2510,7 +2513,7 @@ impl Lua {
|
|||
let mut has_name = false;
|
||||
for (k, f) in registry.meta_fields {
|
||||
has_name = has_name || k == "__name";
|
||||
self.push_value(f(self)?)?;
|
||||
self.push_value(f(self, MultiValue::new())?.pop_front().unwrap())?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
// Set `__name` if not provided
|
||||
|
@ -2523,6 +2526,32 @@ impl Lua {
|
|||
|
||||
let mut extra_tables_count = 0;
|
||||
|
||||
let fields_nrec = registry.fields.len();
|
||||
if fields_nrec > 0 {
|
||||
// If __index is a table then update it inplace
|
||||
let index_type = ffi::lua_getfield(state, metatable_index, cstr!("__index"));
|
||||
match index_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE => {
|
||||
if index_type == ffi::LUA_TNIL {
|
||||
// Create a new table
|
||||
ffi::lua_pop(state, 1);
|
||||
push_table(state, 0, fields_nrec as c_int, true)?;
|
||||
}
|
||||
for (k, f) in registry.fields {
|
||||
self.push_value(f(self, MultiValue::new())?.pop_front().unwrap())?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
rawset_field(state, metatable_index, "__index")?;
|
||||
}
|
||||
_ => {
|
||||
// Propagate fields to the field getters
|
||||
for (k, f) in registry.fields {
|
||||
registry.field_getters.push((k, f))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut field_getters_index = None;
|
||||
let field_getters_nrec = registry.field_getters.len();
|
||||
if field_getters_nrec > 0 {
|
||||
|
@ -2552,7 +2581,16 @@ impl Lua {
|
|||
#[cfg(feature = "async")]
|
||||
let methods_nrec = methods_nrec + registry.async_methods.len();
|
||||
if methods_nrec > 0 {
|
||||
push_table(state, 0, methods_nrec as c_int, true)?;
|
||||
// If __index is a table then update it inplace
|
||||
let index_type = ffi::lua_getfield(state, metatable_index, cstr!("__index"));
|
||||
match index_type {
|
||||
ffi::LUA_TTABLE => {} // Update the existing table
|
||||
_ => {
|
||||
// Create a new table
|
||||
ffi::lua_pop(state, 1);
|
||||
push_table(state, 0, methods_nrec as c_int, true)?;
|
||||
}
|
||||
}
|
||||
for (k, m) in registry.methods {
|
||||
self.push_value(Value::Function(self.create_callback(m)?))?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
|
@ -2562,8 +2600,18 @@ impl Lua {
|
|||
self.push_value(Value::Function(self.create_async_callback(m)?))?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
methods_index = Some(ffi::lua_absindex(state, -1));
|
||||
extra_tables_count += 1;
|
||||
match index_type {
|
||||
ffi::LUA_TTABLE => {
|
||||
ffi::lua_pop(state, 1); // All done
|
||||
}
|
||||
ffi::LUA_TNIL => {
|
||||
rawset_field(state, metatable_index, "__index")?; // Set the new table as __index
|
||||
}
|
||||
_ => {
|
||||
methods_index = Some(ffi::lua_absindex(state, -1));
|
||||
extra_tables_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init_userdata_metatable::<UserDataCell<T>>(
|
||||
|
|
58
src/scope.rs
58
src/scope.rs
|
@ -14,6 +14,7 @@ use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend};
|
|||
use crate::userdata::{
|
||||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
use crate::userdata_impl::UserDataRegistrar;
|
||||
use crate::util::{
|
||||
assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table, rawset_field,
|
||||
take_userdata, StackGuard,
|
||||
|
@ -32,7 +33,10 @@ use std::future::Future;
|
|||
/// See [`Lua::scope`] for more details.
|
||||
///
|
||||
/// [`Lua::scope`]: crate::Lua.html::scope
|
||||
pub struct Scope<'lua, 'scope> {
|
||||
pub struct Scope<'lua, 'scope>
|
||||
where
|
||||
'lua: 'scope,
|
||||
{
|
||||
lua: &'lua Lua,
|
||||
destructors: RefCell<Vec<(LuaRef<'lua>, DestructorCallback<'lua>)>>,
|
||||
_scope_invariant: PhantomData<Cell<&'scope ()>>,
|
||||
|
@ -393,7 +397,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
for (k, f) in ud_fields.meta_fields {
|
||||
lua.push_value(f(mem::transmute(lua))?)?;
|
||||
lua.push_value(f(lua, MultiValue::new())?.pop_front().unwrap())?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
let metatable_index = ffi::lua_absindex(state, -1);
|
||||
|
@ -734,15 +738,16 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
|
|||
}
|
||||
|
||||
struct NonStaticUserDataFields<'lua, T: UserData> {
|
||||
fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
field_getters: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
field_setters: Vec<(String, NonStaticMethod<'lua, T>)>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
meta_fields: Vec<(String, Box<dyn Fn(&'lua Lua) -> Result<Value<'lua>>>)>,
|
||||
meta_fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
}
|
||||
|
||||
impl<'lua, T: UserData> Default for NonStaticUserDataFields<'lua, T> {
|
||||
fn default() -> NonStaticUserDataFields<'lua, T> {
|
||||
NonStaticUserDataFields {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
|
@ -751,6 +756,17 @@ impl<'lua, T: UserData> Default for NonStaticUserDataFields<'lua, T> {
|
|||
}
|
||||
|
||||
impl<'lua, T: UserData> UserDataFields<'lua, T> for NonStaticUserDataFields<'lua, T> {
|
||||
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
self.fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| value.clone().into_lua_multi(lua)),
|
||||
));
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -796,30 +812,30 @@ impl<'lua, T: UserData> UserDataFields<'lua, T> for NonStaticUserDataFields<'lua
|
|||
self.field_setters.push((name.as_ref().into(), func));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| {
|
||||
UserDataRegistrar::<()>::check_meta_field(lua, &name2, value.clone())
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
|
||||
where
|
||||
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name.clone(),
|
||||
Box::new(move |lua| {
|
||||
let value = f(lua)?.into_lua(lua)?;
|
||||
if name == MetaMethod::Index || name == MetaMethod::NewIndex {
|
||||
match value {
|
||||
Value::Nil | Value::Table(_) | Value::Function(_) => {}
|
||||
_ => {
|
||||
return Err(Error::MetaMethodTypeError {
|
||||
method: name.clone(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(value)
|
||||
}),
|
||||
name,
|
||||
Box::new(move |lua, _| UserDataRegistrar::<()>::check_meta_field(lua, &name2, f(lua)?)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,12 +21,10 @@ use crate::function::Function;
|
|||
use crate::lua::Lua;
|
||||
use crate::string::String;
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::types::{LuaRef, MaybeSend};
|
||||
use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use crate::types::AsyncCallback;
|
||||
use crate::UserDataRegistrar;
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
pub(crate) const USER_VALUE_MAXSLOT: usize = 8;
|
||||
|
@ -412,29 +410,26 @@ pub trait UserDataMethods<'lua, T> {
|
|||
//
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_callback(&mut self, _name: StdString, _callback: Callback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_callback(&mut self, _name: StdString, _callback: AsyncCallback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_meta_callback(&mut self, _name: StdString, _callback: Callback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_meta_callback(
|
||||
&mut self,
|
||||
_name: StdString,
|
||||
_callback: AsyncCallback<'lua, 'static>,
|
||||
) {
|
||||
}
|
||||
fn append_methods_from<S>(&mut self, _other: UserDataRegistrar<'lua, S>) {}
|
||||
}
|
||||
|
||||
/// Field registry for [`UserData`] implementors.
|
||||
///
|
||||
/// [`UserData`]: crate::UserData
|
||||
pub trait UserDataFields<'lua, T> {
|
||||
/// Add a static field to the `UserData`.
|
||||
///
|
||||
/// Static fields are implemented by updating the `__index` metamethod and returning the
|
||||
/// accessed field. This allows them to be used with the expected `userdata.field` syntax.
|
||||
///
|
||||
/// Static fields are usually shared between all instances of the `UserData` of the same type.
|
||||
///
|
||||
/// If `add_meta_method` is used to set the `__index` metamethod, it will
|
||||
/// be used as a fall-back if no regular field or method are found.
|
||||
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static;
|
||||
|
||||
/// 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
|
||||
|
@ -483,9 +478,21 @@ pub trait UserDataFields<'lua, T> {
|
|||
F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static,
|
||||
A: FromLua<'lua>;
|
||||
|
||||
/// Add a metamethod value computed from `f`.
|
||||
/// Add a metatable field.
|
||||
///
|
||||
/// This will initialize the metamethod value from `f` on `UserData` creation.
|
||||
/// This will initialize the metatable field with `value` on `UserData` creation.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
||||
/// like `__gc` or `__metatable`.
|
||||
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static;
|
||||
|
||||
/// Add a metatable field computed from `f`.
|
||||
///
|
||||
/// This will initialize the metatable field from `f` on `UserData` creation.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
|
@ -501,10 +508,7 @@ pub trait UserDataFields<'lua, T> {
|
|||
//
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_field_getter(&mut self, _name: StdString, _callback: Callback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_field_setter(&mut self, _name: StdString, _callback: Callback<'lua, 'static>) {}
|
||||
fn append_fields_from<S>(&mut self, _other: UserDataRegistrar<'lua, S>) {}
|
||||
}
|
||||
|
||||
/// Trait for custom userdata types.
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::userdata::{
|
|||
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
|
||||
};
|
||||
use crate::util::{check_stack, get_userdata, short_type_name, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
|
||||
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Value};
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
use std::rc::Rc;
|
||||
|
@ -25,13 +25,10 @@ use {
|
|||
|
||||
pub struct UserDataRegistrar<'lua, T: 'static> {
|
||||
// Fields
|
||||
pub(crate) fields: Vec<(String, Callback<'lua, '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>,
|
||||
)>,
|
||||
pub(crate) meta_fields: Vec<(String, Callback<'lua, 'static>)>,
|
||||
|
||||
// Methods
|
||||
pub(crate) methods: Vec<(String, Callback<'lua, 'static>)>,
|
||||
|
@ -47,6 +44,7 @@ pub struct UserDataRegistrar<'lua, T: 'static> {
|
|||
impl<'lua, T: 'static> UserDataRegistrar<'lua, T> {
|
||||
pub(crate) const fn new() -> Self {
|
||||
UserDataRegistrar {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
|
@ -360,6 +358,30 @@ impl<'lua, T: 'static> UserDataRegistrar<'lua, T> {
|
|||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn check_meta_field<V>(
|
||||
lua: &'lua Lua,
|
||||
name: &str,
|
||||
value: V,
|
||||
) -> Result<MultiValue<'lua>>
|
||||
where
|
||||
V: IntoLua<'lua>,
|
||||
{
|
||||
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_multi(lua)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns function name for the type `T`, without the module path
|
||||
|
@ -368,6 +390,17 @@ fn get_function_name<T>(name: &str) -> StdString {
|
|||
}
|
||||
|
||||
impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistrar<'lua, T> {
|
||||
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
self.fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| value.clone().into_lua_multi(lua)),
|
||||
));
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -408,41 +441,38 @@ impl<'lua, T: 'static> UserDataFields<'lua, T> for UserDataRegistrar<'lua, T> {
|
|||
self.field_setters.push((name.into(), func));
|
||||
}
|
||||
|
||||
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
|
||||
where
|
||||
V: IntoLua<'lua> + Clone + 'static,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name,
|
||||
Box::new(move |lua, _| Self::check_meta_field(lua, &name2, value.clone())),
|
||||
));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
|
||||
where
|
||||
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
|
||||
R: IntoLua<'lua>,
|
||||
{
|
||||
let name = name.as_ref().to_string();
|
||||
let name2 = name.clone();
|
||||
self.meta_fields.push((
|
||||
name.clone(),
|
||||
Box::new(move |lua| {
|
||||
let value = f(lua)?.into_lua(lua)?;
|
||||
if name == MetaMethod::Index || name == MetaMethod::NewIndex {
|
||||
match value {
|
||||
Value::Nil | Value::Table(_) | Value::Function(_) => {}
|
||||
_ => {
|
||||
return Err(Error::MetaMethodTypeError {
|
||||
method: name.clone(),
|
||||
type_name: value.type_name(),
|
||||
message: Some("expected nil, table or function".to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(value)
|
||||
}),
|
||||
name,
|
||||
Box::new(move |lua, _| Self::check_meta_field(lua, &name2, f(lua)?)),
|
||||
));
|
||||
}
|
||||
|
||||
// Below are internal methods
|
||||
|
||||
fn add_field_getter(&mut self, name: String, callback: Callback<'lua, 'static>) {
|
||||
self.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_setter(&mut self, name: String, callback: Callback<'lua, 'static>) {
|
||||
self.field_setters.push((name, callback));
|
||||
fn append_fields_from<S>(&mut self, other: UserDataRegistrar<'lua, S>) {
|
||||
self.fields.extend(other.fields);
|
||||
self.field_getters.extend(other.field_getters);
|
||||
self.field_setters.extend(other.field_setters);
|
||||
self.meta_fields.extend(other.meta_fields);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -591,22 +621,13 @@ impl<'lua, T: 'static> UserDataMethods<'lua, T> for UserDataRegistrar<'lua, T> {
|
|||
|
||||
// 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))
|
||||
fn append_methods_from<S>(&mut self, other: UserDataRegistrar<'lua, S>) {
|
||||
self.methods.extend(other.methods);
|
||||
#[cfg(feature = "async")]
|
||||
self.async_methods.extend(other.async_methods);
|
||||
self.meta_methods.extend(other.meta_methods);
|
||||
#[cfg(feature = "async")]
|
||||
self.async_meta_methods.extend(other.async_meta_methods);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -626,31 +647,13 @@ macro_rules! lua_userdata_impl {
|
|||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
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);
|
||||
}
|
||||
for (name, callback) in orig_fields.field_setters {
|
||||
fields.add_field_setter(name, callback);
|
||||
}
|
||||
fields.append_fields_from(orig_fields);
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
let mut orig_methods = UserDataRegistrar::new();
|
||||
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);
|
||||
}
|
||||
methods.append_methods_from(orig_methods);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -402,9 +402,12 @@ unsafe extern "C" fn lua_error_impl(state: *mut ffi::lua_State) -> c_int {
|
|||
}
|
||||
|
||||
unsafe extern "C" fn lua_isfunction_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
let t = ffi::lua_type(state, -1);
|
||||
ffi::lua_pop(state, 1);
|
||||
ffi::lua_pushboolean(state, (t == ffi::LUA_TFUNCTION) as c_int);
|
||||
ffi::lua_pushboolean(state, ffi::lua_isfunction(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
unsafe extern "C" fn lua_istable_impl(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushboolean(state, ffi::lua_istable(state, -1));
|
||||
1
|
||||
}
|
||||
|
||||
|
@ -418,14 +421,19 @@ unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()
|
|||
// Create and cache `__index` generator
|
||||
let code = cstr!(
|
||||
r#"
|
||||
local error, isfunction = ...
|
||||
local error, isfunction, istable = ...
|
||||
return function (__index, field_getters, methods)
|
||||
-- Fastpath to return methods table for index access
|
||||
if __index == nil and field_getters == nil then
|
||||
return methods
|
||||
-- Common case: has field getters and index is a table
|
||||
if field_getters ~= nil and methods == nil and istable(__index) then
|
||||
return function (self, key)
|
||||
local field_getter = field_getters[key]
|
||||
if field_getter ~= nil then
|
||||
return field_getter(self)
|
||||
end
|
||||
return __index[key]
|
||||
end
|
||||
end
|
||||
|
||||
-- Alternatively return a function for index access
|
||||
return function (self, key)
|
||||
if field_getters ~= nil then
|
||||
local field_getter = field_getters[key]
|
||||
|
@ -460,7 +468,8 @@ unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()
|
|||
}
|
||||
ffi::lua_pushcfunction(state, lua_error_impl);
|
||||
ffi::lua_pushcfunction(state, lua_isfunction_impl);
|
||||
ffi::lua_call(state, 2, 1);
|
||||
ffi::lua_pushcfunction(state, lua_istable_impl);
|
||||
ffi::lua_call(state, 3, 1);
|
||||
|
||||
#[cfg(feature = "luau-jit")]
|
||||
if ffi::luau_codegen_supported() != 0 {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::collections::HashMap;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
#[cfg(not(feature = "parking_lot"))]
|
||||
|
@ -486,6 +487,7 @@ fn test_fields() -> Result<()> {
|
|||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field("static", "constant");
|
||||
fields.add_field_method_get("val", |_, data| Ok(data.0));
|
||||
fields.add_field_method_set("val", |_, data, val| {
|
||||
data.0 = val;
|
||||
|
@ -497,11 +499,7 @@ fn test_fields() -> Result<()> {
|
|||
fields
|
||||
.add_field_function_set("uval", |_, ud, s| ud.set_user_value::<Option<String>>(s));
|
||||
|
||||
fields.add_meta_field_with(MetaMethod::Index, |lua| {
|
||||
let index = lua.create_table()?;
|
||||
index.set("f", 321)?;
|
||||
Ok(index)
|
||||
});
|
||||
fields.add_meta_field(MetaMethod::Index, HashMap::from([("f", 321)]));
|
||||
fields.add_meta_field_with(MetaMethod::NewIndex, |lua| {
|
||||
lua.create_function(|lua, (_, field, val): (AnyUserData, String, Value)| {
|
||||
lua.globals().set(field, val)?;
|
||||
|
@ -516,6 +514,7 @@ fn test_fields() -> Result<()> {
|
|||
globals.set("ud", MyUserData(7))?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(ud.static == "constant")
|
||||
assert(ud.val == 7)
|
||||
ud.val = 10
|
||||
assert(ud.val == 10)
|
||||
|
@ -614,6 +613,7 @@ fn test_userdata_wrapped() -> Result<()> {
|
|||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field("static", "constant");
|
||||
fields.add_field_method_get("data", |_, this| Ok(this.0));
|
||||
fields.add_field_method_set("data", |_, this, val| {
|
||||
this.0 = val;
|
||||
|
@ -631,6 +631,7 @@ fn test_userdata_wrapped() -> Result<()> {
|
|||
globals.set("rc_refcell_ud", ud1.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(rc_refcell_ud.static == "constant")
|
||||
rc_refcell_ud.data = rc_refcell_ud.data + 1
|
||||
assert(rc_refcell_ud.data == 2)
|
||||
"#,
|
||||
|
@ -646,6 +647,7 @@ fn test_userdata_wrapped() -> Result<()> {
|
|||
globals.set("arc_mutex_ud", ud2.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_mutex_ud.static == "constant")
|
||||
arc_mutex_ud.data = arc_mutex_ud.data + 1
|
||||
assert(arc_mutex_ud.data == 3)
|
||||
"#,
|
||||
|
@ -660,6 +662,7 @@ fn test_userdata_wrapped() -> Result<()> {
|
|||
globals.set("arc_rwlock_ud", ud3.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_rwlock_ud.static == "constant")
|
||||
arc_rwlock_ud.data = arc_rwlock_ud.data + 1
|
||||
assert(arc_rwlock_ud.data == 4)
|
||||
"#,
|
||||
|
@ -686,7 +689,7 @@ fn test_userdata_proxy() -> Result<()> {
|
|||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_function_get("static_field", |_, _| Ok(123));
|
||||
fields.add_field("static_field", 123);
|
||||
fields.add_field_method_get("n", |_, this| Ok(this.0));
|
||||
}
|
||||
|
||||
|
@ -780,10 +783,7 @@ fn test_userdata_ext() -> Result<()> {
|
|||
assert_eq!(ud.get::<_, u32>("n")?, 123);
|
||||
ud.set("n", 321)?;
|
||||
assert_eq!(ud.get::<_, u32>("n")?, 321);
|
||||
match ud.get::<_, u32>("non-existent") {
|
||||
Err(Error::RuntimeError(_)) => {}
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
}
|
||||
assert_eq!(ud.get::<_, Option<u32>>("non-existent")?, None);
|
||||
match ud.set::<_, u32>("non-existent", 123) {
|
||||
Err(Error::RuntimeError(_)) => {}
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
|
|
Loading…
Reference in New Issue