mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Delay "any" userdata metatable creation until first instance
This commit is contained in:
+2
-2
@@ -174,7 +174,7 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
let ud_ptr = util::push_uninit_userdata::<UserDataStorage<T>>(state, protect)?;
|
||||
|
||||
// Push the metatable and register it with no TypeId
|
||||
let mut registry = UserDataRegistry::new_unique(ud_ptr as *mut _);
|
||||
let mut registry = UserDataRegistry::new_unique(self.lua.lua(), ud_ptr as *mut _);
|
||||
T::register(&mut registry);
|
||||
self.lua.push_userdata_metatable(registry.into_raw())?;
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
@@ -222,7 +222,7 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
let ud_ptr = util::push_uninit_userdata::<UserDataStorage<T>>(state, protect)?;
|
||||
|
||||
// Push the metatable and register it with no TypeId
|
||||
let mut registry = UserDataRegistry::new_unique(ud_ptr as *mut _);
|
||||
let mut registry = UserDataRegistry::new_unique(self.lua.lua(), ud_ptr as *mut _);
|
||||
register(&mut registry);
|
||||
self.lua.push_userdata_metatable(registry.into_raw())?;
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
|
||||
+3
-3
@@ -1306,7 +1306,7 @@ impl Lua {
|
||||
/// 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 UserDataRegistry<T>)) -> Result<()> {
|
||||
let type_id = TypeId::of::<T>();
|
||||
let mut registry = UserDataRegistry::new(type_id);
|
||||
let mut registry = UserDataRegistry::new(self, type_id);
|
||||
f(&mut registry);
|
||||
|
||||
let lua = self.lock();
|
||||
@@ -1316,8 +1316,8 @@ impl Lua {
|
||||
ffi::luaL_unref(lua.state(), ffi::LUA_REGISTRYINDEX, table_id);
|
||||
}
|
||||
|
||||
// Register the type
|
||||
lua.create_userdata_metatable(registry.into_raw())?;
|
||||
// Add to "pending" registration map
|
||||
((*lua.extra.get()).pending_userdata_reg).insert(type_id, registry.into_raw());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use crate::error::Result;
|
||||
use crate::state::RawLua;
|
||||
use crate::stdlib::StdLib;
|
||||
use crate::types::{AppData, ReentrantMutex, XRc};
|
||||
use crate::userdata::RawUserDataRegistry;
|
||||
use crate::util::{get_internal_metatable, push_internal_userdata, TypeKey, WrappedFailure};
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -35,6 +36,7 @@ pub(crate) struct ExtraData {
|
||||
pub(super) weak: MaybeUninit<WeakLua>,
|
||||
pub(super) owned: bool,
|
||||
|
||||
pub(super) pending_userdata_reg: FxHashMap<TypeId, RawUserDataRegistry>,
|
||||
pub(super) registered_userdata_t: FxHashMap<TypeId, c_int>,
|
||||
pub(super) registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>,
|
||||
pub(super) last_checked_userdata_mt: (*const c_void, Option<TypeId>),
|
||||
@@ -144,6 +146,7 @@ impl ExtraData {
|
||||
lua: MaybeUninit::uninit(),
|
||||
weak: MaybeUninit::uninit(),
|
||||
owned,
|
||||
pending_userdata_reg: FxHashMap::default(),
|
||||
registered_userdata_t: FxHashMap::default(),
|
||||
registered_userdata_mt: FxHashMap::default(),
|
||||
last_checked_userdata_mt: (ptr::null(), None),
|
||||
|
||||
+13
-10
@@ -756,7 +756,7 @@ impl RawLua {
|
||||
}
|
||||
|
||||
// Create a new metatable from `UserData` definition
|
||||
let mut registry = UserDataRegistry::new(type_id);
|
||||
let mut registry = UserDataRegistry::new(self.lua(), type_id);
|
||||
T::register(&mut registry);
|
||||
|
||||
self.create_userdata_metatable(registry.into_raw())
|
||||
@@ -774,9 +774,12 @@ impl RawLua {
|
||||
return Ok(table_id as Integer);
|
||||
}
|
||||
|
||||
// Create an empty metatable
|
||||
let registry = UserDataRegistry::<T>::new(type_id);
|
||||
self.create_userdata_metatable(registry.into_raw())
|
||||
// Check if metatable creation is pending or create an empty metatable otherwise
|
||||
let registry = match (*self.extra.get()).pending_userdata_reg.remove(&type_id) {
|
||||
Some(registry) => registry,
|
||||
None => UserDataRegistry::<T>::new(self.lua(), type_id).into_raw(),
|
||||
};
|
||||
self.create_userdata_metatable(registry)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -851,9 +854,9 @@ impl RawLua {
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
let mut has_name = false;
|
||||
for (k, push_field) in registry.meta_fields {
|
||||
for (k, v) in registry.meta_fields {
|
||||
has_name = has_name || k == MetaMethod::Type;
|
||||
push_field(self)?;
|
||||
v?.push_into_stack(self)?;
|
||||
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
|
||||
}
|
||||
// Set `__name/__type` if not provided
|
||||
@@ -875,8 +878,8 @@ impl RawLua {
|
||||
ffi::lua_pop(state, 1);
|
||||
push_table(state, 0, fields_nrec, true)?;
|
||||
}
|
||||
for (k, push_field) in mem::take(&mut registry.fields) {
|
||||
push_field(self)?;
|
||||
for (k, v) in mem::take(&mut registry.fields) {
|
||||
v?.push_into_stack(self)?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
rawset_field(state, metatable_index, "__index")?;
|
||||
@@ -896,12 +899,12 @@ impl RawLua {
|
||||
self.push(self.create_callback(m)?)?;
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
for (k, push_field) in registry.fields {
|
||||
for (k, v) in registry.fields {
|
||||
unsafe extern "C-unwind" fn return_field(state: *mut ffi::lua_State) -> c_int {
|
||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
||||
1
|
||||
}
|
||||
push_field(self)?;
|
||||
v?.push_into_stack(self)?;
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
ffi::lua_pushcclosure(state, return_field, 1);
|
||||
})?;
|
||||
|
||||
+24
-28
@@ -7,7 +7,7 @@ use std::os::raw::c_void;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::state::{Lua, RawLua};
|
||||
use crate::state::{Lua, LuaGuard};
|
||||
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
||||
use crate::types::{Callback, MaybeSend};
|
||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataStorage};
|
||||
@@ -26,8 +26,6 @@ use std::rc::Rc;
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
type StaticFieldCallback = Box<dyn FnOnce(&RawLua) -> Result<()> + 'static>;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum UserDataTypeId {
|
||||
Shared(TypeId),
|
||||
@@ -51,6 +49,7 @@ enum UserDataTypeId {
|
||||
|
||||
/// Handle to registry for userdata methods and metamethods.
|
||||
pub struct UserDataRegistry<T> {
|
||||
lua: LuaGuard,
|
||||
raw: RawUserDataRegistry,
|
||||
ud_type_id: UserDataTypeId,
|
||||
_type: PhantomData<T>,
|
||||
@@ -58,10 +57,10 @@ pub struct UserDataRegistry<T> {
|
||||
|
||||
pub(crate) struct RawUserDataRegistry {
|
||||
// Fields
|
||||
pub(crate) fields: Vec<(String, StaticFieldCallback)>,
|
||||
pub(crate) fields: Vec<(String, Result<Value>)>,
|
||||
pub(crate) field_getters: Vec<(String, Callback)>,
|
||||
pub(crate) field_setters: Vec<(String, Callback)>,
|
||||
pub(crate) meta_fields: Vec<(String, StaticFieldCallback)>,
|
||||
pub(crate) meta_fields: Vec<(String, Result<Value>)>,
|
||||
|
||||
// Methods
|
||||
pub(crate) methods: Vec<(String, Callback)>,
|
||||
@@ -102,17 +101,17 @@ impl UserDataTypeId {
|
||||
|
||||
impl<T> UserDataRegistry<T> {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(type_id: TypeId) -> Self {
|
||||
Self::with_type_id(UserDataTypeId::Shared(type_id))
|
||||
pub(crate) fn new(lua: &Lua, type_id: TypeId) -> Self {
|
||||
Self::with_type_id(lua, UserDataTypeId::Shared(type_id))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_unique(ud_ptr: *mut c_void) -> Self {
|
||||
Self::with_type_id(UserDataTypeId::Unique(ud_ptr))
|
||||
pub(crate) fn new_unique(lua: &Lua, ud_ptr: *mut c_void) -> Self {
|
||||
Self::with_type_id(lua, UserDataTypeId::Unique(ud_ptr))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn with_type_id(ud_type_id: UserDataTypeId) -> Self {
|
||||
fn with_type_id(lua: &Lua, ud_type_id: UserDataTypeId) -> Self {
|
||||
let raw = RawUserDataRegistry {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
@@ -130,6 +129,7 @@ impl<T> UserDataRegistry<T> {
|
||||
};
|
||||
|
||||
UserDataRegistry {
|
||||
lua: lua.lock_arc(),
|
||||
raw,
|
||||
ud_type_id,
|
||||
_type: PhantomData,
|
||||
@@ -548,10 +548,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
let name = name.to_string();
|
||||
self.raw.fields.push((
|
||||
name,
|
||||
Box::new(move |rawlua| unsafe { value.push_into_stack(rawlua) }),
|
||||
));
|
||||
self.raw.fields.push((name, value.into_lua(self.lua.lua())));
|
||||
}
|
||||
|
||||
fn add_field_method_get<M, R>(&mut self, name: impl ToString, method: M)
|
||||
@@ -598,13 +595,10 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
where
|
||||
V: IntoLua + 'static,
|
||||
{
|
||||
let lua = self.lua.lua();
|
||||
let name = name.to_string();
|
||||
self.raw.meta_fields.push((
|
||||
name.clone(),
|
||||
Box::new(move |rawlua| unsafe {
|
||||
Self::check_meta_field(rawlua.lua(), &name, value)?.push_into_stack(rawlua)
|
||||
}),
|
||||
));
|
||||
let field = Self::check_meta_field(lua, &name, value).and_then(|v| v.into_lua(lua));
|
||||
self.raw.meta_fields.push((name, field));
|
||||
}
|
||||
|
||||
fn add_meta_field_with<F, R>(&mut self, name: impl ToString, f: F)
|
||||
@@ -612,14 +606,10 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
|
||||
F: FnOnce(&Lua) -> Result<R> + 'static,
|
||||
R: IntoLua,
|
||||
{
|
||||
let lua = self.lua.lua();
|
||||
let name = name.to_string();
|
||||
self.raw.meta_fields.push((
|
||||
name.clone(),
|
||||
Box::new(move |rawlua| unsafe {
|
||||
let lua = rawlua.lua();
|
||||
Self::check_meta_field(lua, &name, f(lua)?)?.push_into_stack(rawlua)
|
||||
}),
|
||||
));
|
||||
let field = f(lua).and_then(|v| Self::check_meta_field(lua, &name, v).and_then(|v| v.into_lua(lua)));
|
||||
self.raw.meta_fields.push((name, field));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -803,7 +793,7 @@ macro_rules! lua_userdata_impl {
|
||||
($type:ty, $type_id:expr) => {
|
||||
impl<T: UserData + 'static> UserData for $type {
|
||||
fn register(registry: &mut UserDataRegistry<Self>) {
|
||||
let mut orig_registry = UserDataRegistry::with_type_id($type_id);
|
||||
let mut orig_registry = UserDataRegistry::with_type_id(registry.lua.lua(), $type_id);
|
||||
T::register(&mut orig_registry);
|
||||
|
||||
// Copy all fields, methods, etc. from the original registry
|
||||
@@ -841,3 +831,9 @@ lua_userdata_impl!(Arc<RwLock<T>> => ArcRwLock);
|
||||
lua_userdata_impl!(Arc<parking_lot::Mutex<T>> => ArcParkingLotMutex);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<parking_lot::RwLock<T>> => ArcParkingLotRwLock);
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
#[cfg(feature = "send")]
|
||||
static_assertions::assert_impl_all!(super::RawUserDataRegistry: Send);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user