Skip to content

Commit

Permalink
Add Lua::scope back
Browse files Browse the repository at this point in the history
  • Loading branch information
khvzak committed Sep 20, 2024
1 parent 7c2e9b5 commit da4404b
Show file tree
Hide file tree
Showing 42 changed files with 847 additions and 1,607 deletions.
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ mod hook;
mod luau;
mod memory;
mod multi;
// mod scope;
mod scope;
mod state;
mod stdlib;
mod string;
Expand Down
900 changes: 111 additions & 789 deletions src/scope.rs

Large diffs are not rendered by default.

37 changes: 17 additions & 20 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::error::{Error, Result};
use crate::function::Function;
use crate::hook::Debug;
use crate::memory::MemoryState;
// use crate::scope::Scope;
use crate::scope::Scope;
use crate::stdlib::StdLib;
use crate::string::String;
use crate::table::Table;
Expand All @@ -21,7 +21,7 @@ use crate::types::{
AppDataRef, AppDataRefMut, ArcReentrantMutexGuard, Integer, LightUserData, MaybeSend, Number,
ReentrantMutex, ReentrantMutexGuard, RegistryKey, XRc, XWeak,
};
use crate::userdata::{AnyUserData, UserData, UserDataProxy, UserDataRegistry, UserDataVariant};
use crate::userdata::{AnyUserData, UserData, UserDataProxy, UserDataRegistry, UserDataStorage};
use crate::util::{assert_stack, check_stack, push_string, push_table, rawset_field, StackGuard};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};

Expand Down Expand Up @@ -1201,7 +1201,7 @@ impl Lua {
where
T: UserData + MaybeSend + 'static,
{
unsafe { self.lock().make_userdata(UserDataVariant::new(data)) }
unsafe { self.lock().make_userdata(UserDataStorage::new(data)) }
}

/// Creates a Lua userdata object from a custom serializable userdata type.
Expand All @@ -1214,7 +1214,7 @@ impl Lua {
where
T: UserData + Serialize + MaybeSend + 'static,
{
unsafe { self.lock().make_userdata(UserDataVariant::new_ser(data)) }
unsafe { self.lock().make_userdata(UserDataStorage::new_ser(data)) }
}

/// Creates a Lua userdata object from a custom Rust type.
Expand All @@ -1229,7 +1229,7 @@ impl Lua {
where
T: MaybeSend + 'static,
{
unsafe { self.lock().make_any_userdata(UserDataVariant::new(data)) }
unsafe { self.lock().make_any_userdata(UserDataStorage::new(data)) }
}

/// Creates a Lua userdata object from a custom serializable Rust type.
Expand All @@ -1244,26 +1244,26 @@ impl Lua {
where
T: Serialize + MaybeSend + 'static,
{
unsafe { (self.lock()).make_any_userdata(UserDataVariant::new_ser(data)) }
unsafe { (self.lock()).make_any_userdata(UserDataStorage::new_ser(data)) }
}

/// 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 UserDataRegistry<T>)) -> Result<()> {
let mut registry = const { UserDataRegistry::new() };
let type_id = TypeId::of::<T>();
let mut registry = UserDataRegistry::new(type_id);
f(&mut registry);

let lua = self.lock();
unsafe {
// Deregister the type if it already registered
let type_id = TypeId::of::<T>();
if let Some(&table_id) = (*lua.extra.get()).registered_userdata.get(&type_id) {
if let Some(&table_id) = (*lua.extra.get()).registered_userdata_t.get(&type_id) {
ffi::luaL_unref(lua.state(), ffi::LUA_REGISTRYINDEX, table_id);
}

// Register the type
lua.register_userdata_metatable(registry)?;
lua.create_userdata_metatable(registry)?;
}
Ok(())
}
Expand Down Expand Up @@ -1306,7 +1306,7 @@ impl Lua {
T: UserData + 'static,
{
let ud = UserDataProxy::<T>(PhantomData);
unsafe { self.lock().make_userdata(UserDataVariant::new(ud)) }
unsafe { self.lock().make_userdata(UserDataStorage::new(ud)) }
}

/// Sets the metatable for a Luau builtin vector type.
Expand Down Expand Up @@ -1380,15 +1380,12 @@ impl Lua {
/// dropped. `Function` types will error when called, and `AnyUserData` will be typeless. It
/// would be impossible to prevent handles to scoped values from escaping anyway, since you
/// would always be able to smuggle them through Lua state.
// pub fn scope<'lua, 'scope, R>(
// &'lua self,
// f: impl FnOnce(&Scope<'lua, 'scope>) -> Result<R>,
// ) -> Result<R>
// where
// 'lua: 'scope,
// {
// f(&Scope::new(self))
// }
pub fn scope<'env, R>(
&self,
f: impl for<'scope> FnOnce(&'scope mut Scope<'scope, 'env>) -> Result<R>,
) -> Result<R> {
f(&mut Scope::new(self.lock_arc()))
}

/// Attempts to coerce a Lua value into a String in a manner consistent with Lua's internal
/// behavior.
Expand Down
4 changes: 2 additions & 2 deletions src/state/extra.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ pub(crate) struct ExtraData {
pub(super) weak: MaybeUninit<WeakLua>,
pub(super) owned: bool,

pub(super) registered_userdata: FxHashMap<TypeId, c_int>,
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,7 +144,7 @@ impl ExtraData {
lua: MaybeUninit::uninit(),
weak: MaybeUninit::uninit(),
owned,
registered_userdata: FxHashMap::default(),
registered_userdata_t: FxHashMap::default(),
registered_userdata_mt: FxHashMap::default(),
last_checked_userdata_mt: (ptr::null(), None),
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
Expand Down
106 changes: 50 additions & 56 deletions src/state/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::types::{
AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData,
MaybeSend, ReentrantMutex, RegistryKey, SubtypeId, ValueRef, XRc,
};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataRegistry, UserDataVariant};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataRegistry, UserDataStorage};
use crate::util::{
assert_stack, check_stack, get_destructed_userdata_metatable, get_internal_userdata, get_main_state,
get_userdata, init_error_registry, init_internal_metatable, init_userdata_metatable, pop_error,
Expand Down Expand Up @@ -711,45 +711,45 @@ impl RawLua {
}
}

pub(crate) unsafe fn make_userdata<T>(&self, data: UserDataVariant<T>) -> Result<AnyUserData>
pub(crate) unsafe fn make_userdata<T>(&self, data: UserDataStorage<T>) -> Result<AnyUserData>
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) {
if let Some(&table_id) = (*self.extra.get()).registered_userdata_t.get(&type_id) {
return Ok(table_id as Integer);
}

// Create a new metatable from `UserData` definition
let mut registry = const { UserDataRegistry::new() };
let mut registry = UserDataRegistry::new(type_id);
T::register(&mut registry);

self.register_userdata_metatable(registry)
self.create_userdata_metatable(registry)
})
}

pub(crate) unsafe fn make_any_userdata<T>(&self, data: UserDataVariant<T>) -> Result<AnyUserData>
pub(crate) unsafe fn make_any_userdata<T>(&self, data: UserDataStorage<T>) -> Result<AnyUserData>
where
T: '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) {
if let Some(&table_id) = (*self.extra.get()).registered_userdata_t.get(&type_id) {
return Ok(table_id as Integer);
}

// Create an empty metatable
let registry = const { UserDataRegistry::new() };
self.register_userdata_metatable::<T>(registry)
let registry = UserDataRegistry::<T>::new(type_id);
self.create_userdata_metatable(registry)
})
}

