Merge branch 'dev'

This commit is contained in:
Alex Orlenko 2021-11-07 13:07:22 +00:00
commit 204eedde3c
No known key found for this signature in database
GPG Key ID: 4C150C250863B96D
17 changed files with 437 additions and 124 deletions

View File

@ -33,6 +33,7 @@ lua53 = []
lua52 = [] lua52 = []
lua51 = [] lua51 = []
luajit = [] luajit = []
luajit52 = ["luajit"]
vendored = ["lua-src", "luajit-src"] vendored = ["lua-src", "luajit-src"]
module = ["mlua_derive"] module = ["mlua_derive"]
async = ["futures-core", "futures-task", "futures-util"] async = ["futures-core", "futures-task", "futures-util"]
@ -45,6 +46,7 @@ mlua_derive = { version = "=0.6.0", optional = true, path = "mlua_derive" }
bstr = { version = "0.2", features = ["std"], default_features = false } bstr = { version = "0.2", features = ["std"], default_features = false }
once_cell = { version = "1.0" } once_cell = { version = "1.0" }
num-traits = { version = "0.2.14" } num-traits = { version = "0.2.14" }
rustc-hash = "1.0"
futures-core = { version = "0.3.5", optional = true } futures-core = { version = "0.3.5", optional = true }
futures-task = { version = "0.3.5", optional = true } futures-task = { version = "0.3.5", optional = true }
futures-util = { version = "0.3.5", optional = true } futures-util = { version = "0.3.5", optional = true }
@ -55,7 +57,7 @@ erased-serde = { version = "0.3", optional = true }
cc = { version = "1.0" } cc = { version = "1.0" }
pkg-config = { version = "0.3.17" } pkg-config = { version = "0.3.17" }
lua-src = { version = ">= 540.0.0, < 550.0.0", optional = true } lua-src = { version = ">= 540.0.0, < 550.0.0", optional = true }
luajit-src = { version = ">= 210.1.2, < 220.0.0", optional = true } luajit-src = { version = ">= 210.3.1, < 220.0.0", optional = true }
[dev-dependencies] [dev-dependencies]
rustyline = "8.0" rustyline = "8.0"

View File

@ -33,6 +33,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
* `lua52`: activate Lua [5.2] support * `lua52`: activate Lua [5.2] support
* `lua51`: activate Lua [5.1] support * `lua51`: activate Lua [5.1] support
* `luajit`: activate [LuaJIT] support * `luajit`: activate [LuaJIT] support
* `luajit52`: activate [LuaJIT] support with partial compatibility with Lua 5.2
* `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates * `vendored`: build static Lua(JIT) library from sources during `mlua` compilation using [lua-src] or [luajit-src] crates
* `module`: enable module mode (building loadable `cdylib` library for Lua) * `module`: enable module mode (building loadable `cdylib` library for Lua)
* `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std]) * `async`: enable async/await support (any executor can be used, eg. [tokio] or [async-std])

View File

@ -10,7 +10,13 @@ pub fn probe_lua() -> PathBuf {
#[cfg(feature = "lua51")] #[cfg(feature = "lua51")]
let artifacts = lua_src::Build::new().build(lua_src::Lua51); let artifacts = lua_src::Build::new().build(lua_src::Lua51);
#[cfg(feature = "luajit")] #[cfg(feature = "luajit")]
let artifacts = luajit_src::Build::new().build(); let artifacts = {
let mut builder = luajit_src::Build::new();
if cfg!(feature = "luajit52") {
builder.lua52compat(true);
}
builder.build()
};
#[cfg(not(feature = "module"))] #[cfg(not(feature = "module"))]
artifacts.print_cargo_metadata(); artifacts.print_cargo_metadata();

View File

@ -201,7 +201,9 @@ fn main() {
feature = "lua51", feature = "lua51",
feature = "luajit" feature = "luajit"
)))] )))]
compile_error!("You must enable one of the features: lua54, lua53, lua52, lua51, luajit"); compile_error!(
"You must enable one of the features: lua54, lua53, lua52, lua51, luajit, luajit52"
);
#[cfg(all( #[cfg(all(
feature = "lua54", feature = "lua54",
@ -212,19 +214,27 @@ fn main() {
feature = "luajit" feature = "luajit"
) )
))] ))]
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit"); compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52"
);
#[cfg(all( #[cfg(all(
feature = "lua53", feature = "lua53",
any(feature = "lua52", feature = "lua51", feature = "luajit") any(feature = "lua52", feature = "lua51", feature = "luajit")
))] ))]
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit"); compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52"
);
#[cfg(all(feature = "lua52", any(feature = "lua51", feature = "luajit")))] #[cfg(all(feature = "lua52", any(feature = "lua51", feature = "luajit")))]
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit"); compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52"
);
#[cfg(all(feature = "lua51", feature = "luajit"))] #[cfg(all(feature = "lua51", feature = "luajit"))]
compile_error!("You can enable only one of the features: lua54, lua53, lua52, lua51, luajit"); compile_error!(
"You can enable only one of the features: lua54, lua53, lua52, lua51, luajit, luajit52"
);
// We don't support "vendored module" mode on windows // We don't support "vendored module" mode on windows
#[cfg(all(feature = "vendored", feature = "module", target_os = "windows"))] #[cfg(all(feature = "vendored", feature = "module", target_os = "windows"))]

View File

