Merge branch 'dev'
This commit is contained in:
commit
204eedde3c
|
@ -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"
|
||||||
|
|
|
@ -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])
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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"))]
|
||||||
|
|
|
@ -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)+) => {
|
where
|
||||||
$(
|
T: ToLua<'lua>,
|
||||||
impl<'lua, T> ToLua<'lua> for [T; $N]
|
{
|
||||||
where
|
|
||||||
T: Clone + 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]
|
|
||||||
where
|
|
||||||
T: Clone + ToLua<'lua>,
|
|
||||||
{
|
|
||||||
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
|
|
||||||
Ok(Value::Table(
|
|
||||||
lua.create_sequence_from(self.iter().cloned())?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_convert_array! {
|
impl<'lua, T, const N: usize> FromLua<'lua> for [T; N]
|
||||||
0 1 2 3 4 5 6 7 8 9
|
where
|
||||||
10 11 12 13 14 15 16 17 18 19
|
T: FromLua<'lua>,
|
||||||
20 21 22 23 24 25 26 27 28 29
|
{
|
||||||
30 31 32
|
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
|
||||||
|
if let Value::Table(table) = value {
|
||||||
|
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()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> {
|
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Box<[T]> {
|
||||||
|
|
|
@ -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"))]
|
||||||
|
|
141
src/lua.rs
141
src/lua.rs
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
29
src/scope.rs
29
src/scope.rs
|
@ -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> {
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
15
src/util.rs
15
src/util.rs
|
@ -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",
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(())
|
||||||
|
}
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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")?;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue