Skip to content

Commit 00e9dd6

Browse files
committed
Delay "any" userdata metatable creation until first instance
1 parent 4ed623c commit 00e9dd6

File tree

5 files changed

+45
-43
lines changed

5 files changed

+45
-43
lines changed

src/scope.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
174174
let ud_ptr = util::push_uninit_userdata::<UserDataStorage<T>>(state, protect)?;
175175

176176
// Push the metatable and register it with no TypeId
177-
let mut registry = UserDataRegistry::new_unique(ud_ptr as *mut _);
177+
let mut registry = UserDataRegistry::new_unique(self.lua.lua(), ud_ptr as *mut _);
178178
T::register(&mut registry);
179179
self.lua.push_userdata_metatable(registry.into_raw())?;
180180
let mt_ptr = ffi::lua_topointer(state, -1);
@@ -222,7 +222,7 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
222222
let ud_ptr = util::push_uninit_userdata::<UserDataStorage<T>>(state, protect)?;
223223

224224
// Push the metatable and register it with no TypeId
225-
let mut registry = UserDataRegistry::new_unique(ud_ptr as *mut _);
225+
let mut registry = UserDataRegistry::new_unique(self.lua.lua(), ud_ptr as *mut _);
226226
register(&mut registry);
227227
self.lua.push_userdata_metatable(registry.into_raw())?;
228228
let mt_ptr = ffi::lua_topointer(state, -1);

src/state.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,7 +1306,7 @@ impl Lua {
13061306
/// This methods provides a way to add fields or methods to userdata objects of a type `T`.
13071307
pub fn register_userdata_type<T: 'static>(&self, f: impl FnOnce(&mut UserDataRegistry<T>)) -> Result<()> {
13081308
let type_id = TypeId::of::<T>();
1309-
let mut registry = UserDataRegistry::new(type_id);
1309+
let mut registry = UserDataRegistry::new(self, type_id);
13101310
f(&mut registry);
13111311

13121312
let lua = self.lock();
@@ -1316,8 +1316,8 @@ impl Lua {
13161316
ffi::luaL_unref(lua.state(), ffi::LUA_REGISTRYINDEX, table_id);
13171317
}
13181318

1319-
// Register the type
1320-
lua.create_userdata_metatable(registry.into_raw())?;
1319+
// Add to "pending" registration map
1320+
((*lua.extra.get()).pending_userdata_reg).insert(type_id, registry.into_raw());
13211321
}
13221322
Ok(())
13231323
}

src/state/extra.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::error::Result;
1313
use crate::state::RawLua;
1414
use crate::stdlib::StdLib;
1515
use crate::types::{AppData, ReentrantMutex, XRc};
16+
use crate::userdata::RawUserDataRegistry;
1617
use crate::util::{get_internal_metatable, push_internal_userdata, TypeKey, WrappedFailure};
1718

1819
#[cfg(any(feature = "luau", doc))]
@@ -35,6 +36,7 @@ pub(crate) struct ExtraData {
3536
pub(super) weak: MaybeUninit<WeakLua>,
3637
pub(super) owned: bool,
3738

39+
pub(super) pending_userdata_reg: FxHashMap<TypeId, RawUserDataRegistry>,
3840
pub(super) registered_userdata_t: FxHashMap<TypeId, c_int>,
3941
pub(super) registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>,
4042
pub(super) last_checked_userdata_mt: (*const c_void, Option<TypeId>),
@@ -144,6 +146,7 @@ impl ExtraData {
144146
lua: MaybeUninit::uninit(),
145147
weak: MaybeUninit::uninit(),
146148
owned,
149+
pending_userdata_reg: FxHashMap::default(),
147150
registered_userdata_t: FxHashMap::default(),
148151
registered_userdata_mt: FxHashMap::default(),
149152
last_checked_userdata_mt: (ptr::null(), None),

src/state/raw.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,7 @@ impl RawLua {
756756
}
757757

758758
// Create a new metatable from `UserData` definition
759-
let mut registry = UserDataRegistry::new(type_id);
759+
let mut registry = UserDataRegistry::new(self.lua(), type_id);
760760
T::register(&mut registry);
761761

762762
self.create_userdata_metatable(registry.into_raw())
@@ -774,9 +774,12 @@ impl RawLua {
774774
return Ok(table_id as Integer);
775775
}
776776

777-
// Create an empty metatable
778-
let registry = UserDataRegistry::<T>::new(type_id);
779-
self.create_userdata_metatable(registry.into_raw())
777+
// Check if metatable creation is pending or create an empty metatable otherwise
778+
let registry = match (*self.extra.get()).pending_userdata_reg.remove(&type_id) {
779+
Some(registry) => registry,
780+
None => UserDataRegistry::<T>::new(self.lua(), type_id).into_raw(),
781+
};
782+
self.create_userdata_metatable(registry)
780783
})
781784
}
782785