@ -2,6 +2,7 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::convert::TryInto;
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::hash::{BuildHasher, Hash}; use std::hash::{BuildHasher, Hash};
use std::string::String as StdString; use std::string::String as StdString;
@ -348,18 +349,15 @@ macro_rules! lua_convert_int {
($x:ty) => { ($x:ty) => {
impl<'lua> ToLua<'lua> for $x { impl<'lua> ToLua<'lua> for $x {
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> { fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
if let Some(i) = cast(self) {
Ok(Value::Integer(i))
} else {
// TODO: Remove conversion to Number in v0.7
cast(self) cast(self)
.map(Value::Integer)
.or_else(|| cast(self).map(Value::Number))
// This is impossible error because conversion to Number never fails
.ok_or_else(|| Error::ToLuaConversionError { .ok_or_else(|| Error::ToLuaConversionError {
from: stringify!($x), from: stringify!($x),
to: "number", to: "number",
message: Some("out of range".to_owned()), message: Some("out of range".to_owned()),
}) })
.map(Value::Number)
}
} }
} }
@ -451,37 +449,36 @@ where
} }
} }
macro_rules! lua_convert_array { impl<'lua, T, const N: usize> ToLua<'lua> for [T; N]
($($N:literal)+) => {
$(
impl<'lua, T> ToLua<'lua> for [T; $N]
where where
T: Clone + ToLua<'lua>, T: ToLua<'lua>,
{ {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> { fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
(&self).to_lua(lua) Ok(Value::Table(lua.create_sequence_from(self)?))
} }
} }
impl<'lua, T> ToLua<'lua> for &[T; $N] impl<'lua, T, const N: usize> FromLua<'lua> for [T; N]
where where
T: Clone + ToLua<'lua>, T: FromLua<'lua>,
{ {
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> { fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
Ok(Value::Table( if let Value::Table(table) = value {
lua.create_sequence_from(self.iter().cloned())?, let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
)) vec.try_into()
.map_err(|vec: Vec<T>| Error::FromLuaConversionError {
from: "Table",
to: "Array",
message: Some(format!("expected table of length {}, got {}", N, vec.len())),
})
} else {
Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "Array",
message: Some("expected table".to_string()),
})
} }
} }
)+
}
}
lua_convert_array! {
0 1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32
} }
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> { impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> {

View File

@ -23,6 +23,7 @@
//! Contains definitions from `lua.h`. //! Contains definitions from `lua.h`.
use std::marker::{PhantomData, PhantomPinned};
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
use std::os::raw::c_uchar; use std::os::raw::c_uchar;
use std::os::raw::{c_char, c_int, c_void}; use std::os::raw::{c_char, c_int, c_void};
@ -81,7 +82,11 @@ pub const LUA_ERRERR: c_int = 5;
pub const LUA_ERRERR: c_int = 6; pub const LUA_ERRERR: c_int = 6;
/// A raw Lua state associated with a thread. /// A raw Lua state associated with a thread.
pub type lua_State = c_void; #[repr(C)]
pub struct lua_State {
_data: [u8; 0],
_marker: PhantomData<(*mut u8, PhantomPinned)>,
}
// basic types // basic types
pub const LUA_TNONE: c_int = -1; pub const LUA_TNONE: c_int = -1;
@ -621,7 +626,7 @@ extern "C" {
#[cfg(any(feature = "lua54", feature = "lua53"))] #[cfg(any(feature = "lua54", feature = "lua53"))]
#[inline(always)] #[inline(always)]
pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void { pub unsafe fn lua_getextraspace(L: *mut lua_State) -> *mut c_void {
L.offset(-super::glue::LUA_EXTRASPACE as isize) as *mut c_void (L as *mut c_char).offset(-super::glue::LUA_EXTRASPACE as isize) as *mut c_void
} }
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]

View File

@ -1,4 +1,4 @@
use std::any::TypeId; use std::any::{Any, TypeId};
use std::cell::{Ref, RefCell, RefMut, UnsafeCell}; use std::cell::{Ref, RefCell, RefMut, UnsafeCell};
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::CString; use std::ffi::CString;
@ -9,6 +9,8 @@ use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe, Location};
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
use std::{mem, ptr, str}; use std::{mem, ptr, str};
use rustc_hash::FxHashMap;
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::ffi; use crate::ffi;
use crate::function::Function; use crate::function::Function;
@ -63,10 +65,12 @@ pub struct Lua {
// Data associated with the Lua. // Data associated with the Lua.
struct ExtraData { struct ExtraData {
registered_userdata: HashMap<TypeId, c_int>, registered_userdata: FxHashMap<TypeId, c_int>,
registered_userdata_mt: HashMap<*const c_void, Option<TypeId>>, registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>,
registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>, registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
app_data: RefCell<HashMap<TypeId, Box<dyn Any>>>,
libs: StdLib, libs: StdLib,
mem_info: Option<Box<MemoryInfo>>, mem_info: Option<Box<MemoryInfo>>,
safe: bool, // Same as in the Lua struct safe: bool, // Same as in the Lua struct
@ -456,9 +460,10 @@ impl Lua {
// Create ExtraData // Create ExtraData
let extra = Arc::new(UnsafeCell::new(ExtraData { let extra = Arc::new(UnsafeCell::new(ExtraData {
registered_userdata: HashMap::new(), registered_userdata: FxHashMap::default(),
registered_userdata_mt: HashMap::new(), registered_userdata_mt: FxHashMap::default(),
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))), registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
app_data: RefCell::new(HashMap::new()),
ref_thread, ref_thread,
libs: StdLib::NONE, libs: StdLib::NONE,
mem_info: None, mem_info: None,
@ -1577,6 +1582,74 @@ impl Lua {
} }
} }
/// Sets or replaces an application data object of type `T`.
///
/// Application data could be accessed at any time by using [`Lua::app_data_ref()`] or [`Lua::app_data_mut()`]
/// methods where `T` is the data type.
///
/// # Examples
///
/// ```
/// use mlua::{Lua, Result};
///
/// fn hello(lua: &Lua, _: ()) -> Result<()> {
/// let mut s = lua.app_data_mut::<&str>().unwrap();
/// assert_eq!(*s, "hello");
/// *s = "world";
/// Ok(())
/// }
///
/// fn main() -> Result<()> {
/// let lua = Lua::new();
/// lua.set_app_data("hello");
/// lua.create_function(hello)?.call(())?;
/// let s = lua.app_data_ref::<&str>().unwrap();
/// assert_eq!(*s, "world");
/// Ok(())
/// }
/// ```
pub fn set_app_data<T: 'static + MaybeSend>(&self, data: T) {
let extra = unsafe { &mut (*self.extra.get()) };
extra
.app_data
.try_borrow_mut()
.expect("cannot borrow mutably app data container")
.insert(TypeId::of::<T>(), Box::new(data));
}
/// Gets a reference to an application data object stored by [`Lua::set_app_data()`] of type `T`.
pub fn app_data_ref<T: 'static>(&self) -> Option<Ref<T>> {
let extra = unsafe { &(*self.extra.get()) };
let app_data = extra
.app_data
.try_borrow()
.expect("cannot borrow app data container");
let value = app_data.get(&TypeId::of::<T>())?.downcast_ref::<T>()? as *const _;
Some(Ref::map(app_data, |_| unsafe { &*value }))
}
/// Gets a mutable reference to an application data object stored by [`Lua::set_app_data()`] of type `T`.
pub fn app_data_mut<T: 'static>(&self) -> Option<RefMut<T>> {
let extra = unsafe { &(*self.extra.get()) };
let mut app_data = extra
.app_data
.try_borrow_mut()
.expect("cannot mutably borrow app data container");
let value = app_data.get_mut(&TypeId::of::<T>())?.downcast_mut::<T>()? as *mut _;
Some(RefMut::map(app_data, |_| unsafe { &mut *value }))
}
/// Removes an application data of type `T`.
pub fn remove_app_data<T: 'static>(&self) -> Option<T> {
let extra = unsafe { &mut (*self.extra.get()) };
extra
.app_data
.try_borrow_mut()
.expect("cannot mutably borrow app data container")
.remove(&TypeId::of::<T>())
.and_then(|data| data.downcast().ok().map(|data| *data))
}
// Uses 2 stack spaces, does not call checkstack // Uses 2 stack spaces, does not call checkstack
pub(crate) unsafe fn push_value(&self, value: Value) -> Result<()> { pub(crate) unsafe fn push_value(&self, value: Value) -> Result<()> {
match value { match value {
@ -1769,11 +1842,18 @@ impl Lua {
// Prepare metatable, add meta methods first and then meta fields // Prepare metatable, add meta methods first and then meta fields
let metatable_nrec = methods.meta_methods.len() + fields.meta_fields.len(); let metatable_nrec = methods.meta_methods.len() + fields.meta_fields.len();
#[cfg(feature = "async")]
let metatable_nrec = metatable_nrec + methods.async_meta_methods.len();
push_table(self.state, 0, metatable_nrec as c_int)?; push_table(self.state, 0, metatable_nrec as c_int)?;
for (k, m) in methods.meta_methods { for (k, m) in methods.meta_methods {
self.push_value(Value::Function(self.create_callback(m)?))?; self.push_value(Value::Function(self.create_callback(m)?))?;
rawset_field(self.state, -2, k.validate()?.name())?; rawset_field(self.state, -2, k.validate()?.name())?;
} }
#[cfg(feature = "async")]
for (k, m) in methods.async_meta_methods {
self.push_value(Value::Function(self.create_async_callback(m)?))?;
rawset_field(self.state, -2, k.validate()?.name())?;
}
for (k, f) in fields.meta_fields { for (k, f) in fields.meta_fields {
self.push_value(f(self)?)?; self.push_value(f(self)?)?;
rawset_field(self.state, -2, k.validate()?.name())?; rawset_field(self.state, -2, k.validate()?.name())?;
@ -1807,10 +1887,9 @@ impl Lua {
} }
let mut methods_index = None; let mut methods_index = None;
#[cfg(feature = "async")]
let methods_nrec = methods.methods.len() + methods.async_methods.len();
#[cfg(not(feature = "async"))]
let methods_nrec = methods.methods.len(); let methods_nrec = methods.methods.len();
#[cfg(feature = "async")]
let methods_nrec = methods_nrec + methods.async_methods.len();
if methods_nrec > 0 { if methods_nrec > 0 {
push_table(self.state, 0, methods_nrec as c_int)?; push_table(self.state, 0, methods_nrec as c_int)?;
for (k, m) in methods.methods { for (k, m) in methods.methods {
@ -2425,7 +2504,7 @@ impl<'lua, T: AsRef<[u8]> + ?Sized> AsChunk<'lua> for T {
} }
// Creates required entries in the metatable cache (see `util::METATABLE_CACHE`) // Creates required entries in the metatable cache (see `util::METATABLE_CACHE`)
pub(crate) fn init_metatable_cache(cache: &mut HashMap<TypeId, u8>) { pub(crate) fn init_metatable_cache(cache: &mut FxHashMap<TypeId, u8>) {
cache.insert(TypeId::of::<Arc<UnsafeCell<ExtraData>>>(), 0); cache.insert(TypeId::of::<Arc<UnsafeCell<ExtraData>>>(), 0);
cache.insert(TypeId::of::<Callback>(), 0); cache.insert(TypeId::of::<Callback>(), 0);
cache.insert(TypeId::of::<CallbackUpvalue>(), 0); cache.insert(TypeId::of::<CallbackUpvalue>(), 0);
@ -2712,6 +2791,8 @@ struct StaticUserDataMethods<'lua, T: 'static + UserData> {
#[cfg(feature = "async")] #[cfg(feature = "async")]
async_methods: Vec<(Vec<u8>, AsyncCallback<'lua, 'static>)>, async_methods: Vec<(Vec<u8>, AsyncCallback<'lua, 'static>)>,
meta_methods: Vec<(MetaMethod, Callback<'lua, 'static>)>, meta_methods: Vec<(MetaMethod, Callback<'lua, 'static>)>,
#[cfg(feature = "async")]
async_meta_methods: Vec<(MetaMethod, AsyncCallback<'lua, 'static>)>,
_type: PhantomData<T>, _type: PhantomData<T>,
} }
@ -2722,6 +2803,8 @@ impl<'lua, T: 'static + UserData> Default for StaticUserDataMethods<'lua, T> {
#[cfg(feature = "async")] #[cfg(feature = "async")]
async_methods: Vec::new(), async_methods: Vec::new(),
meta_methods: Vec::new(), meta_methods: Vec::new(),
#[cfg(feature = "async")]
async_meta_methods: Vec::new(),
_type: PhantomData, _type: PhantomData,
} }
} }
@ -2821,6 +2904,20 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
.push((meta.into(), Self::box_method_mut(method))); .push((meta.into(), Self::box_method_mut(method)));
} }
#[cfg(all(feature = "async", not(feature = "lua51")))]
fn add_async_meta_method<S, A, R, M, MR>(&mut self, meta: S, method: M)
where
T: Clone,
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
MR: 'lua + Future<Output = Result<R>>,
{
self.async_meta_methods
.push((meta.into(), Self::box_async_method(method)));
}
fn add_meta_function<S, A, R, F>(&mut self, meta: S, function: F) fn add_meta_function<S, A, R, F>(&mut self, meta: S, function: F)
where where
S: Into<MetaMethod>, S: Into<MetaMethod>,
@ -2843,6 +2940,19 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
.push((meta.into(), Self::box_function_mut(function))); .push((meta.into(), Self::box_function_mut(function)));
} }
#[cfg(all(feature = "async", not(feature = "lua51")))]
fn add_async_meta_function<S, A, R, F, FR>(&mut self, meta: S, function: F)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
FR: 'lua + Future<Output = Result<R>>,
{
self.async_meta_methods
.push((meta.into(), Self::box_async_function(function)));
}
// Below are internal methods used in generated code // Below are internal methods used in generated code
fn add_callback(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) { fn add_callback(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) {
@ -2857,6 +2967,15 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
fn add_meta_callback(&mut self, meta: MetaMethod, callback: Callback<'lua, 'static>) { fn add_meta_callback(&mut self, meta: MetaMethod, callback: Callback<'lua, 'static>) {
self.meta_methods.push((meta, callback)); self.meta_methods.push((meta, callback));
} }
#[cfg(feature = "async")]
fn add_async_meta_callback(
&mut self,
meta: MetaMethod,
callback: AsyncCallback<'lua, 'static>,
) {
self.async_meta_methods.push((meta, callback))
}
} }
impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> { impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
@ -3202,6 +3321,10 @@ macro_rules! lua_userdata_impl {
for (meta, callback) in orig_methods.meta_methods { for (meta, callback) in orig_methods.meta_methods {
methods.add_meta_callback(meta, callback); methods.add_meta_callback(meta, callback);
} }
#[cfg(feature = "async")]
for (meta, callback) in orig_methods.async_meta_methods {
methods.add_async_meta_callback(meta, callback);
}
} }
} }
}; };