unsafe fn make_userdata_with_metatable<T>(
&self,
data: UserDataVariant<T>,
data: UserDataStorage<T>,
get_metatable_id: impl FnOnce() -> Result<Integer>,
) -> Result<AnyUserData> {
let state = self.state();
Expand All @@ -760,10 +760,7 @@ impl RawLua {
ffi::lua_pushnil(state);
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, get_metatable_id()?);
let protect = !self.unlikely_memory_error();
#[cfg(not(feature = "lua54"))]
crate::util::push_userdata(state, data, protect)?;
#[cfg(feature = "lua54")]
crate::util::push_userdata_uv(state, data, crate::userdata::USER_VALUE_MAXSLOT as c_int, protect)?;
ffi::lua_replace(state, -3);
ffi::lua_setmetatable(state, -2);

Expand All @@ -782,12 +779,31 @@ impl RawLua {
Ok(AnyUserData(self.pop_ref(), SubtypeId::None))
}

pub(crate) unsafe fn register_userdata_metatable<T: 'static>(
pub(crate) unsafe fn create_userdata_metatable<T>(
&self,
mut registry: UserDataRegistry<T>,
registry: UserDataRegistry<T>,
) -> Result<Integer> {
let state = self.state();
let _sg = StackGuard::new(state);
let type_id = registry.type_id();

self.push_userdata_metatable(registry)?;

let mt_ptr = ffi::lua_topointer(state, -1);
let id = protect_lua!(state, 1, 0, |state| {
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
})?;

if let Some(type_id) = type_id {
(*self.extra.get()).registered_userdata_t.insert(type_id, id);
}
self.register_userdata_metatable(mt_ptr, type_id);

Ok(id as Integer)
}

pub(crate) unsafe fn push_userdata_metatable<T>(&self, mut registry: UserDataRegistry<T>) -> Result<()> {
let state = self.state();
let _sg = StackGuard::with_top(state, ffi::lua_gettop(state) + 1);
check_stack(state, 13)?;

// Prepare metatable, add meta methods first and then meta fields
Expand Down Expand Up @@ -922,7 +938,7 @@ impl RawLua {
let extra_init = None;
#[cfg(not(feature = "luau"))]
let extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>> = Some(|state| {
ffi::lua_pushcfunction(state, crate::util::userdata_destructor::<UserDataVariant<T>>);
ffi::lua_pushcfunction(state, crate::util::userdata_destructor::<UserDataStorage<T>>);
rawset_field(state, -2, "__gc")
});

Expand All @@ -938,44 +954,21 @@ impl RawLua {
// Pop extra tables to get metatable on top of the stack
ffi::lua_pop(state, extra_tables_count);

let mt_ptr = ffi::lua_topointer(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(always)]
pub(crate) unsafe fn register_userdata_metatable(&self, mt_ptr: *const c_void, type_id: Option<TypeId>) {
(*self.extra.get()).registered_userdata_mt.insert(mt_ptr, type_id);
}

// #[inline]
// pub(crate) unsafe fn register_raw_userdata_metatable(
// &self,
// ptr: *const c_void,
// type_id: Option<TypeId>,
// ) {
// (*self.extra.get())
// .registered_userdata_mt
// .insert(ptr, type_id);
// }

// #[inline]
// pub(crate) unsafe fn deregister_raw_userdata_metatable(&self, ptr: *const c_void) {
// (*self.extra.get()).registered_userdata_mt.remove(&ptr);
// if (*self.extra.get()).last_checked_userdata_mt.0 == ptr {
// (*self.extra.get()).last_checked_userdata_mt = (ptr::null(), None);
// }
// }

// #[inline(always)]
// pub(crate) unsafe fn get_userdata_ref<T: 'static>(&self, idx: c_int) -> Result<UserDataRef<T>> {
// let guard = self.lua().lock_arc();
// (*get_userdata::<UserDataVariant<T>>(self.state(), idx)).try_make_ref(guard)
// }
#[inline(always)]
pub(crate) unsafe fn deregister_userdata_metatable(&self, mt_ptr: *const c_void) {
(*self.extra.get()).registered_userdata_mt.remove(&mt_ptr);
if (*self.extra.get()).last_checked_userdata_mt.0 == mt_ptr {
(*self.extra.get()).last_checked_userdata_mt = (ptr::null(), None);
}
}

// Returns `TypeId` for the userdata ref, checking that it's registered and not destructed.
//
Expand Down Expand Up @@ -1028,17 +1021,17 @@ impl RawLua {

// Creates a Function out of a Callback containing a 'static Fn.
pub(crate) fn create_callback(&self, func: Callback) -> Result<Function> {
// This is non-scoped version of the callback (upvalue is always valid)
// TODO: add a scoped version
unsafe extern "C-unwind" fn call_callback(state: *mut ffi::lua_State) -> c_int {
let upvalue = get_userdata::<CallbackUpvalue>(state, ffi::lua_upvalueindex(1));
callback_error_ext(state, (*upvalue).extra.get(), |extra, nargs| {
// Lua ensures that `LUA_MINSTACK` stack spaces are available (after pushing arguments)
// The lock must be already held as the callback is executed
let rawlua = (*extra).raw_lua();
let _guard = StateGuard::new(rawlua, state);
let func = &*(*upvalue).data;
func(rawlua, nargs)
match (*upvalue).data {
Some(ref func) => func(rawlua, nargs),
None => Err(Error::CallbackDestructed),
}
})
}

Expand All @@ -1047,6 +1040,7 @@ impl RawLua {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;

let func = Some(func);
let extra = XRc::clone(&self.extra);
let protect = !self.unlikely_memory_error();
push_internal_userdata(state, CallbackUpvalue { data: func, extra }, protect)?;
Expand Down
4 changes: 3 additions & 1 deletion src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,14 @@ pub(crate) type Callback = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + Send +
#[cfg(not(feature = "send"))]
pub(crate) type Callback = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + 'static>;

pub(crate) type ScopedCallback<'s> = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + 's>;

pub(crate) struct Upvalue<T> {
pub(crate) data: T,
pub(crate) extra: XRc<UnsafeCell<ExtraData>>,
}

pub(crate) type CallbackUpvalue = Upvalue<Callback>;
pub(crate) type CallbackUpvalue = Upvalue<Option<Callback>>;

#[cfg(all(feature = "async", feature = "send"))]
pub(crate) type AsyncCallback =
Expand Down
Loading

0 comments on commit da4404b

Please sign in to comment.