Something went wrong. Try again.
Personal fork of prime agent. Mirror of: https://mnemosyne.sigilzero.dev/sigilzero/prime-agent
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330use std::cell::RefCell;use std::marker::PhantomData;use std::mem;
use crate::error::{Error, Result};use crate::function::Function;use crate::state::{Lua, LuaGuard, RawLua};use crate::traits::{FromLuaMulti, IntoLuaMulti};use crate::types::{Callback, CallbackUpvalue, ScopedCallback, ValueRef};use crate::userdata::{AnyUserData, UserData, UserDataRegistry, UserDataStorage};use crate::util::{self, check_stack, get_metatable_ptr, get_userdata, take_userdata, StackGuard};
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and/// callbacks that are not required to be `Send` or `'static`.////// See [`Lua::scope`] for more details.pub struct Scope<'scope, 'env: 'scope> { lua: LuaGuard, // Internal destructors run first, then user destructors (based on the declaration order) destructors: Destructors<'env>, user_destructors: UserDestructors<'env>, _scope_invariant: PhantomData<&'scope mut &'scope ()>, _env_invariant: PhantomData<&'env mut &'env ()>,}
type DestructorCallback<'a> = Box<dyn FnOnce(&RawLua, ValueRef) -> Vec<Box<dyn FnOnce() + 'a>>>;
// Implement Drop on Destructors instead of Scope to avoid compilation errorstruct Destructors<'a>(RefCell<Vec<(ValueRef, DestructorCallback<'a>)>>);
struct UserDestructors<'a>(RefCell<Vec<Box<dyn FnOnce() + 'a>>>);
impl<'scope, 'env: 'scope> Scope<'scope, 'env> { pub(crate) fn new(lua: LuaGuard) -> Self { Scope { lua, destructors: Destructors(RefCell::new(Vec::new())), user_destructors: UserDestructors(RefCell::new(Vec::new())), _scope_invariant: PhantomData, _env_invariant: PhantomData, } }
/// Wraps a Rust function or closure, creating a callable Lua function handle to it. /// /// This is a version of [`Lua::create_function`] that creates a callback which expires on /// scope drop. See [`Lua::scope`] for more details. pub fn create_function<F, A, R>(&'scope self, func: F) -> Result<Function> where F: Fn(&Lua, A) -> Result<R> + 'scope, A: FromLuaMulti, R: IntoLuaMulti, { unsafe { self.create_callback(Box::new(move |rawlua, nargs| { let args = A::from_stack_args(nargs, 1, None, rawlua)?; func(rawlua.lua(), args)?.push_into_stack_multi(rawlua) })) } }
/// Wraps a Rust mutable closure, creating a callable Lua function handle to it. /// /// This is a version of [`Lua::create_function_mut`] that creates a callback which expires /// on scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details. pub fn create_function_mut<F, A, R>(&'scope self, func: F) -> Result<Function> where F: FnMut(&Lua, A) -> Result<R> + 'scope, A: FromLuaMulti, R: IntoLuaMulti, { let func = RefCell::new(func); self.create_function(move |lua, args| { (*func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?)(lua, args) }) }
/// Creates a Lua userdata object from a reference to custom userdata type. /// /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on /// scope drop, and does not require that the userdata type be Send. This method takes /// non-'static reference to the data. See [`Lua::scope`] for more details. /// /// Userdata created with this method will not be able to be mutated from Lua. pub fn create_userdata_ref<T>(&'scope self, data: &'env T) -> Result<AnyUserData> where T: UserData + 'static, { let ud = unsafe { self.lua.make_userdata(UserDataStorage::new_ref(data)) }?; self.seal_userdata::<T>(&ud); Ok(ud) }
/// Creates a Lua userdata object from a mutable reference to custom userdata type. /// /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on /// scope drop, and does not require that the userdata type be Send. This method takes /// non-'static mutable reference to the data. See [`Lua::scope`] for more details. pub fn create_userdata_ref_mut<T>(&'scope self, data: &'env mut T) -> Result<AnyUserData> where T: UserData + 'static, { let ud = unsafe { self.lua.make_userdata(UserDataStorage::new_ref_mut(data)) }?; self.seal_userdata::<T>(&ud); Ok(ud) }
/// Creates a Lua userdata object from a reference to custom Rust type. /// /// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on /// scope drop, and does not require that the Rust type be Send. This method takes non-'static /// reference to the data. See [`Lua::scope`] for more details. /// /// Userdata created with this method will not be able to be mutated from Lua. pub fn create_any_userdata_ref<T>(&'scope self, data: &'env T) -> Result<AnyUserData> where T: 'static, { let ud = unsafe { self.lua.make_any_userdata(UserDataStorage::new_ref(data)) }?; self.seal_userdata::<T>(&ud); Ok(ud) }
/// Creates a Lua userdata object from a mutable reference to custom Rust type. /// /// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on /// scope drop, and does not require that the Rust type be Send. This method takes non-'static /// mutable reference to the data. See [`Lua::scope`] for more details. pub fn create_any_userdata_ref_mut<T>(&'scope self, data: &'env mut T) -> Result<AnyUserData> where T: 'static, { let ud = unsafe { self.lua.make_any_userdata(UserDataStorage::new_ref_mut(data)) }?; self.seal_userdata::<T>(&ud); Ok(ud) }
/// Creates a Lua userdata object from a custom userdata type. /// /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on /// scope drop, and does not require that the userdata type be `Send` or `'static`. See /// [`Lua::scope`] for more details. /// /// The main limitation that comes from using non-'static userdata is that the produced userdata /// will no longer have a [`TypeId`] associated with it, because [`TypeId`] can only work for /// `'static` types. This means that it is impossible, once the userdata is created, to get a /// reference to it back *out* of an [`AnyUserData`] handle. This also implies that the /// "function" type methods that can be added via [`UserDataMethods`] (the ones that accept /// [`AnyUserData`] as a first parameter) are vastly less useful. Also, there is no way to /// re-use a single metatable for multiple non-'static types, so there is a higher cost /// associated with creating the userdata metatable each time a new userdata is created. /// /// [`TypeId`]: std::any::TypeId /// [`UserDataMethods`]: crate::UserDataMethods pub fn create_userdata<T>(&'scope self, data: T) -> Result<AnyUserData> where T: UserData + 'env, { let state = self.lua.state(); unsafe { let _sg = StackGuard::new(state); check_stack(state, 3)?;
// We don't write the data to the userdata until pushing the metatable let protect = !self.lua.unlikely_memory_error(); #[cfg(feature = "luau")] let ud_ptr = { let data = UserDataStorage::new_scoped(data); util::push_userdata(state, data, protect)? }; #[cfg(not(feature = "luau"))] 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(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); self.lua.register_userdata_metatable(mt_ptr, None);
// Write data to the pointer and attach metatable #[cfg(not(feature = "luau"))] std::ptr::write(ud_ptr, UserDataStorage::new_scoped(data)); ffi::lua_setmetatable(state, -2);
let ud = AnyUserData(self.lua.pop_ref()); self.seal_userdata::<T>(&ud);
Ok(ud) } }
/// Creates a Lua userdata object from a custom Rust type. /// /// Since the Rust type is not required to be static and implement [`UserData`] trait, /// you need to provide a function to register fields or methods for the object. /// /// See also [`Scope::create_userdata`] for more details about non-static limitations. pub fn create_any_userdata<T>( &'scope self, data: T, register: impl FnOnce(&mut UserDataRegistry<T>), ) -> Result<AnyUserData> where T: 'env, { let state = self.lua.state(); let ud = unsafe { let _sg = StackGuard::new(state); check_stack(state, 3)?;
// We don't write the data to the userdata until pushing the metatable let protect = !self.lua.unlikely_memory_error(); #[cfg(feature = "luau")] let ud_ptr = { let data = UserDataStorage::new_scoped(data); util::push_userdata(state, data, protect)? }; #[cfg(not(feature = "luau"))] 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(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); self.lua.register_userdata_metatable(mt_ptr, None);
// Write data to the pointer and attach metatable #[cfg(not(feature = "luau"))] std::ptr::write(ud_ptr, UserDataStorage::new_scoped(data)); ffi::lua_setmetatable(state, -2);
AnyUserData(self.lua.pop_ref()) }; self.seal_userdata::<T>(&ud); Ok(ud) }
/// Adds a destructor function to be run when the scope ends. /// /// This functionality is useful for cleaning up any resources after the scope ends. /// /// # Example /// /// ```rust /// # use mlua::{Error, Lua, Result}; /// # fn main() -> Result<()> { /// let lua = Lua::new(); /// let ud = lua.create_any_userdata(String::from("hello"))?; /// lua.scope(|scope| { /// scope.add_destructor(|| { /// _ = ud.take::<String>(); /// }); /// // Run the code that uses `ud` here /// Ok(()) /// })?; /// assert!(matches!(ud.borrow::<String>(), Err(Error::UserDataDestructed))); /// # Ok(()) /// # } pub fn add_destructor(&'scope self, destructor: impl FnOnce() + 'env) { self.user_destructors.0.borrow_mut().push(Box::new(destructor)); }
unsafe fn create_callback(&'scope self, f: ScopedCallback<'scope>) -> Result<Function> { let f = mem::transmute::<ScopedCallback, Callback>(f); let f = self.lua.create_callback(f)?;
let destructor: DestructorCallback = Box::new(|rawlua, vref| { let ref_thread = rawlua.ref_thread(); ffi::lua_getupvalue(ref_thread, vref.index, 1); let upvalue = get_userdata::<CallbackUpvalue>(ref_thread, -1); let data = (*upvalue).data.take(); ffi::lua_pop(ref_thread, 1); vec![Box::new(move || drop(data))] }); self.destructors.0.borrow_mut().push((f.0.clone(), destructor));
Ok(f) }
/// Shortens the lifetime of the userdata to the lifetime of the scope. fn seal_userdata<T: 'env>(&self, ud: &AnyUserData) { let destructor: DestructorCallback = Box::new(|rawlua, vref| unsafe { // Ensure that userdata is not destructed match rawlua.get_userdata_ref_type_id(&vref) { Ok(Some(_)) => {} Ok(None) => { // Deregister metatable let mt_ptr = get_metatable_ptr(rawlua.ref_thread(), vref.index); rawlua.deregister_userdata_metatable(mt_ptr); } Err(_) => return vec![], }
let data = take_userdata::<UserDataStorage<T>>(rawlua.ref_thread(), vref.index); vec![Box::new(move || drop(data))] }); self.destructors.0.borrow_mut().push((ud.0.clone(), destructor)); }}
impl Drop for Destructors<'_> { fn drop(&mut self) { // We separate the action of invalidating the userdata in Lua and actually dropping the // userdata type into two phases. This is so that, in the event a userdata drop panics, // we can be sure that all of the userdata in Lua is actually invalidated.
let destructors = mem::take(&mut *self.0.borrow_mut()); if let Some(lua) = destructors.first().map(|(vref, _)| vref.lua.lock()) { // All destructors are non-panicking, so this is fine let to_drop = destructors .into_iter() .flat_map(|(vref, destructor)| destructor(&lua, vref)) .collect::<Vec<_>>();
drop(to_drop); } }}
impl Drop for UserDestructors<'_> { fn drop(&mut self) { let destructors = mem::take(&mut *self.0.borrow_mut()); for destructor in destructors { destructor(); } }}