View File

@ -693,6 +693,21 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
)); ));
} }
#[cfg(all(feature = "async", not(feature = "lua51")))]
fn add_async_meta_method<S, A, R, M, MR>(&mut self, _meta: S, _method: M)
where
T: Clone,
S: Into<MetaMethod>,
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 meta methods are not supported for non-static userdata")
}
fn add_meta_function<S, A, R, F>(&mut self, meta: S, function: F) fn add_meta_function<S, A, R, F>(&mut self, meta: S, function: F)
where where
S: Into<MetaMethod>, S: Into<MetaMethod>,
@ -722,6 +737,20 @@ impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'l
})), })),
)); ));
} }
#[cfg(all(feature = "async", not(feature = "lua51")))]
fn add_async_meta_function<S, A, R, F, FR>(&mut self, _meta: S, _function: F)
where
S: Into<MetaMethod>,
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 meta functions are not supported for non-static userdata")
}
} }
struct NonStaticUserDataFields<'lua, T: UserData> { struct NonStaticUserDataFields<'lua, T: UserData> {

View File

@ -1,9 +1,9 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashSet;
use std::os::raw::c_void; use std::os::raw::c_void;
use std::rc::Rc; use std::rc::Rc;
use std::string::String as StdString; use std::string::String as StdString;
use rustc_hash::FxHashSet;
use serde::de::{self, IntoDeserializer}; use serde::de::{self, IntoDeserializer};
use crate::error::{Error, Result}; use crate::error::{Error, Result};
@ -16,7 +16,7 @@ use crate::value::Value;
pub struct Deserializer<'lua> { pub struct Deserializer<'lua> {
value: Value<'lua>, value: Value<'lua>,
options: Options, options: Options,
visited: Rc<RefCell<HashSet<*const c_void>>>, visited: Rc<RefCell<FxHashSet<*const c_void>>>,
} }
/// A struct with options to change default deserializer behavior. /// A struct with options to change default deserializer behavior.
@ -45,23 +45,23 @@ pub struct Options {
impl Default for Options { impl Default for Options {
fn default() -> Self { fn default() -> Self {
Options { Self::new()
deny_unsupported_types: true,
deny_recursive_tables: true,
}
} }
} }
impl Options { impl Options {
/// Returns a new instance of `Options` with default parameters. /// Returns a new instance of `Options` with default parameters.
pub fn new() -> Self { pub const fn new() -> Self {
Self::default() Options {
deny_unsupported_types: true,
deny_recursive_tables: true,
}
} }
/// Sets [`deny_unsupported_types`] option. /// Sets [`deny_unsupported_types`] option.
/// ///
/// [`deny_unsupported_types`]: #structfield.deny_unsupported_types /// [`deny_unsupported_types`]: #structfield.deny_unsupported_types
pub fn deny_unsupported_types(mut self, enabled: bool) -> Self { pub const fn deny_unsupported_types(mut self, enabled: bool) -> Self {
self.deny_unsupported_types = enabled; self.deny_unsupported_types = enabled;
self self
} }
@ -86,14 +86,14 @@ impl<'lua> Deserializer<'lua> {
Deserializer { Deserializer {
value, value,
options, options,
visited: Rc::new(RefCell::new(HashSet::new())), visited: Rc::new(RefCell::new(FxHashSet::default())),
} }
} }
fn from_parts( fn from_parts(
value: Value<'lua>, value: Value<'lua>,
options: Options, options: Options,
visited: Rc<RefCell<HashSet<*const c_void>>>, visited: Rc<RefCell<FxHashSet<*const c_void>>>,
) -> Self { ) -> Self {
Deserializer { Deserializer {
value, value,
@ -313,7 +313,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
struct SeqDeserializer<'lua> { struct SeqDeserializer<'lua> {
seq: TableSequence<'lua, Value<'lua>>, seq: TableSequence<'lua, Value<'lua>>,
options: Options, options: Options,
visited: Rc<RefCell<HashSet<*const c_void>>>, visited: Rc<RefCell<FxHashSet<*const c_void>>>,
} }
impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> { impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
@ -351,7 +351,7 @@ struct MapDeserializer<'lua> {
pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>, pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>,
value: Option<Value<'lua>>, value: Option<Value<'lua>>,
options: Options, options: Options,
visited: Rc<RefCell<HashSet<*const c_void>>>, visited: Rc<RefCell<FxHashSet<*const c_void>>>,
processed: usize, processed: usize,
} }
@ -407,7 +407,7 @@ struct EnumDeserializer<'lua> {
variant: StdString, variant: StdString,
value: Option<Value<'lua>>, value: Option<Value<'lua>>,
options: Options, options: Options,
visited: Rc<RefCell<HashSet<*const c_void>>>, visited: Rc<RefCell<FxHashSet<*const c_void>>>,
} }
impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> { impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
@ -431,7 +431,7 @@ impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
struct VariantDeserializer<'lua> { struct VariantDeserializer<'lua> {
value: Option<Value<'lua>>, value: Option<Value<'lua>>,
options: Options, options: Options,
visited: Rc<RefCell<HashSet<*const c_void>>>, visited: Rc<RefCell<FxHashSet<*const c_void>>>,
} }
impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> { impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
@ -499,12 +499,12 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
// Used to track recursive tables but allow to traverse same tables multiple times // Used to track recursive tables but allow to traverse same tables multiple times
struct RecursionGuard { struct RecursionGuard {
ptr: *const c_void, ptr: *const c_void,
visited: Rc<RefCell<HashSet<*const c_void>>>, visited: Rc<RefCell<FxHashSet<*const c_void>>>,
} }
impl RecursionGuard { impl RecursionGuard {
#[inline] #[inline]
fn new(table: &Table, visited: &Rc<RefCell<HashSet<*const c_void>>>) -> Self { fn new(table: &Table, visited: &Rc<RefCell<FxHashSet<*const c_void>>>) -> Self {
let visited = Rc::clone(visited); let visited = Rc::clone(visited);
let lua = table.0.lua; let lua = table.0.lua;
let ptr = let ptr =
@ -524,7 +524,7 @@ impl Drop for RecursionGuard {
fn check_value_if_skip( fn check_value_if_skip(
value: &Value, value: &Value,
options: Options, options: Options,
visited: &RefCell<HashSet<*const c_void>>, visited: &RefCell<FxHashSet<*const c_void>>,
) -> Result<bool> { ) -> Result<bool> {
match value { match value {
Value::Table(table) => { Value::Table(table) => {

View File

@ -52,24 +52,24 @@ pub struct Options {
impl Default for Options { impl Default for Options {
fn default() -> Self { fn default() -> Self {
Self::new()
}
}
impl Options {
/// Returns a new instance of [`Options`] with default parameters.
pub const fn new() -> Self {
Options { Options {
set_array_metatable: true, set_array_metatable: true,
serialize_none_to_null: true, serialize_none_to_null: true,
serialize_unit_to_null: true, serialize_unit_to_null: true,
} }
} }
}
impl Options {
/// Returns a new instance of [`Options`] with default parameters.
pub fn new() -> Self {
Self::default()
}
/// Sets [`set_array_metatable`] option. /// Sets [`set_array_metatable`] option.
/// ///
/// [`set_array_metatable`]: #structfield.set_array_metatable /// [`set_array_metatable`]: #structfield.set_array_metatable
pub fn set_array_metatable(mut self, enabled: bool) -> Self { pub const fn set_array_metatable(mut self, enabled: bool) -> Self {
self.set_array_metatable = enabled; self.set_array_metatable = enabled;
self self
} }
@ -77,7 +77,7 @@ impl Options {
/// Sets [`serialize_none_to_null`] option. /// Sets [`serialize_none_to_null`] option.
/// ///
/// [`serialize_none_to_null`]: #structfield.serialize_none_to_null /// [`serialize_none_to_null`]: #structfield.serialize_none_to_null
pub fn serialize_none_to_null(mut self, enabled: bool) -> Self { pub const fn serialize_none_to_null(mut self, enabled: bool) -> Self {
self.serialize_none_to_null = enabled; self.serialize_none_to_null = enabled;
self self
} }
@ -85,7 +85,7 @@ impl Options {
/// Sets [`serialize_unit_to_null`] option. /// Sets [`serialize_unit_to_null`] option.
/// ///
/// [`serialize_unit_to_null`]: #structfield.serialize_unit_to_null /// [`serialize_unit_to_null`]: #structfield.serialize_unit_to_null
pub fn serialize_unit_to_null(mut self, enabled: bool) -> Self { pub const fn serialize_unit_to_null(mut self, enabled: bool) -> Self {
self.serialize_unit_to_null = enabled; self.serialize_unit_to_null = enabled;
self self
} }

View File

@ -102,7 +102,13 @@ pub enum MetaMethod {
/// This is not an operator, but it will be called by the built-in `pairs` function. /// This is not an operator, but it will be called by the built-in `pairs` function.
/// ///
/// Requires `feature = "lua54/lua53/lua52"` /// Requires `feature = "lua54/lua53/lua52"`
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))] #[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit52",
doc
))]
Pairs, Pairs,
/// The `__ipairs` metamethod. /// The `__ipairs` metamethod.
/// ///
@ -111,7 +117,7 @@ pub enum MetaMethod {
/// Requires `feature = "lua52"` /// Requires `feature = "lua52"`
/// ///
/// [`ipairs`]: https://www.lua.org/manual/5.2/manual.html#pdf-ipairs /// [`ipairs`]: https://www.lua.org/manual/5.2/manual.html#pdf-ipairs
#[cfg(any(feature = "lua52", doc))] #[cfg(any(feature = "lua52", feature = "luajit52", doc))]
IPairs, IPairs,
/// The `__close` metamethod. /// The `__close` metamethod.
/// ///
@ -188,9 +194,14 @@ impl MetaMethod {
MetaMethod::Call => "__call", MetaMethod::Call => "__call",
MetaMethod::ToString => "__tostring", MetaMethod::ToString => "__tostring",
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] #[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit52"
))]
MetaMethod::Pairs => "__pairs", MetaMethod::Pairs => "__pairs",
#[cfg(feature = "lua52")] #[cfg(any(feature = "lua52", feature = "luajit52"))]
MetaMethod::IPairs => "__ipairs", MetaMethod::IPairs => "__ipairs",
#[cfg(feature = "lua54")] #[cfg(feature = "lua54")]
@ -250,9 +261,14 @@ impl From<StdString> for MetaMethod {
"__call" => MetaMethod::Call, "__call" => MetaMethod::Call,
"__tostring" => MetaMethod::ToString, "__tostring" => MetaMethod::ToString,
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] #[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit52"
))]
"__pairs" => MetaMethod::Pairs, "__pairs" => MetaMethod::Pairs,
#[cfg(feature = "lua52")] #[cfg(any(feature = "lua52", feature = "luajit52"))]
"__ipairs" => MetaMethod::IPairs, "__ipairs" => MetaMethod::IPairs,
#[cfg(feature = "lua54")] #[cfg(feature = "lua54")]
@ -395,6 +411,25 @@ pub trait UserDataMethods<'lua, T: UserData> {
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>; M: 'static + MaybeSend + FnMut(&'lua Lua, &mut T, A) -> Result<R>;
/// Add an async metamethod which accepts a `T` as the first parameter and returns Future.
/// The passed `T` is cloned from the original value.
///
/// This is an async version of [`add_meta_method`].
///
/// Requires `feature = "async"`
///
/// [`add_meta_method`]: #method.add_meta_method
#[cfg(all(feature = "async", not(feature = "lua51")))]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_meta_method<S, A, R, M, MR>(&mut self, name: S, method: M)
where
T: Clone,
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
M: 'static + MaybeSend + Fn(&'lua Lua, T, A) -> MR,
MR: 'lua + Future<Output = Result<R>>;
/// Add a metamethod which accepts generic arguments. /// Add a metamethod which accepts generic arguments.
/// ///
/// Metamethods for binary operators can be triggered if either the left or right argument to /// Metamethods for binary operators can be triggered if either the left or right argument to
@ -419,6 +454,23 @@ pub trait UserDataMethods<'lua, T: UserData> {
R: ToLuaMulti<'lua>, R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>; F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>;
/// Add a metamethod which accepts generic arguments and returns Future.
///
/// This is an async version of [`add_meta_function`].
///
/// Requires `feature = "async"`
///
/// [`add_meta_function`]: #method.add_meta_function
#[cfg(all(feature = "async", not(feature = "lua51")))]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_meta_function<S, A, R, F, FR>(&mut self, name: S, function: F)
where
S: Into<MetaMethod>,
A: FromLuaMulti<'lua>,
R: ToLuaMulti<'lua>,
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> FR,
FR: 'lua + Future<Output = Result<R>>;
// //
// Below are internal methods used in generated code // Below are internal methods used in generated code
// //
@ -432,6 +484,15 @@ pub trait UserDataMethods<'lua, T: UserData> {
#[doc(hidden)] #[doc(hidden)]
fn add_meta_callback(&mut self, _meta: MetaMethod, _callback: Callback<'lua, 'static>) {} fn add_meta_callback(&mut self, _meta: MetaMethod, _callback: Callback<'lua, 'static>) {}
#[doc(hidden)]
#[cfg(feature = "async")]
fn add_async_meta_callback(
&mut self,
_meta: MetaMethod,
_callback: AsyncCallback<'lua, 'static>,
) {
}
} }
/// Field registry for [`UserData`] implementors. /// Field registry for [`UserData`] implementors.
@ -758,7 +819,6 @@ impl<'lua> AnyUserData<'lua> {
/// Takes out the value of `UserData` and sets the special "destructed" metatable that prevents /// Takes out the value of `UserData` and sets the special "destructed" metatable that prevents
/// any further operations with this userdata. /// any further operations with this userdata.
#[doc(hidden)]
pub fn take<T: 'static + UserData>(&self) -> Result<T> { pub fn take<T: 'static + UserData>(&self) -> Result<T> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {

View File

@ -1,5 +1,4 @@
use std::any::{Any, TypeId}; use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::error::Error as StdError; use std::error::Error as StdError;
use std::fmt::Write; use std::fmt::Write;
use std::os::raw::{c_char, c_int, c_void}; use std::os::raw::{c_char, c_int, c_void};
@ -8,12 +7,13 @@ use std::sync::Arc;
use std::{mem, ptr, slice}; use std::{mem, ptr, slice};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use rustc_hash::FxHashMap;
use crate::error::{Error, Result}; use crate::error::{Error, Result};
use crate::ffi; use crate::ffi;
static METATABLE_CACHE: Lazy<HashMap<TypeId, u8>> = Lazy::new(|| { static METATABLE_CACHE: Lazy<FxHashMap<TypeId, u8>> = Lazy::new(|| {
let mut map = HashMap::with_capacity(32); let mut map = FxHashMap::with_capacity_and_hasher(32, Default::default());
crate::lua::init_metatable_cache(&mut map); crate::lua::init_metatable_cache(&mut map);
map.insert(TypeId::of::<WrappedFailure>(), 0); map.insert(TypeId::of::<WrappedFailure>(), 0);
map.insert(TypeId::of::<String>(), 0); map.insert(TypeId::of::<String>(), 0);
@ -842,9 +842,14 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
"__newindex", "__newindex",
"__call", "__call",
"__tostring", "__tostring",
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] #[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit52"
))]
"__pairs", "__pairs",
#[cfg(any(feature = "lua53", feature = "lua52"))] #[cfg(any(feature = "lua53", feature = "lua52", feature = "luajit52"))]
"__ipairs", "__ipairs",
#[cfg(feature = "lua54")] #[cfg(feature = "lua54")]
"__close", "__close",

View File

@ -3,7 +3,7 @@
use std::cell::Cell; use std::cell::Cell;
use std::rc::Rc; use std::rc::Rc;
use std::sync::{ use std::sync::{
atomic::{AtomicI64, Ordering}, atomic::{AtomicI64, AtomicU64, Ordering},
Arc, Arc,
}; };
use std::time::Duration; use std::time::Duration;
@ -12,7 +12,8 @@ use futures_timer::Delay;
use futures_util::stream::TryStreamExt; use futures_util::stream::TryStreamExt;
use mlua::{ use mlua::{
Error, Function, Lua, Result, Table, TableExt, Thread, UserData, UserDataMethods, Value, Error, Function, Lua, MetaMethod, Result, Table, TableExt, Thread, UserData, UserDataMethods,
Value,
}; };
#[tokio::test] #[tokio::test]
@ -276,7 +277,7 @@ async fn test_async_table() -> Result<()> {
#[tokio::test] #[tokio::test]
async fn test_async_userdata() -> Result<()> { async fn test_async_userdata() -> Result<()> {
#[derive(Clone)] #[derive(Clone)]
struct MyUserData(Arc<AtomicI64>); struct MyUserData(Arc<AtomicU64>);
impl UserData for MyUserData { impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
@ -295,13 +296,20 @@ async fn test_async_userdata() -> Result<()> {
Delay::new(Duration::from_millis(n)).await; Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n)) Ok(format!("elapsed:{}ms", n))
}); });
#[cfg(not(feature = "lua51"))]
methods.add_async_meta_method(MetaMethod::Call, |_, data, ()| async move {
let n = data.0.load(Ordering::Relaxed);
Delay::new(Duration::from_millis(n)).await;
Ok(format!("elapsed:{}ms", n))
});
} }
} }
let lua = Lua::new(); let lua = Lua::new();
let globals = lua.globals(); let globals = lua.globals();
let userdata = lua.create_userdata(MyUserData(Arc::new(AtomicI64::new(11))))?; let userdata = lua.create_userdata(MyUserData(Arc::new(AtomicU64::new(11))))?;
globals.set("userdata", userdata.clone())?; globals.set("userdata", userdata.clone())?;
lua.load( lua.load(
@ -315,6 +323,16 @@ async fn test_async_userdata() -> Result<()> {
.exec_async() .exec_async()
.await?; .await?;
#[cfg(not(feature = "lua51"))]
lua.load(
r#"
userdata:set_value(15)
assert(userdata() == "elapsed:15ms")
"#,
)
.exec_async()
.await?;
Ok(()) Ok(())
} }

View File

@ -3,7 +3,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use maplit::{btreemap, btreeset, hashmap, hashset}; use maplit::{btreemap, btreeset, hashmap, hashset};
use mlua::{Lua, Result}; use mlua::{Error, Lua, Result};
#[test] #[test]
fn test_conv_vec() -> Result<()> { fn test_conv_vec() -> Result<()> {
@ -123,3 +123,18 @@ fn test_conv_boxed_slice() -> Result<()> {
Ok(()) Ok(())
} }
#[test]
fn test_conv_array() -> Result<()> {
let lua = Lua::new();
let v = [1, 2, 3];
lua.globals().set("v", v)?;
let v2: [i32; 3] = lua.globals().get("v")?;
assert_eq!(v, v2);
let v2 = lua.globals().get::<_, [i32; 4]>("v");
assert!(matches!(v2, Err(Error::FromLuaConversionError { .. })));
Ok(())
}

View File

@ -139,14 +139,6 @@ fn test_table_sequence_from() -> Result<()> {
vec![1, 2, 3] vec![1, 2, 3]
); );
assert_eq!(
get_table
.call::<_, Table>(&[1, 2, 3])?
.sequence_values()
.collect::<Result<Vec<i64>>>()?,
vec![1, 2, 3]
);
Ok(()) Ok(())
} }

View File

@ -847,6 +847,37 @@ fn test_mismatched_registry_key() -> Result<()> {
Ok(()) Ok(())
} }
#[test]
fn test_application_data() -> Result<()> {
let lua = Lua::new();
lua.set_app_data("test1");
lua.set_app_data(vec!["test2"]);
let f = lua.create_function(|lua, ()| {
{
let data1 = lua.app_data_ref::<&str>().unwrap();
assert_eq!(*data1, "test1");
}
let mut data2 = lua.app_data_mut::<Vec<&str>>().unwrap();
assert_eq!(*data2, vec!["test2"]);
data2.push("test3");
Ok(())
})?;
f.call(())?;
assert_eq!(*lua.app_data_ref::<&str>().unwrap(), "test1");
assert_eq!(
*lua.app_data_ref::<Vec<&str>>().unwrap(),
vec!["test2", "test3"]
);
lua.remove_app_data::<Vec<&str>>();
assert!(matches!(lua.app_data_ref::<Vec<&str>>(), None));
Ok(())
}
#[test] #[test]
fn test_recursion() -> Result<()> { fn test_recursion() -> Result<()> {
let lua = Lua::new(); let lua = Lua::new();
@ -1046,17 +1077,22 @@ fn test_context_thread() -> Result<()> {
) )
.into_function()?; .into_function()?;
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] #[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit52"
))]
f.call::<_, ()>(lua.current_thread())?; f.call::<_, ()>(lua.current_thread())?;
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", all(feature = "luajit", not(feature = "luajit52"))))]
f.call::<_, ()>(Nil)?; f.call::<_, ()>(Nil)?;
Ok(()) Ok(())
} }
#[test] #[test]
#[cfg(any(feature = "lua51", feature = "luajit"))] #[cfg(any(feature = "lua51", all(feature = "luajit", not(feature = "luajit52"))))]
fn test_context_thread_51() -> Result<()> { fn test_context_thread_51() -> Result<()> {
let lua = Lua::new(); let lua = Lua::new();

View File

@ -111,7 +111,12 @@ fn test_metamethods() -> Result<()> {
Err("no such custom index".to_lua_err()) Err("no such custom index".to_lua_err())
} }
}); });
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] #[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit52"
))]
methods.add_meta_method(MetaMethod::Pairs, |lua, data, ()| { methods.add_meta_method(MetaMethod::Pairs, |lua, data, ()| {
use std::iter::FromIterator; use std::iter::FromIterator;
let stateless_iter = lua.create_function(|_, (data, i): (MyUserData, i64)| { let stateless_iter = lua.create_function(|_, (data, i): (MyUserData, i64)| {
@ -136,11 +141,16 @@ fn test_metamethods() -> Result<()> {
10 10
); );
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))] #[cfg(any(
let pairs_it = { feature = "lua54",
lua.load( feature = "lua53",
feature = "lua52",
feature = "luajit52"
))]
let pairs_it = lua
.load(
r#" r#"
function pairs_it() function()
local r = 0 local r = 0
for i, v in pairs(userdata1) do for i, v in pairs(userdata1) do
r = r + v r = r + v
@ -149,17 +159,21 @@ fn test_metamethods() -> Result<()> {
end end
"#, "#,
) )
.exec()?; .eval::<Function>()?;
globals.get::<_, Function>("pairs_it")?
};
assert_eq!(lua.load("userdata1 - userdata2").eval::<MyUserData>()?.0, 4); assert_eq!(lua.load("userdata1 - userdata2").eval::<MyUserData>()?.0, 4);
assert_eq!(lua.load("userdata1:get()").eval::<i64>()?, 7); assert_eq!(lua.load("userdata1:get()").eval::<i64>()?, 7);
assert_eq!(lua.load("userdata2.inner").eval::<i64>()?, 3); assert_eq!(lua.load("userdata2.inner").eval::<i64>()?, 3);
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
assert_eq!(pairs_it.call::<_, i64>(())?, 28);
assert!(lua.load("userdata2.nonexist_field").eval::<()>().is_err()); assert!(lua.load("userdata2.nonexist_field").eval::<()>().is_err());
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit52"
))]
assert_eq!(pairs_it.call::<_, i64>(())?, 28);
let userdata2: Value = globals.get("userdata2")?; let userdata2: Value = globals.get("userdata2")?;
let userdata3: Value = globals.get("userdata3")?; let userdata3: Value = globals.get("userdata3")?;