@@ -851,9 +854,9 @@ impl RawLua {
851854
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
852855
}
853856
let mut has_name = false;
854-
for (k, push_field) in registry.meta_fields {
857+
for (k, v) in registry.meta_fields {
855858
has_name = has_name || k == MetaMethod::Type;
856-
push_field(self)?;
859+
v?.push_into_stack(self)?;
857860
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
858861
}
859862
// Set `__name/__type` if not provided
@@ -875,8 +878,8 @@ impl RawLua {
875878
ffi::lua_pop(state, 1);
876879
push_table(state, 0, fields_nrec, true)?;
877880
}
878-
for (k, push_field) in mem::take(&mut registry.fields) {
879-
push_field(self)?;
881+
for (k, v) in mem::take(&mut registry.fields) {
882+
v?.push_into_stack(self)?;
880883
rawset_field(state, -2, &k)?;
881884
}
882885
rawset_field(state, metatable_index, "__index")?;
@@ -896,12 +899,12 @@ impl RawLua {
896899
self.push(self.create_callback(m)?)?;
897900
rawset_field(state, -2, &k)?;
898901
}
899-
for (k, push_field) in registry.fields {
902+
for (k, v) in registry.fields {
900903
unsafe extern "C-unwind" fn return_field(state: *mut ffi::lua_State) -> c_int {
901904
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
902905
1
903906
}
904-
push_field(self)?;
907+
v?.push_into_stack(self)?;
905908
protect_lua!(state, 1, 1, fn(state) {
906909
ffi::lua_pushcclosure(state, return_field, 1);
907910
})?;

src/userdata/registry.rs

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::os::raw::c_void;
77
use std::string::String as StdString;
88

99
use crate::error::{Error, Result};
10-
use crate::state::{Lua, RawLua};
10+
use crate::state::{Lua, LuaGuard};
1111
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
1212
use crate::types::{Callback, MaybeSend};
1313
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataStorage};
@@ -26,8 +26,6 @@ use std::rc::Rc;
2626
#[cfg(feature = "userdata-wrappers")]
2727
use std::sync::{Arc, Mutex, RwLock};
2828

29-
type StaticFieldCallback = Box<dyn FnOnce(&RawLua) -> Result<()> + 'static>;
30-
3129
#[derive(Clone, Copy)]
3230
enum UserDataTypeId {
3331
Shared(TypeId),
@@ -51,17 +49,18 @@ enum UserDataTypeId {
5149

5250
/// Handle to registry for userdata methods and metamethods.
5351
pub struct UserDataRegistry<T> {
52+
lua: LuaGuard,
5453
raw: RawUserDataRegistry,
5554
ud_type_id: UserDataTypeId,
5655
_type: PhantomData<T>,
5756
}
5857

5958
pub(crate) struct RawUserDataRegistry {
6059
// Fields
61-
pub(crate) fields: Vec<(String, StaticFieldCallback)>,
60+
pub(crate) fields: Vec<(String, Result<Value>)>,
6261
pub(crate) field_getters: Vec<(String, Callback)>,
6362
pub(crate) field_setters: Vec<(String, Callback)>,
64-
pub(crate) meta_fields: Vec<(String, StaticFieldCallback)>,
63+
pub(crate) meta_fields: Vec<(String, Result<Value>)>,
6564

6665
// Methods
6766
pub(crate) methods: Vec<(String, Callback)>,
@@ -102,17 +101,17 @@ impl UserDataTypeId {
102101

103102
impl<T> UserDataRegistry<T> {
104103
#[inline(always)]
105-
pub(crate) fn new(type_id: TypeId) -> Self {
106-
Self::with_type_id(UserDataTypeId::Shared(type_id))
104+
pub(crate) fn new(lua: &Lua, type_id: TypeId) -> Self {
105+
Self::with_type_id(lua, UserDataTypeId::Shared(type_id))
107106
}
108107

109108
#[inline(always)]
110-
pub(crate) fn new_unique(ud_ptr: *mut c_void) -> Self {
111-
Self::with_type_id(UserDataTypeId::Unique(ud_ptr as usize))
109+
pub(crate) fn new_unique(lua: &Lua, ud_ptr: *mut c_void) -> Self {
110+
Self::with_type_id(lua, UserDataTypeId::Unique(ud_ptr as usize))
112111
}
113112

114113
#[inline(always)]
115-
fn with_type_id(ud_type_id: UserDataTypeId) -> Self {
114+
fn with_type_id(lua: &Lua, ud_type_id: UserDataTypeId) -> Self {
116115
let raw = RawUserDataRegistry {
117116
fields: Vec::new(),
118117
field_getters: Vec::new(),
@@ -130,6 +129,7 @@ impl<T> UserDataRegistry<T> {
130129
};
131130

132131
UserDataRegistry {
132+
lua: lua.lock_arc(),
133133
raw,
134134
ud_type_id,
135135
_type: PhantomData,
@@ -548,10 +548,7 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
548548
V: IntoLua + 'static,
549549
{
550550
let name = name.to_string();
551-
self.raw.fields.push((
552-
name,
553-
Box::new(move |rawlua| unsafe { value.push_into_stack(rawlua) }),
554-
));
551+
self.raw.fields.push((name, value.into_lua(self.lua.lua())));
555552
}
556553

557554
fn add_field_method_get<M, R>(&mut self, name: impl ToString, method: M)
@@ -598,28 +595,21 @@ impl<T> UserDataFields<T> for UserDataRegistry<T> {
598595
where
599596
V: IntoLua + 'static,
600597
{
598+
let lua = self.lua.lua();
601599
let name = name.to_string();
602-
self.raw.meta_fields.push((
603-
name.clone(),
604-
Box::new(move |rawlua| unsafe {
605-
Self::check_meta_field(rawlua.lua(), &name, value)?.push_into_stack(rawlua)
606-
}),
607-
));
600+
let field = Self::check_meta_field(lua, &name, value).and_then(|v| v.into_lua(lua));
601+
self.raw.meta_fields.push((name, field));
608602
}
609603

610604
fn add_meta_field_with<F, R>(&mut self, name: impl ToString, f: F)
611605
where
612606
F: FnOnce(&Lua) -> Result<R> + 'static,
613607
R: IntoLua,
614608
{
609+
let lua = self.lua.lua();
615610
let name = name.to_string();
616-
self.raw.meta_fields.push((
617-
name.clone(),
618-
Box::new(move |rawlua| unsafe {
619-
let lua = rawlua.lua();
620-
Self::check_meta_field(lua, &name, f(lua)?)?.push_into_stack(rawlua)
621-
}),
622-
));
611+
let field = f(lua).and_then(|v| Self::check_meta_field(lua, &name, v).and_then(|v| v.into_lua(lua)));
612+
self.raw.meta_fields.push((name, field));
623613
}
624614
}
625615

@@ -803,7 +793,7 @@ macro_rules! lua_userdata_impl {
803793
($type:ty, $type_id:expr) => {
804794
impl<T: UserData + 'static> UserData for $type {
805795
fn register(registry: &mut UserDataRegistry<Self>) {
806-
let mut orig_registry = UserDataRegistry::with_type_id($type_id);
796+
let mut orig_registry = UserDataRegistry::with_type_id(registry.lua.lua(), $type_id);
807797
T::register(&mut orig_registry);
808798

809799
// Copy all fields, methods, etc. from the original registry
@@ -841,3 +831,9 @@ lua_userdata_impl!(Arc<RwLock<T>> => ArcRwLock);
841831
lua_userdata_impl!(Arc<parking_lot::Mutex<T>> => ArcParkingLotMutex);
842832
#[cfg(feature = "userdata-wrappers")]
843833
lua_userdata_impl!(Arc<parking_lot::RwLock<T>> => ArcParkingLotRwLock);
834+
835+
#[cfg(test)]
836+
mod assertions {
837+
#[cfg(feature = "send")]
838+
static_assertions::assert_impl_all!(super::RawUserDataRegistry: Send);
839+
}

0 commit comments

Comments
 (0)