Skip to content

Commit

Permalink
Delay "any" userdata metatable creation until first instance
Browse files Browse the repository at this point in the history
  • Loading branch information
khvzak committed Nov 12, 2024
1 parent 4ed623c commit 00e9dd6
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 43 deletions.
4 changes: 2 additions & 2 deletions src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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(())
}
Expand Down
3 changes: 3 additions & 0 deletions src/state/extra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand All @@ -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>),
Expand Down Expand Up @@ -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),
Expand Down
23 changes: 13 additions & 10 deletions src/state/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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)
})
}

Expand Down Expand Up @@ -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
Expand All @@ -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")?;
Expand All @@ -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);
})?;
Expand Down
52 changes: 24 additions & 28 deletions src/userdata/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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),
Expand All @@ -51,17 +49,18 @@ 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>,
}

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)>,
Expand Down Expand Up @@ -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 as usize))
pub(crate) fn new_unique(lua: &Lua, ud_ptr: *mut c_void) -> Self {
Self::with_type_id(lua, UserDataTypeId::Unique(ud_ptr as usize))
}

#[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(),
Expand All @@ -130,6 +129,7 @@ impl<T> UserDataRegistry<T> {
};

UserDataRegistry {
lua: lua.lock_arc(),
raw,
ud_type_id,
_type: PhantomData,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -598,28 +595,21 @@ 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)
where
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));
}
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}

0 comments on commit 00e9dd6

Please sign in to comment.