Scope support (including async)
This commit is contained in:
parent
2bd5c2f6ca
commit
d366ce0dd4
|
@ -54,7 +54,7 @@ luajit-src = { version = "210.0.0", optional = true }
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rustyline = "6.0"
|
rustyline = "6.0"
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
trybuild = "1.0"
|
trybuild = "1.0.27"
|
||||||
futures = "0.3.4"
|
futures = "0.3.4"
|
||||||
hyper = "0.13"
|
hyper = "0.13"
|
||||||
tokio = { version = "0.2.18", features = ["full"] }
|
tokio = { version = "0.2.18", features = ["full"] }
|
||||||
|
|
|
@ -12,7 +12,7 @@ use crate::lua::Lua;
|
||||||
use crate::string::String;
|
use crate::string::String;
|
||||||
use crate::table::Table;
|
use crate::table::Table;
|
||||||
use crate::thread::Thread;
|
use crate::thread::Thread;
|
||||||
use crate::types::{LightUserData, Number, MaybeSend};
|
use crate::types::{LightUserData, MaybeSend, Number};
|
||||||
use crate::userdata::{AnyUserData, UserData};
|
use crate::userdata::{AnyUserData, UserData};
|
||||||
use crate::value::{FromLua, Nil, ToLua, Value};
|
use crate::value::{FromLua, Nil, ToLua, Value};
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ pub use self::lua::{
|
||||||
pub use self::lua::{lua_KContext, lua_KFunction};
|
pub use self::lua::{lua_KContext, lua_KFunction};
|
||||||
|
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
pub use self::lua::lua_setfenv;
|
pub use self::lua::{lua_getfenv, lua_setfenv};
|
||||||
|
|
||||||
// C API functions
|
// C API functions
|
||||||
pub use self::lua::{
|
pub use self::lua::{
|
||||||
|
|
|
@ -59,6 +59,7 @@ mod ffi;
|
||||||
mod function;
|
mod function;
|
||||||
mod lua;
|
mod lua;
|
||||||
mod multi;
|
mod multi;
|
||||||
|
mod scope;
|
||||||
mod stdlib;
|
mod stdlib;
|
||||||
mod string;
|
mod string;
|
||||||
mod table;
|
mod table;
|
||||||
|
@ -74,6 +75,7 @@ pub use crate::error::{Error, ExternalError, ExternalResult, Result};
|
||||||
pub use crate::function::Function;
|
pub use crate::function::Function;
|
||||||
pub use crate::lua::{Chunk, Lua};
|
pub use crate::lua::{Chunk, Lua};
|
||||||
pub use crate::multi::Variadic;
|
pub use crate::multi::Variadic;
|
||||||
|
pub use crate::scope::Scope;
|
||||||
pub use crate::stdlib::StdLib;
|
pub use crate::stdlib::StdLib;
|
||||||
pub use crate::string::String;
|
pub use crate::string::String;
|
||||||
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
|
pub use crate::table::{Table, TableExt, TablePairs, TableSequence};
|
||||||
|
|
50
src/lua.rs
50
src/lua.rs
|
@ -10,11 +10,12 @@ use std::{mem, ptr, str};
|
||||||
use crate::error::{Error, Result};
|
use crate::error::{Error, Result};
|
||||||
use crate::ffi;
|
use crate::ffi;
|
||||||
use crate::function::Function;
|
use crate::function::Function;
|
||||||
|
use crate::scope::Scope;
|
||||||
use crate::stdlib::StdLib;
|
use crate::stdlib::StdLib;
|
||||||
use crate::string::String;
|
use crate::string::String;
|
||||||
use crate::table::Table;
|
use crate::table::Table;
|
||||||
use crate::thread::Thread;
|
use crate::thread::Thread;
|
||||||
use crate::types::{Callback, Integer, LightUserData, LuaRef, Number, RegistryKey, MaybeSend};
|
use crate::types::{Callback, Integer, LightUserData, LuaRef, MaybeSend, Number, RegistryKey};
|
||||||
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
||||||
use crate::util::{
|
use crate::util::{
|
||||||
assert_stack, callback_error, check_stack, get_gc_userdata, get_main_state,
|
assert_stack, callback_error, check_stack, get_gc_userdata, get_main_state,
|
||||||
|
@ -611,6 +612,53 @@ impl Lua {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Calls the given function with a `Scope` parameter, giving the function the ability to create
|
||||||
|
/// userdata and callbacks from rust types that are !Send or non-'static.
|
||||||
|
///
|
||||||
|
/// The lifetime of any function or userdata created through `Scope` lasts only until the
|
||||||
|
/// completion of this method call, on completion all such created values are automatically
|
||||||
|
/// dropped and Lua references to them are invalidated. If a script accesses a value created
|
||||||
|
/// through `Scope` outside of this method, a Lua error will result. Since we can ensure the
|
||||||
|
/// lifetime of values created through `Scope`, and we know that `Lua` cannot be sent to another
|
||||||
|
/// thread while `Scope` is live, it is safe to allow !Send datatypes and whose lifetimes only
|
||||||
|
/// outlive the scope lifetime.
|
||||||
|
///
|
||||||
|
/// Inside the scope callback, all handles created through Scope will share the same unique 'lua
|
||||||
|
/// lifetime of the parent `Lua`. This allows scoped and non-scoped values to be mixed in
|
||||||
|
/// API calls, which is very useful (e.g. passing a scoped userdata to a non-scoped function).
|
||||||
|
/// However, this also enables handles to scoped values to be trivially leaked from the given
|
||||||
|
/// callback. This is not dangerous, though! After the callback returns, all scoped values are
|
||||||
|
/// invalidated, which means that though references may exist, the Rust types backing them have
|
||||||
|
/// 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, F>(&'lua self, f: F) -> Result<R>
|
||||||
|
where
|
||||||
|
'lua: 'scope,
|
||||||
|
R: 'static,
|
||||||
|
F: FnOnce(&Scope<'lua, 'scope>) -> Result<R>,
|
||||||
|
{
|
||||||
|
f(&Scope::new(self))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An asynchronous version of [`scope`] that allows to create scoped async functions and
|
||||||
|
/// execute them.
|
||||||
|
///
|
||||||
|
/// [`scope`]: #method.scope
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
pub fn async_scope<'lua, 'scope, R, F, FR>(
|
||||||
|
&'lua self,
|
||||||
|
f: F,
|
||||||
|
) -> LocalBoxFuture<'scope, Result<R>>
|
||||||
|
where
|
||||||
|
'lua: 'scope,
|
||||||
|
R: 'static,
|
||||||
|
F: FnOnce(Scope<'lua, 'scope>) -> FR,
|
||||||
|
FR: 'scope + Future<Output = Result<R>>,
|
||||||
|
{
|
||||||
|
Box::pin(f(Scope::new(self)))
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts to coerce a Lua value into a String in a manner consistent with Lua's internal
|
/// Attempts to coerce a Lua value into a String in a manner consistent with Lua's internal
|
||||||
/// behavior.
|
/// behavior.
|
||||||
///
|
///
|
||||||
|
|
|
@ -0,0 +1,601 @@
|
||||||
|
use std::any::Any;
|
||||||
|
use std::cell::{Cell, RefCell};
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::mem;
|
||||||
|
use std::os::raw::c_void;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::error::{Error, Result};
|
||||||
|
use crate::ffi;
|
||||||
|
use crate::function::Function;
|
||||||
|
use crate::lua::Lua;
|
||||||
|
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||||
|
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
||||||
|
use crate::util::{
|
||||||
|
assert_stack, init_userdata_metatable, protect_lua_closure, push_string, push_userdata,
|
||||||
|
take_userdata, StackGuard,
|
||||||
|
};
|
||||||
|
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti, Value};
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
use {
|
||||||
|
crate::types::AsyncCallback,
|
||||||
|
futures_core::future::Future,
|
||||||
|
futures_util::future::{self, TryFutureExt},
|
||||||
|
std::os::raw::c_char,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||||
|
pub struct Scope<'lua, 'scope> {
|
||||||
|
lua: &'lua Lua,
|
||||||
|
destructors: RefCell<Vec<(LuaRef<'lua>, fn(LuaRef<'lua>) -> Vec<Box<dyn Any>>)>>,
|
||||||
|
_scope_invariant: PhantomData<Cell<&'scope ()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
|
pub(crate) fn new(lua: &'lua Lua) -> Scope<'lua, 'scope> {
|
||||||
|
Scope {
|
||||||
|
lua,
|
||||||
|
destructors: RefCell::new(Vec::new()),
|
||||||
|
_scope_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.
|
||||||
|
///
|
||||||
|
/// [`Lua::create_function`]: struct.Lua.html#method.create_function
|
||||||
|
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||||
|
pub fn create_function<'callback, A, R, F>(&'callback self, func: F) -> Result<Function<'lua>>
|
||||||
|
where
|
||||||
|
A: FromLuaMulti<'callback>,
|
||||||
|
R: ToLuaMulti<'callback>,
|
||||||
|
F: 'scope + Fn(&'callback Lua, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
// Safe, because 'scope must outlive 'callback (due to Self containing 'scope), however the
|
||||||
|
// callback itself must be 'scope lifetime, so the function should not be able to capture
|
||||||
|
// anything of 'callback lifetime. 'scope can't be shortened due to being invariant, and
|
||||||
|
// the 'callback lifetime here can't be enlarged due to coming from a universal
|
||||||
|
// quantification in Lua::scope.
|
||||||
|
//
|
||||||
|
// I hope I got this explanation right, but in any case this is tested with compiletest_rs
|
||||||
|
// to make sure callbacks can't capture handles with lifetime outside the scope, inside the
|
||||||
|
// scope, and owned inside the callback itself.
|
||||||
|
unsafe {
|
||||||
|
self.create_callback(Box::new(move |lua, args| {
|
||||||
|
func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// [`Lua::create_function_mut`]: struct.Lua.html#method.create_function_mut
|
||||||
|
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||||
|
/// [`Scope::create_function`]: #method.create_function
|
||||||
|
pub fn create_function_mut<'callback, A, R, F>(
|
||||||
|
&'callback self,
|
||||||
|
func: F,
|
||||||
|
) -> Result<Function<'lua>>
|
||||||
|
where
|
||||||
|
A: FromLuaMulti<'callback>,
|
||||||
|
R: ToLuaMulti<'callback>,
|
||||||
|
F: 'scope + FnMut(&'callback Lua, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
let func = RefCell::new(func);
|
||||||
|
self.create_function(move |lua, args| {
|
||||||
|
(&mut *func
|
||||||
|
.try_borrow_mut()
|
||||||
|
.map_err(|_| Error::RecursiveMutCallback)?)(lua, args)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wraps a Rust async function or closure, creating a callable Lua function handle to it.
|
||||||
|
///
|
||||||
|
/// This is a version of [`Lua::create_async_function`] that creates a callback which expires on
|
||||||
|
/// scope drop. See [`Lua::scope`] and [`Lua::async_scope`] for more details.
|
||||||
|
///
|
||||||
|
/// [`Lua::create_async_function`]: struct.Lua.html#method.create_async_function
|
||||||
|
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||||
|
/// [`Lua::async_scope`]: struct.Lua.html#method.async_scope
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
pub fn create_async_function<'callback, A, R, F, FR>(
|
||||||
|
&'callback self,
|
||||||
|
func: F,
|
||||||
|
) -> Result<Function<'lua>>
|
||||||
|
where
|
||||||
|
A: FromLuaMulti<'callback>,
|
||||||
|
R: ToLuaMulti<'callback>,
|
||||||
|
F: 'scope + Fn(&'callback Lua, A) -> FR,
|
||||||
|
FR: 'callback + Future<Output = Result<R>>,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
self.create_async_callback(Box::new(move |lua, args| {
|
||||||
|
let args = match A::from_lua_multi(args, lua) {
|
||||||
|
Ok(args) => args,
|
||||||
|
Err(e) => return Box::pin(future::err(e)),
|
||||||
|
};
|
||||||
|
Box::pin(func(lua, args).and_then(move |ret| future::ready(ret.to_lua_multi(lua))))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create 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 (but still requires that the
|
||||||
|
/// UserData be 'static).
|
||||||
|
/// See [`Lua::scope`] for more details.
|
||||||
|
///
|
||||||
|
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
|
||||||
|
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||||
|
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
|
||||||
|
where
|
||||||
|
T: 'static + UserData,
|
||||||
|
{
|
||||||
|
// Safe even though T may not be Send, because the parent Lua cannot be sent to another
|
||||||
|
// thread while the Scope is alive (or the returned AnyUserData handle even).
|
||||||
|
unsafe {
|
||||||
|
let u = self.lua.make_userdata(data)?;
|
||||||
|
self.destructors.borrow_mut().push((u.0.clone(), |u| {
|
||||||
|
let state = u.lua.state;
|
||||||
|
assert_stack(state, 2);
|
||||||
|
u.lua.push_ref(&u);
|
||||||
|
// We know the destructor has not run yet because we hold a reference to the
|
||||||
|
// userdata.
|
||||||
|
vec![Box::new(take_userdata::<RefCell<T>>(state))]
|
||||||
|
}));
|
||||||
|
Ok(u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create 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.
|
||||||
|
///
|
||||||
|
/// Lifting the requirement that the UserData type be 'static comes with some important
|
||||||
|
/// limitations, so if you only need to eliminate the Send requirement, it is probably better to
|
||||||
|
/// use [`Scope::create_userdata`] instead.
|
||||||
|
///
|
||||||
|
/// The main limitation that comes from using non-'static userdata is that the produced userdata
|
||||||
|
/// will no longer have a `TypeId` associated with it, becuase `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.
|
||||||
|
///
|
||||||
|
/// [`Scope::create_userdata`]: #method.create_userdata
|
||||||
|
/// [`Lua::create_userdata`]: struct.Lua.html#method.create_userdata
|
||||||
|
/// [`Lua::scope`]: struct.Lua.html#method.scope
|
||||||
|
/// [`UserDataMethods`]: trait.UserDataMethods.html
|
||||||
|
pub fn create_nonstatic_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
|
||||||
|
where
|
||||||
|
T: 'scope + UserData,
|
||||||
|
{
|
||||||
|
let data = Rc::new(RefCell::new(data));
|
||||||
|
|
||||||
|
// 'callback outliving 'scope is a lie to make the types work out, required due to the
|
||||||
|
// inability to work with the more correct callback type that is universally quantified over
|
||||||
|
// 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua
|
||||||
|
// lifetime, so none of the static methods UserData types can add can possibly capture
|
||||||
|
// parameters.
|
||||||
|
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
||||||
|
scope: &Scope<'lua, 'scope>,
|
||||||
|
data: Rc<RefCell<T>>,
|
||||||
|
method: NonStaticMethod<'callback, T>,
|
||||||
|
) -> Result<Function<'lua>> {
|
||||||
|
// On methods that actually receive the userdata, we fake a type check on the passed in
|
||||||
|
// userdata, where we pretend there is a unique type per call to
|
||||||
|
// `Scope::create_nonstatic_userdata`. You can grab a method from a userdata and call
|
||||||
|
// it on a mismatched userdata type, which when using normal 'static userdata will fail
|
||||||
|
// with a type mismatch, but here without this check would proceed as though you had
|
||||||
|
// called the method on the original value (since we otherwise completely ignore the
|
||||||
|
// first argument).
|
||||||
|
let check_data = data.clone();
|
||||||
|
let check_ud_type = move |lua: &'callback Lua, value| {
|
||||||
|
if let Some(value) = value {
|
||||||
|
if let Value::UserData(u) = value {
|
||||||
|
unsafe {
|
||||||
|
assert_stack(lua.state, 1);
|
||||||
|
lua.push_ref(&u.0);
|
||||||
|
ffi::lua_getuservalue(lua.state, -1);
|
||||||
|
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||||
|
{
|
||||||
|
ffi::lua_rawgeti(lua.state, -1, 1);
|
||||||
|
ffi::lua_remove(lua.state, -2);
|
||||||
|
}
|
||||||
|
return ffi::lua_touserdata(lua.state, -1)
|
||||||
|
== check_data.as_ptr() as *mut c_void;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
match method {
|
||||||
|
NonStaticMethod::Method(method) => {
|
||||||
|
let method_data = data.clone();
|
||||||
|
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
||||||
|
if !check_ud_type(lua, args.pop_front()) {
|
||||||
|
return Err(Error::UserDataTypeMismatch);
|
||||||
|
}
|
||||||
|
let data = method_data
|
||||||
|
.try_borrow()
|
||||||
|
.map_err(|_| Error::UserDataBorrowError)?;
|
||||||
|
method(lua, &*data, args)
|
||||||
|
});
|
||||||
|
unsafe { scope.create_callback(f) }
|
||||||
|
}
|
||||||
|
NonStaticMethod::MethodMut(method) => {
|
||||||
|
let method = RefCell::new(method);
|
||||||
|
let method_data = data.clone();
|
||||||
|
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
||||||
|
if !check_ud_type(lua, args.pop_front()) {
|
||||||
|
return Err(Error::UserDataTypeMismatch);
|
||||||
|
}
|
||||||
|
let mut method = method
|
||||||
|
.try_borrow_mut()
|
||||||
|
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||||
|
let mut data = method_data
|
||||||
|
.try_borrow_mut()
|
||||||
|
.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||||
|
(&mut *method)(lua, &mut *data, args)
|
||||||
|
});
|
||||||
|
unsafe { scope.create_callback(f) }
|
||||||
|
}
|
||||||
|
NonStaticMethod::Function(function) => unsafe { scope.create_callback(function) },
|
||||||
|
NonStaticMethod::FunctionMut(function) => {
|
||||||
|
let function = RefCell::new(function);
|
||||||
|
let f = Box::new(move |lua, args| {
|
||||||
|
(&mut *function
|
||||||
|
.try_borrow_mut()
|
||||||
|
.map_err(|_| Error::RecursiveMutCallback)?)(
|
||||||
|
lua, args
|
||||||
|
)
|
||||||
|
});
|
||||||
|
unsafe { scope.create_callback(f) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ud_methods = NonStaticUserDataMethods::default();
|
||||||
|
T::add_methods(&mut ud_methods);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let lua = self.lua;
|
||||||
|
let _sg = StackGuard::new(lua.state);
|
||||||
|
assert_stack(lua.state, 6);
|
||||||
|
|
||||||
|
push_userdata(lua.state, ())?;
|
||||||
|
#[cfg(feature = "lua53")]
|
||||||
|
ffi::lua_pushlightuserdata(lua.state, data.as_ptr() as *mut c_void);
|
||||||
|
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||||
|
protect_lua_closure(lua.state, 0, 1, |state| {
|
||||||
|
// Lua 5.2/5.1 allows to store only table. Then we will wrap the value.
|
||||||
|
ffi::lua_createtable(state, 1, 0);
|
||||||
|
ffi::lua_pushlightuserdata(state, data.as_ptr() as *mut c_void);
|
||||||
|
ffi::lua_rawseti(state, -2, 1);
|
||||||
|
})?;
|
||||||
|
ffi::lua_setuservalue(lua.state, -2);
|
||||||
|
|
||||||
|
protect_lua_closure(lua.state, 0, 1, move |state| {
|
||||||
|
ffi::lua_newtable(state);
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for (k, m) in ud_methods.meta_methods {
|
||||||
|
push_string(lua.state, k.name())?;
|
||||||
|
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
|
||||||
|
|
||||||
|
protect_lua_closure(lua.state, 3, 1, |state| {
|
||||||
|
ffi::lua_rawset(state, -3);
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ud_methods.methods.is_empty() {
|
||||||
|
init_userdata_metatable::<()>(lua.state, -1, None)?;
|
||||||
|
} else {
|
||||||
|
protect_lua_closure(lua.state, 0, 1, |state| {
|
||||||
|
ffi::lua_newtable(state);
|
||||||
|
})?;
|
||||||
|
for (k, m) in ud_methods.methods {
|
||||||
|
push_string(lua.state, &k)?;
|
||||||
|
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
|
||||||
|
protect_lua_closure(lua.state, 3, 1, |state| {
|
||||||
|
ffi::lua_rawset(state, -3);
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_userdata_metatable::<()>(lua.state, -2, Some(-1))?;
|
||||||
|
ffi::lua_pop(lua.state, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ffi::lua_setmetatable(lua.state, -2);
|
||||||
|
|
||||||
|
Ok(AnyUserData(lua.pop_ref()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unsafe, because the callback can improperly capture any value with 'callback scope, such as
|
||||||
|
// improperly capturing an argument. Since the 'callback lifetime is chosen by the user and the
|
||||||
|
// lifetime of the callback itself is 'scope (non-'static), the borrow checker will happily pick
|
||||||
|
// a 'callback that outlives 'scope to allow this. In order for this to be safe, the callback
|
||||||
|
// must NOT capture any parameters.
|
||||||
|
unsafe fn create_callback<'callback>(
|
||||||
|
&self,
|
||||||
|
f: Callback<'callback, 'scope>,
|
||||||
|
) -> Result<Function<'lua>> {
|
||||||
|
let f = mem::transmute::<Callback<'callback, 'scope>, Callback<'lua, 'static>>(f);
|
||||||
|
let f = self.lua.create_callback(f)?;
|
||||||
|
|
||||||
|
let mut destructors = self.destructors.borrow_mut();
|
||||||
|
destructors.push((f.0.clone(), |f| {
|
||||||
|
let state = f.lua.state;
|
||||||
|
assert_stack(state, 3);
|
||||||
|
f.lua.push_ref(&f);
|
||||||
|
|
||||||
|
// We know the destructor has not run yet because we hold a reference to the callback.
|
||||||
|
|
||||||
|
ffi::lua_getupvalue(state, -1, 1);
|
||||||
|
let ud1 = take_userdata::<Callback>(state);
|
||||||
|
ffi::lua_pushnil(state);
|
||||||
|
ffi::lua_setupvalue(state, -2, 1);
|
||||||
|
|
||||||
|
ffi::lua_getupvalue(state, -1, 2);
|
||||||
|
let ud2 = take_userdata::<Lua>(state);
|
||||||
|
ffi::lua_pushnil(state);
|
||||||
|
ffi::lua_setupvalue(state, -2, 2);
|
||||||
|
|
||||||
|
ffi::lua_pop(state, 1);
|
||||||
|
vec![Box::new(ud1), Box::new(ud2)]
|
||||||
|
}));
|
||||||
|
Ok(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
unsafe fn create_async_callback<'callback>(
|
||||||
|
&self,
|
||||||
|
f: AsyncCallback<'callback, 'scope>,
|
||||||
|
) -> Result<Function<'lua>> {
|
||||||
|
let f = mem::transmute::<AsyncCallback<'callback, 'scope>, AsyncCallback<'lua, 'static>>(f);
|
||||||
|
let f = self.lua.create_async_callback(f)?;
|
||||||
|
|
||||||
|
let mut destructors = self.destructors.borrow_mut();
|
||||||
|
destructors.push((f.0.clone(), |f| {
|
||||||
|
let state = f.lua.state;
|
||||||
|
assert_stack(state, 4);
|
||||||
|
f.lua.push_ref(&f);
|
||||||
|
|
||||||
|
// We know the destructor has not run yet because we hold a reference to the callback.
|
||||||
|
|
||||||
|
// First, get the environment table
|
||||||
|
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||||
|
ffi::lua_getupvalue(state, -1, 1);
|
||||||
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
|
ffi::lua_getfenv(state, -1);
|
||||||
|
|
||||||
|
// Then, get the get_poll() closure using the corresponding key
|
||||||
|
let key = "get_poll";
|
||||||
|
ffi::lua_pushlstring(state, key.as_ptr() as *const c_char, key.len());
|
||||||
|
ffi::lua_rawget(state, -2);
|
||||||
|
|
||||||
|
// Finally, destroy all upvalues
|
||||||
|
ffi::lua_getupvalue(state, -1, 1);
|
||||||
|
let ud1 = take_userdata::<AsyncCallback>(state);
|
||||||
|
ffi::lua_pushnil(state);
|
||||||
|
ffi::lua_setupvalue(state, -2, 1);
|
||||||
|
|
||||||
|
ffi::lua_getupvalue(state, -1, 2);
|
||||||
|
let ud2 = take_userdata::<Lua>(state);
|
||||||
|
ffi::lua_pushnil(state);
|
||||||
|
ffi::lua_setupvalue(state, -2, 2);
|
||||||
|
|
||||||
|
ffi::lua_pop(state, 1);
|
||||||
|
|
||||||
|
vec![Box::new(ud1), Box::new(ud2)]
|
||||||
|
}));
|
||||||
|
|
||||||
|
Ok(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
|
||||||
|
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.
|
||||||
|
|
||||||
|
// All destructors are non-panicking, so this is fine
|
||||||
|
let to_drop = self
|
||||||
|
.destructors
|
||||||
|
.get_mut()
|
||||||
|
.drain(..)
|
||||||
|
.flat_map(|(r, dest)| dest(r))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
drop(to_drop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum NonStaticMethod<'lua, T> {
|
||||||
|
Method(Box<dyn Fn(&'lua Lua, &T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||||
|
MethodMut(Box<dyn FnMut(&'lua Lua, &mut T, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||||
|
Function(Box<dyn Fn(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||||
|
FunctionMut(Box<dyn FnMut(&'lua Lua, MultiValue<'lua>) -> Result<MultiValue<'lua>>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NonStaticUserDataMethods<'lua, T: UserData> {
|
||||||
|
methods: Vec<(Vec<u8>, NonStaticMethod<'lua, T>)>,
|
||||||
|
meta_methods: Vec<(MetaMethod, NonStaticMethod<'lua, T>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> {
|
||||||
|
fn default() -> NonStaticUserDataMethods<'lua, T> {
|
||||||
|
NonStaticUserDataMethods {
|
||||||
|
methods: Vec::new(),
|
||||||
|
meta_methods: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'lua, T> {
|
||||||
|
fn add_method<S, A, R, M>(&mut self, name: &S, method: M)
|
||||||
|
where
|
||||||
|
S: AsRef<[u8]> + ?Sized,
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
self.methods.push((
|
||||||
|
name.as_ref().to_vec(),
|
||||||
|
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
||||||
|
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
|
})),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_method_mut<S, A, R, M>(&mut self, name: &S, mut method: M)
|
||||||
|
where
|
||||||
|
S: AsRef<[u8]> + ?Sized,
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
self.methods.push((
|
||||||
|
name.as_ref().to_vec(),
|
||||||
|
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||||
|
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
|
})),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
fn add_async_method<S, A, R, M, MR>(&mut self, _name: &S, _method: M)
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
S: AsRef<[u8]> + ?Sized,
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
|
||||||
|
MR: 'lua + Future<Output = Result<R>>,
|
||||||
|
{
|
||||||
|
// The panic should never happen as async non-static code wouldn't compile
|
||||||
|
// Non-static lifetime must be bounded to 'lua lifetime
|
||||||
|
mlua_panic!("asynchronous methods are not supported for non-static userdata")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_function<S, A, R, F>(&mut self, name: &S, function: F)
|
||||||
|
where
|
||||||
|
S: AsRef<[u8]> + ?Sized,
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
self.methods.push((
|
||||||
|
name.as_ref().to_vec(),
|
||||||
|
NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||||
|
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
|
})),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_function_mut<S, A, R, F>(&mut self, name: &S, mut function: F)
|
||||||
|
where
|
||||||
|
S: AsRef<[u8]> + ?Sized,
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
self.methods.push((
|
||||||
|
name.as_ref().to_vec(),
|
||||||
|
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||||
|
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
|
})),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async")]
|
||||||
|
fn add_async_function<S, A, R, F, FR>(&mut self, _name: &S, _function: F)
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
S: AsRef<[u8]> + ?Sized,
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
|
||||||
|
FR: 'lua + Future<Output = Result<R>>,
|
||||||
|
{
|
||||||
|
// The panic should never happen as async non-static code wouldn't compile
|
||||||
|
// Non-static lifetime must be bounded to 'lua lifetime
|
||||||
|
mlua_panic!("asynchronous functions are not supported for non-static userdata")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_meta_method<A, R, M>(&mut self, meta: MetaMethod, method: M)
|
||||||
|
where
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
M: 'static + MaybeSend + Fn(&'lua Lua, &T, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
self.meta_methods.push((
|
||||||
|
meta,
|
||||||
|
NonStaticMethod::Method(Box::new(move |lua, ud, args| {
|
||||||
|
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
|
})),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_meta_method_mut<A, R, M>(&mut self, meta: MetaMethod, mut method: M)
|
||||||
|
where
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
self.meta_methods.push((
|
||||||
|
meta,
|
||||||
|
NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| {
|
||||||
|
method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
|
})),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_meta_function<A, R, F>(&mut self, meta: MetaMethod, function: F)
|
||||||
|
where
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
self.meta_methods.push((
|
||||||
|
meta,
|
||||||
|
NonStaticMethod::Function(Box::new(move |lua, args| {
|
||||||
|
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
|
})),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_meta_function_mut<A, R, F>(&mut self, meta: MetaMethod, mut function: F)
|
||||||
|
where
|
||||||
|
A: FromLuaMulti<'lua>,
|
||||||
|
R: ToLuaMulti<'lua>,
|
||||||
|
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>,
|
||||||
|
{
|
||||||
|
self.meta_methods.push((
|
||||||
|
meta,
|
||||||
|
NonStaticMethod::FunctionMut(Box::new(move |lua, args| {
|
||||||
|
function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||||
|
})),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
107
tests/async.rs
107
tests/async.rs
|
@ -1,5 +1,7 @@
|
||||||
#![cfg(feature = "async")]
|
#![cfg(feature = "async")]
|
||||||
|
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::{
|
use std::sync::{
|
||||||
atomic::{AtomicI64, Ordering},
|
atomic::{AtomicI64, Ordering},
|
||||||
Arc,
|
Arc,
|
||||||
|
@ -286,8 +288,8 @@ async fn test_async_userdata() -> Result<()> {
|
||||||
r#"
|
r#"
|
||||||
assert(userdata:get_value() == 11)
|
assert(userdata:get_value() == 11)
|
||||||
userdata:set_value(12)
|
userdata:set_value(12)
|
||||||
assert(userdata:get_value() == 12)
|
|
||||||
assert(userdata.sleep(5) == "elapsed:5ms")
|
assert(userdata.sleep(5) == "elapsed:5ms")
|
||||||
|
assert(userdata:get_value() == 12)
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.exec_async()
|
.exec_async()
|
||||||
|
@ -295,3 +297,106 @@ async fn test_async_userdata() -> Result<()> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async_scope() -> Result<()> {
|
||||||
|
let ref lua = Lua::new();
|
||||||
|
|
||||||
|
let ref rc = Rc::new(Cell::new(0));
|
||||||
|
|
||||||
|
let fut = lua.async_scope(|scope| async move {
|
||||||
|
let f = scope.create_async_function(move |_, n: u64| {
|
||||||
|
let rc2 = rc.clone();
|
||||||
|
async move {
|
||||||
|
rc2.set(42);
|
||||||
|
Delay::new(Duration::from_millis(n)).await;
|
||||||
|
assert_eq!(Rc::strong_count(&rc2), 2);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
lua.globals().set("f", f.clone())?;
|
||||||
|
|
||||||
|
assert_eq!(Rc::strong_count(rc), 1);
|
||||||
|
let _ = f.call_async::<u64, ()>(10).await?;
|
||||||
|
assert_eq!(Rc::strong_count(rc), 1);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(Rc::strong_count(rc), 1);
|
||||||
|
let _ = fut.await?;
|
||||||
|
|
||||||
|
match lua
|
||||||
|
.globals()
|
||||||
|
.get::<_, Function>("f")?
|
||||||
|
.call_async::<_, ()>(10)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||||
|
Error::CallbackDestructed => {}
|
||||||
|
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
|
||||||
|
},
|
||||||
|
r => panic!("improper return for destructed function: {:?}", r),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_async_scope_userdata() -> Result<()> {
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct MyUserData(Arc<AtomicI64>);
|
||||||
|
|
||||||
|
impl UserData for MyUserData {
|
||||||
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_async_method("get_value", |_, data, ()| async move {
|
||||||
|
Delay::new(Duration::from_millis(10)).await;
|
||||||
|
Ok(data.0.load(Ordering::Relaxed))
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_async_method("set_value", |_, data, n| async move {
|
||||||
|
Delay::new(Duration::from_millis(10)).await;
|
||||||
|
data.0.store(n, Ordering::Relaxed);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_async_function("sleep", |_, n| async move {
|
||||||
|
Delay::new(Duration::from_millis(n)).await;
|
||||||
|
Ok(format!("elapsed:{}ms", n))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ref lua = Lua::new();
|
||||||
|
|
||||||
|
let ref arc = Arc::new(AtomicI64::new(11));
|
||||||
|
|
||||||
|
lua.async_scope(|scope| async move {
|
||||||
|
let ud = scope.create_userdata(MyUserData(arc.clone()))?;
|
||||||
|
lua.globals().set("userdata", ud)?;
|
||||||
|
lua.load(
|
||||||
|
r#"
|
||||||
|
assert(userdata:get_value() == 11)
|
||||||
|
userdata:set_value(12)
|
||||||
|
assert(userdata.sleep(5) == "elapsed:5ms")
|
||||||
|
assert(userdata:get_value() == 12)
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.exec_async()
|
||||||
|
.await
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
assert_eq!(Arc::strong_count(arc), 1);
|
||||||
|
|
||||||
|
match lua.load("userdata:get_value()").exec_async().await {
|
||||||
|
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
|
||||||
|
Error::CallbackDestructed => {}
|
||||||
|
e => panic!("expected `CallbackDestructed` error cause, got {:?}", e),
|
||||||
|
},
|
||||||
|
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
use mlua::{Lua, UserData, UserDataMethods};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let ref lua = Lua::new();
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct MyUserData<'a>(&'a i64);
|
||||||
|
|
||||||
|
impl<'a> UserData for MyUserData<'a> {
|
||||||
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_async_method("print", |_, data, ()| async move {
|
||||||
|
println!("{}", data.0);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
|
||||||
|
--> $DIR/scope_async_userdata.rs:11:72
|
||||||
|
|
|
||||||
|
11 | methods.add_async_method("print", |_, data, ()| async move {
|
||||||
|
| ________________________________________________________________________^
|
||||||
|
12 | | println!("{}", data.0);
|
||||||
|
13 | | Ok(())
|
||||||
|
14 | | });
|
||||||
|
| |_____________^
|
||||||
|
|
|
||||||
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined on the impl at 9:10...
|
||||||
|
--> $DIR/scope_async_userdata.rs:9:10
|
||||||
|
|
|
||||||
|
9 | impl<'a> UserData for MyUserData<'a> {
|
||||||
|
| ^^
|
||||||
|
note: ...so that the types are compatible
|
||||||
|
--> $DIR/scope_async_userdata.rs:11:72
|
||||||
|
|
|
||||||
|
11 | methods.add_async_method("print", |_, data, ()| async move {
|
||||||
|
| ________________________________________________________________________^
|
||||||
|
12 | | println!("{}", data.0);
|
||||||
|
13 | | Ok(())
|
||||||
|
14 | | });
|
||||||
|
| |_____________^
|
||||||
|
= note: expected `main::MyUserData<'_>`
|
||||||
|
found `main::MyUserData<'a>`
|
||||||
|
note: but, the lifetime must be valid for the lifetime `'lua` as defined on the method body at 10:24...
|
||||||
|
--> $DIR/scope_async_userdata.rs:10:24
|
||||||
|
|
|
||||||
|
10 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
| ^^^^
|
||||||
|
note: ...so that the type `impl std::future::Future` will meet its required lifetime bounds
|
||||||
|
--> $DIR/scope_async_userdata.rs:11:21
|
||||||
|
|
|
||||||
|
11 | methods.add_async_method("print", |_, data, ()| async move {
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
|
@ -9,49 +9,3 @@ error[E0277]: the type `std::cell::UnsafeCell<()>` may contain interior mutabili
|
||||||
= note: required because it appears within the type `mlua::lua::Lua`
|
= note: required because it appears within the type `mlua::lua::Lua`
|
||||||
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
|
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
|
||||||
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]`
|
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]`
|
||||||
|
|
||||||
error[E0277]: the type `std::cell::UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
|
||||||
--> $DIR/lua_norefunwindsafe.rs:7:5
|
|
||||||
|
|
|
||||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
|
||||||
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
|
||||||
|
|
|
||||||
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<usize>`
|
|
||||||
= note: required because it appears within the type `std::cell::Cell<usize>`
|
|
||||||
= note: required because it appears within the type `std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>`
|
|
||||||
= note: required because it appears within the type `std::marker::PhantomData<std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>>`
|
|
||||||
= note: required because it appears within the type `std::rc::Rc<std::cell::RefCell<mlua::lua::ExtraData>>`
|
|
||||||
= note: required because it appears within the type `mlua::lua::Lua`
|
|
||||||
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
|
|
||||||
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]`
|
|
||||||
|
|
||||||
error[E0277]: the type `std::cell::UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
|
||||||
--> $DIR/lua_norefunwindsafe.rs:7:5
|
|
||||||
|
|
|
||||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
|
||||||
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
|
||||||
|
|
|
||||||
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<mlua::lua::ExtraData>`
|
|
||||||
= note: required because it appears within the type `std::cell::RefCell<mlua::lua::ExtraData>`
|
|
||||||
= note: required because it appears within the type `std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>`
|
|
||||||
= note: required because it appears within the type `std::marker::PhantomData<std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>>`
|
|
||||||
= note: required because it appears within the type `std::rc::Rc<std::cell::RefCell<mlua::lua::ExtraData>>`
|
|
||||||
= note: required because it appears within the type `mlua::lua::Lua`
|
|
||||||
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
|
|
||||||
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]`
|
|
||||||
|
|
||||||
error[E0277]: the type `std::cell::UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
|
||||||
--> $DIR/lua_norefunwindsafe.rs:7:5
|
|
||||||
|
|
|
||||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
|
||||||
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
|
||||||
|
|
|
||||||
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<isize>`
|
|
||||||
= note: required because it appears within the type `std::cell::Cell<isize>`
|
|
||||||
= note: required because it appears within the type `std::cell::RefCell<mlua::lua::ExtraData>`
|
|
||||||
= note: required because it appears within the type `std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>`
|
|
||||||
= note: required because it appears within the type `std::marker::PhantomData<std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>>`
|
|
||||||
= note: required because it appears within the type `std::rc::Rc<std::cell::RefCell<mlua::lua::ExtraData>>`
|
|
||||||
= note: required because it appears within the type `mlua::lua::Lua`
|
|
||||||
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
|
|
||||||
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/lua_norefunwindsafe.rs:7:18: 7:48 lua:&mlua::lua::Lua]`
|
|
||||||
|
|
|
@ -11,55 +11,3 @@ error[E0277]: the type `std::cell::UnsafeCell<()>` may contain interior mutabili
|
||||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
||||||
= note: required because it appears within the type `mlua::table::Table<'_>`
|
= note: required because it appears within the type `mlua::table::Table<'_>`
|
||||||
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/ref_nounwindsafe.rs:8:18: 8:54 table:mlua::table::Table<'_>]`
|
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/ref_nounwindsafe.rs:8:18: 8:54 table:mlua::table::Table<'_>]`
|
||||||
|
|
||||||
error[E0277]: the type `std::cell::UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
|
||||||
--> $DIR/ref_nounwindsafe.rs:8:5
|
|
||||||
|
|
|
||||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
|
||||||
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
|
||||||
|
|
|
||||||
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<usize>`
|
|
||||||
= note: required because it appears within the type `std::cell::Cell<usize>`
|
|
||||||
= note: required because it appears within the type `std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>`
|
|
||||||
= note: required because it appears within the type `std::marker::PhantomData<std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>>`
|
|
||||||
= note: required because it appears within the type `std::rc::Rc<std::cell::RefCell<mlua::lua::ExtraData>>`
|
|
||||||
= note: required because it appears within the type `mlua::lua::Lua`
|
|
||||||
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
|
|
||||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
|
||||||
= note: required because it appears within the type `mlua::table::Table<'_>`
|
|
||||||
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/ref_nounwindsafe.rs:8:18: 8:54 table:mlua::table::Table<'_>]`
|
|
||||||
|
|
||||||
error[E0277]: the type `std::cell::UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
|
||||||
--> $DIR/ref_nounwindsafe.rs:8:5
|
|
||||||
|
|
|
||||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
|
||||||
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
|
||||||
|
|
|
||||||
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<mlua::lua::ExtraData>`
|
|
||||||
= note: required because it appears within the type `std::cell::RefCell<mlua::lua::ExtraData>`
|
|
||||||
= note: required because it appears within the type `std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>`
|
|
||||||
= note: required because it appears within the type `std::marker::PhantomData<std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>>`
|
|
||||||
= note: required because it appears within the type `std::rc::Rc<std::cell::RefCell<mlua::lua::ExtraData>>`
|
|
||||||
= note: required because it appears within the type `mlua::lua::Lua`
|
|
||||||
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
|
|
||||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
|
||||||
= note: required because it appears within the type `mlua::table::Table<'_>`
|
|
||||||
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/ref_nounwindsafe.rs:8:18: 8:54 table:mlua::table::Table<'_>]`
|
|
||||||
|
|
||||||
error[E0277]: the type `std::cell::UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
|
||||||
--> $DIR/ref_nounwindsafe.rs:8:5
|
|
||||||
|
|
|
||||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
|
||||||
| ^^^^^^^^^^^^ `std::cell::UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
|
||||||
|
|
|
||||||
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<isize>`
|
|
||||||
= note: required because it appears within the type `std::cell::Cell<isize>`
|
|
||||||
= note: required because it appears within the type `std::cell::RefCell<mlua::lua::ExtraData>`
|
|
||||||
= note: required because it appears within the type `std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>`
|
|
||||||
= note: required because it appears within the type `std::marker::PhantomData<std::rc::RcBox<std::cell::RefCell<mlua::lua::ExtraData>>>`
|
|
||||||
= note: required because it appears within the type `std::rc::Rc<std::cell::RefCell<mlua::lua::ExtraData>>`
|
|
||||||
= note: required because it appears within the type `mlua::lua::Lua`
|
|
||||||
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
|
|
||||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
|
||||||
= note: required because it appears within the type `mlua::table::Table<'_>`
|
|
||||||
= note: required because it appears within the type `[closure@$DIR/tests/compile_fail/ref_nounwindsafe.rs:8:18: 8:54 table:mlua::table::Table<'_>]`
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
use mlua::{Lua, Table};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let lua = Lua::new();
|
||||||
|
lua.scope(|scope| {
|
||||||
|
let mut inner: Option<Table> = None;
|
||||||
|
let f = scope
|
||||||
|
.create_function_mut(move |_, t: Table| {
|
||||||
|
if let Some(old) = inner.take() {
|
||||||
|
// Access old callback `Lua`.
|
||||||
|
}
|
||||||
|
inner = Some(t);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
f.call::<_, ()>(lua.create_table()?)?;
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
|
||||||
|
--> $DIR/scope_callback_capture.rs:8:14
|
||||||
|
|
|
||||||
|
8 | .create_function_mut(move |_, t: Table| {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 5:15...
|
||||||
|
--> $DIR/scope_callback_capture.rs:5:15
|
||||||
|
|
|
||||||
|
5 | lua.scope(|scope| {
|
||||||
|
| _______________^
|
||||||
|
6 | | let mut inner: Option<Table> = None;
|
||||||
|
7 | | let f = scope
|
||||||
|
8 | | .create_function_mut(move |_, t: Table| {
|
||||||
|
... |
|
||||||
|
16 | | Ok(())
|
||||||
|
17 | | });
|
||||||
|
| |_____^
|
||||||
|
note: ...so that reference does not outlive borrowed content
|
||||||
|
--> $DIR/scope_callback_capture.rs:7:17
|
||||||
|
|
|
||||||
|
7 | let f = scope
|
||||||
|
| ^^^^^
|
||||||
|
note: but, the lifetime must be valid for the method call at 5:5...
|
||||||
|
--> $DIR/scope_callback_capture.rs:5:5
|
||||||
|
|
|
||||||
|
5 | / lua.scope(|scope| {
|
||||||
|
6 | | let mut inner: Option<Table> = None;
|
||||||
|
7 | | let f = scope
|
||||||
|
8 | | .create_function_mut(move |_, t: Table| {
|
||||||
|
... |
|
||||||
|
16 | | Ok(())
|
||||||
|
17 | | });
|
||||||
|
| |______^
|
||||||
|
note: ...so that a type/lifetime parameter is in scope here
|
||||||
|
--> $DIR/scope_callback_capture.rs:5:5
|
||||||
|
|
|
||||||
|
5 | / lua.scope(|scope| {
|
||||||
|
6 | | let mut inner: Option<Table> = None;
|
||||||
|
7 | | let f = scope
|
||||||
|
8 | | .create_function_mut(move |_, t: Table| {
|
||||||
|
... |
|
||||||
|
16 | | Ok(())
|
||||||
|
17 | | });
|
||||||
|
| |______^
|
|
@ -0,0 +1,15 @@
|
||||||
|
use mlua::{Lua, Table};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let lua = Lua::new();
|
||||||
|
lua.scope(|scope| {
|
||||||
|
let mut inner: Option<Table> = None;
|
||||||
|
let f = scope
|
||||||
|
.create_function_mut(|_, t: Table| {
|
||||||
|
inner = Some(t);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
f.call::<_, ()>(lua.create_table()?)?;
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
|
||||||
|
--> $DIR/scope_callback_inner.rs:8:14
|
||||||
|
|
|
||||||
|
8 | .create_function_mut(|_, t: Table| {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 5:15...
|
||||||
|
--> $DIR/scope_callback_inner.rs:5:15
|
||||||
|
|
|
||||||
|
5 | lua.scope(|scope| {
|
||||||
|
| _______________^
|
||||||
|
6 | | let mut inner: Option<Table> = None;
|
||||||
|
7 | | let f = scope
|
||||||
|
8 | | .create_function_mut(|_, t: Table| {
|
||||||
|
... |
|
||||||
|
13 | | Ok(())
|
||||||
|
14 | | });
|
||||||
|
| |_____^
|
||||||
|
note: ...so that reference does not outlive borrowed content
|
||||||
|
--> $DIR/scope_callback_inner.rs:7:17
|
||||||
|
|
|
||||||
|
7 | let f = scope
|
||||||
|
| ^^^^^
|
||||||
|
note: but, the lifetime must be valid for the method call at 5:5...
|
||||||
|
--> $DIR/scope_callback_inner.rs:5:5
|
||||||
|
|
|
||||||
|
5 | / lua.scope(|scope| {
|
||||||
|
6 | | let mut inner: Option<Table> = None;
|
||||||
|
7 | | let f = scope
|
||||||
|
8 | | .create_function_mut(|_, t: Table| {
|
||||||
|
... |
|
||||||
|
13 | | Ok(())
|
||||||
|
14 | | });
|
||||||
|
| |______^
|
||||||
|
note: ...so that a type/lifetime parameter is in scope here
|
||||||
|
--> $DIR/scope_callback_inner.rs:5:5
|
||||||
|
|
|
||||||
|
5 | / lua.scope(|scope| {
|
||||||
|
6 | | let mut inner: Option<Table> = None;
|
||||||
|
7 | | let f = scope
|
||||||
|
8 | | .create_function_mut(|_, t: Table| {
|
||||||
|
... |
|
||||||
|
13 | | Ok(())
|
||||||
|
14 | | });
|
||||||
|
| |______^
|
|
@ -0,0 +1,15 @@
|
||||||
|
use mlua::{Lua, Table};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let lua = Lua::new();
|
||||||
|
let mut outer: Option<Table> = None;
|
||||||
|
lua.scope(|scope| {
|
||||||
|
let f = scope
|
||||||
|
.create_function_mut(|_, t: Table| {
|
||||||
|
outer = Some(t);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
f.call::<_, ()>(lua.create_table()?)?;
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
error: borrowed data cannot be stored outside of its closure
|
||||||
|
--> $DIR/scope_callback_outer.rs:7:17
|
||||||
|
|
|
||||||
|
5 | let mut outer: Option<Table> = None;
|
||||||
|
| --------- ...so that variable is valid at time of its declaration
|
||||||
|
6 | lua.scope(|scope| {
|
||||||
|
| ------- borrowed data cannot outlive this closure
|
||||||
|
7 | let f = scope
|
||||||
|
| ^^^^^ cannot be stored outside of its closure
|
||||||
|
8 | .create_function_mut(|_, t: Table| {
|
||||||
|
| ------------------- cannot infer an appropriate lifetime...
|
|
@ -0,0 +1,23 @@
|
||||||
|
use mlua::Lua;
|
||||||
|
|
||||||
|
struct Test {
|
||||||
|
field: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let lua = Lua::new();
|
||||||
|
lua.scope(|scope| {
|
||||||
|
let f = {
|
||||||
|
let mut test = Test { field: 0 };
|
||||||
|
|
||||||
|
scope
|
||||||
|
.create_function_mut(|_, ()| {
|
||||||
|
test.field = 42;
|
||||||
|
//~^ error: `test` does not live long enough
|
||||||
|
Ok(())
|
||||||
|
})?
|
||||||
|
};
|
||||||
|
|
||||||
|
f.call::<_, ()>(())
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
error[E0373]: closure may outlive the current function, but it borrows `test`, which is owned by the current function
|
||||||
|
--> $DIR/scope_invariance.rs:14:38
|
||||||
|
|
|
||||||
|
9 | lua.scope(|scope| {
|
||||||
|
| ----- has type `&mlua::scope::Scope<'_, '1>`
|
||||||
|
...
|
||||||
|
14 | .create_function_mut(|_, ()| {
|
||||||
|
| ^^^^^^^ may outlive borrowed value `test`
|
||||||
|
15 | test.field = 42;
|
||||||
|
| ---- `test` is borrowed here
|
||||||
|
|
|
||||||
|
note: function requires argument type to outlive `'1`
|
||||||
|
--> $DIR/scope_invariance.rs:13:13
|
||||||
|
|
|
||||||
|
13 | / scope
|
||||||
|
14 | | .create_function_mut(|_, ()| {
|
||||||
|
15 | | test.field = 42;
|
||||||
|
16 | | //~^ error: `test` does not live long enough
|
||||||
|
17 | | Ok(())
|
||||||
|
18 | | })?
|
||||||
|
| |__________________^
|
||||||
|
help: to force the closure to take ownership of `test` (and any other referenced variables), use the `move` keyword
|
||||||
|
|
|
||||||
|
14 | .create_function_mut(move |_, ()| {
|
||||||
|
| ^^^^^^^^^^^^
|
|
@ -0,0 +1,15 @@
|
||||||
|
use mlua::{Lua, UserData};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
struct MyUserData<'a>(&'a mut i32);
|
||||||
|
impl<'a> UserData for MyUserData<'a> {};
|
||||||
|
|
||||||
|
let mut i = 1;
|
||||||
|
|
||||||
|
let lua = Lua::new();
|
||||||
|
lua.scope(|scope| {
|
||||||
|
let _a = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
|
||||||
|
let _b = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
error[E0499]: cannot borrow `i` as mutable more than once at a time
|
||||||
|
--> $DIR/scope_mutable_aliasing.rs:12:61
|
||||||
|
|
|
||||||
|
11 | let _a = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
|
||||||
|
| ------ first mutable borrow occurs here
|
||||||
|
12 | let _b = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
|
||||||
|
| ------------------------- ^^^^^^ second mutable borrow occurs here
|
||||||
|
| |
|
||||||
|
| first borrow later used by call
|
|
@ -0,0 +1,19 @@
|
||||||
|
use mlua::{Lua, UserData};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Should not allow userdata borrow to outlive lifetime of AnyUserData handle
|
||||||
|
struct MyUserData<'a>(&'a i32);
|
||||||
|
impl<'a> UserData for MyUserData<'a> {};
|
||||||
|
|
||||||
|
let igood = 1;
|
||||||
|
|
||||||
|
let lua = Lua::new();
|
||||||
|
lua.scope(|scope| {
|
||||||
|
let _ugood = scope.create_nonstatic_userdata(MyUserData(&igood)).unwrap();
|
||||||
|
let _ubad = {
|
||||||
|
let ibad = 42;
|
||||||
|
scope.create_nonstatic_userdata(MyUserData(&ibad)).unwrap();
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
error[E0597]: `ibad` does not live long enough
|
||||||
|
--> $DIR/scope_userdata_borrow.rs:15:56
|
||||||
|
|
|
||||||
|
11 | lua.scope(|scope| {
|
||||||
|
| ----- has type `&mlua::scope::Scope<'_, '1>`
|
||||||
|
...
|
||||||
|
15 | scope.create_nonstatic_userdata(MyUserData(&ibad)).unwrap();
|
||||||
|
| -------------------------------------------^^^^^--
|
||||||
|
| | |
|
||||||
|
| | borrowed value does not live long enough
|
||||||
|
| argument requires that `ibad` is borrowed for `'1`
|
||||||
|
16 | };
|
||||||
|
| - `ibad` dropped here while still borrowed
|
|
@ -0,0 +1,228 @@
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use mlua::{Error, Function, Lua, MetaMethod, Result, String, UserData, UserDataMethods};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scope_func() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let rc = Rc::new(Cell::new(0));
|
||||||
|
lua.scope(|scope| {
|
||||||
|
let r = rc.clone();
|
||||||
|
let f = scope.create_function(move |_, ()| {
|
||||||
|
r.set(42);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
lua.globals().set("bad", f.clone())?;
|
||||||
|
f.call::<_, ()>(())?;
|
||||||
|
assert_eq!(Rc::strong_count(&rc), 2);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
assert_eq!(rc.get(), 42);
|
||||||
|
assert_eq!(Rc::strong_count(&rc), 1);
|
||||||
|
|
||||||
|
match lua.globals().get::<_, Function>("bad")?.call::<_, ()>(()) {
|
||||||
|
Err(Error::CallbackError { .. }) => {}
|
||||||
|
r => panic!("improper return for destructed function: {:?}", r),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scope_drop() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
struct MyUserdata(Rc<()>);
|
||||||
|
impl UserData for MyUserdata {
|
||||||
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_method("method", |_, _, ()| Ok(()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rc = Rc::new(());
|
||||||
|
|
||||||
|
lua.scope(|scope| {
|
||||||
|
lua.globals()
|
||||||
|
.set("test", scope.create_userdata(MyUserdata(rc.clone()))?)?;
|
||||||
|
assert_eq!(Rc::strong_count(&rc), 2);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
assert_eq!(Rc::strong_count(&rc), 1);
|
||||||
|
|
||||||
|
match lua.load("test:method()").exec() {
|
||||||
|
Err(Error::CallbackError { .. }) => {}
|
||||||
|
r => panic!("improper return for destructed userdata: {:?}", r),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scope_capture() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
lua.scope(|scope| {
|
||||||
|
scope
|
||||||
|
.create_function_mut(|_, ()| {
|
||||||
|
i = 42;
|
||||||
|
Ok(())
|
||||||
|
})?
|
||||||
|
.call::<_, ()>(())
|
||||||
|
})?;
|
||||||
|
assert_eq!(i, 42);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn outer_lua_access() -> Result<()> {
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let table = lua.create_table()?;
|
||||||
|
lua.scope(|scope| {
|
||||||
|
scope
|
||||||
|
.create_function_mut(|_, ()| table.set("a", "b"))?
|
||||||
|
.call::<_, ()>(())
|
||||||
|
})?;
|
||||||
|
assert_eq!(table.get::<_, String>("a")?, "b");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scope_userdata_methods() -> Result<()> {
|
||||||
|
struct MyUserData<'a>(&'a Cell<i64>);
|
||||||
|
|
||||||
|
impl<'a> UserData for MyUserData<'a> {
|
||||||
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_method("inc", |_, data, ()| {
|
||||||
|
data.0.set(data.0.get() + 1);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_method("dec", |_, data, ()| {
|
||||||
|
data.0.set(data.0.get() - 1);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let i = Cell::new(42);
|
||||||
|
let f: Function = lua
|
||||||
|
.load(
|
||||||
|
r#"
|
||||||
|
function(u)
|
||||||
|
u:inc()
|
||||||
|
u:inc()
|
||||||
|
u:inc()
|
||||||
|
u:dec()
|
||||||
|
end
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.eval()?;
|
||||||
|
|
||||||
|
lua.scope(|scope| f.call::<_, ()>(scope.create_nonstatic_userdata(MyUserData(&i))?))?;
|
||||||
|
|
||||||
|
assert_eq!(i.get(), 44);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scope_userdata_functions() -> Result<()> {
|
||||||
|
struct MyUserData<'a>(&'a i64);
|
||||||
|
|
||||||
|
impl<'a> UserData for MyUserData<'a> {
|
||||||
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_meta_function(MetaMethod::Add, |lua, ()| {
|
||||||
|
let globals = lua.globals();
|
||||||
|
globals.set("i", globals.get::<_, i64>("i")? + 1)?;
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
methods.add_meta_function(MetaMethod::Sub, |lua, ()| {
|
||||||
|
let globals = lua.globals();
|
||||||
|
globals.set("i", globals.get::<_, i64>("i")? + 1)?;
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
let dummy = 0;
|
||||||
|
let f = lua
|
||||||
|
.load(
|
||||||
|
r#"
|
||||||
|
i = 0
|
||||||
|
return function(u)
|
||||||
|
_ = u + u
|
||||||
|
_ = u - 1
|
||||||
|
_ = 1 + u
|
||||||
|
end
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.eval::<Function>()?;
|
||||||
|
|
||||||
|
lua.scope(|scope| f.call::<_, ()>(scope.create_nonstatic_userdata(MyUserData(&dummy))?))?;
|
||||||
|
|
||||||
|
assert_eq!(lua.globals().get::<_, i64>("i")?, 3);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scope_userdata_mismatch() -> Result<()> {
|
||||||
|
struct MyUserData<'a>(&'a Cell<i64>);
|
||||||
|
|
||||||
|
impl<'a> UserData for MyUserData<'a> {
|
||||||
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_method("inc", |_, data, ()| {
|
||||||
|
data.0.set(data.0.get() + 1);
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lua = Lua::new();
|
||||||
|
|
||||||
|
lua.load(
|
||||||
|
r#"
|
||||||
|
function okay(a, b)
|
||||||
|
a.inc(a)
|
||||||
|
b.inc(b)
|
||||||
|
end
|
||||||
|
function bad(a, b)
|
||||||
|
a.inc(b)
|
||||||
|
end
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.exec()?;
|
||||||
|
|
||||||
|
let a = Cell::new(1);
|
||||||
|
let b = Cell::new(1);
|
||||||
|
|
||||||
|
let okay: Function = lua.globals().get("okay")?;
|
||||||
|
let bad: Function = lua.globals().get("bad")?;
|
||||||
|
|
||||||
|
lua.scope(|scope| {
|
||||||
|
let au = scope.create_nonstatic_userdata(MyUserData(&a))?;
|
||||||
|
let bu = scope.create_nonstatic_userdata(MyUserData(&b))?;
|
||||||
|
assert!(okay.call::<_, ()>((au.clone(), bu.clone())).is_ok());
|
||||||
|
match bad.call::<_, ()>((au, bu)) {
|
||||||
|
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
|
||||||
|
Error::UserDataTypeMismatch => {}
|
||||||
|
ref other => panic!("wrong error type {:?}", other),
|
||||||
|
},
|
||||||
|
Err(other) => panic!("wrong error type {:?}", other),
|
||||||
|
Ok(_) => panic!("incorrectly returned Ok"),
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue