Compare commits

..

12 Commits

Author SHA1 Message Date
Alex Orlenko 584b377640 v0.6.4 2021-09-28 18:53:47 +01:00
Alex Orlenko 1141073a65 Update CHANGELOG 2021-09-28 18:51:42 +01:00
Alex Orlenko 5b1483bd56 Change syntax of protect_lua macro 2021-09-28 18:47:08 +01:00
Alex Orlenko a74b637ed4 Update check_stack requirements 2021-09-28 18:41:25 +01:00
Alex Orlenko 7623016d4a Fix doc feature labels 2021-09-28 18:15:53 +01:00
Alex Orlenko bdd3c923ba Fix table traversal used in recursion detection.
This fixes serializing same table multiple times within a parent table.
2021-09-28 16:41:39 +01:00
Alex Orlenko d586eef0f5 Refactor UserData metatables handling 2021-09-28 16:33:36 +01:00
Alex Orlenko 01154c0616 Minor refacor of wrapped failures cache 2021-09-28 16:27:24 +01:00
Alex Orlenko e42d67c70d Make protect_lua as a smart macro to choose from C/closure 2021-09-28 16:26:30 +01:00
Alex Orlenko 771a7775c5 Perf optimization: refactor metatable cache 2021-09-28 16:25:08 +01:00
Alex Orlenko ee1c8a1a3d Add inline attributes to few hot funcs 2021-09-28 16:19:29 +01:00
Alex Orlenko 3597e34ffb Fix internal HookCallback type (missing optional Send) 2021-09-28 16:19:12 +01:00
18 changed files with 446 additions and 269 deletions
+5
View File
@@ -1,3 +1,8 @@
## v0.6.4
- Performance optimizations
- Fixed table traversal used in recursion detection in deserializer
## v0.6.3
- Disabled catching Rust panics in userdata finalizers on drop. It also has positive performance impact.
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua"
version = "0.6.3" # remember to update html_root_url and mlua_derive
version = "0.6.4" # remember to update html_root_url and mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
edition = "2018"
repository = "https://github.com/khvzak/mlua"
+25
View File
@@ -20,24 +20,28 @@ use crate::userdata::{AnyUserData, UserData};
use crate::value::{FromLua, Nil, ToLua, Value};
impl<'lua> ToLua<'lua> for Value<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(self)
}
}
impl<'lua> FromLua<'lua> for Value<'lua> {
#[inline]
fn from_lua(lua_value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
Ok(lua_value)
}
}
impl<'lua> ToLua<'lua> for String<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(self))
}
}
impl<'lua> FromLua<'lua> for String<'lua> {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<String<'lua>> {
let ty = value.type_name();
lua.coerce_string(value)?
@@ -50,12 +54,14 @@ impl<'lua> FromLua<'lua> for String<'lua> {
}
impl<'lua> ToLua<'lua> for Table<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(self))
}
}
impl<'lua> FromLua<'lua> for Table<'lua> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Table<'lua>> {
match value {
Value::Table(table) => Ok(table),
@@ -69,12 +75,14 @@ impl<'lua> FromLua<'lua> for Table<'lua> {
}
impl<'lua> ToLua<'lua> for Function<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Function(self))
}
}
impl<'lua> FromLua<'lua> for Function<'lua> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Function<'lua>> {
match value {
Value::Function(table) => Ok(table),
@@ -88,12 +96,14 @@ impl<'lua> FromLua<'lua> for Function<'lua> {
}
impl<'lua> ToLua<'lua> for Thread<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Thread(self))
}
}
impl<'lua> FromLua<'lua> for Thread<'lua> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Thread<'lua>> {
match value {
Value::Thread(t) => Ok(t),
@@ -107,12 +117,14 @@ impl<'lua> FromLua<'lua> for Thread<'lua> {
}
impl<'lua> ToLua<'lua> for AnyUserData<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::UserData(self))
}
}
impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<AnyUserData<'lua>> {
match value {
Value::UserData(ud) => Ok(ud),
@@ -126,12 +138,14 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
}
impl<'lua, T: 'static + MaybeSend + UserData> ToLua<'lua> for T {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::UserData(lua.create_userdata(self)?))
}
}
impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<T> {
match value {
Value::UserData(ud) => Ok(ud.borrow::<T>()?.clone()),
@@ -145,12 +159,14 @@ impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
}
impl<'lua> ToLua<'lua> for Error {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Error(self))
}
}
impl<'lua> FromLua<'lua> for Error {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Error> {
match value {
Value::Error(err) => Ok(err),
@@ -164,12 +180,14 @@ impl<'lua> FromLua<'lua> for Error {
}
impl<'lua> ToLua<'lua> for bool {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Boolean(self))
}
}
impl<'lua> FromLua<'lua> for bool {
#[inline]
fn from_lua(v: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match v {
Value::Nil => Ok(false),
@@ -180,12 +198,14 @@ impl<'lua> FromLua<'lua> for bool {
}
impl<'lua> ToLua<'lua> for LightUserData {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::LightUserData(self))
}
}
impl<'lua> FromLua<'lua> for LightUserData {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::LightUserData(ud) => Ok(ud),
@@ -199,12 +219,14 @@ impl<'lua> FromLua<'lua> for LightUserData {
}
impl<'lua> ToLua<'lua> for StdString {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?))
}
}
impl<'lua> FromLua<'lua> for StdString {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
Ok(lua
@@ -220,6 +242,7 @@ impl<'lua> FromLua<'lua> for StdString {
}
impl<'lua> ToLua<'lua> for &str {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self)?))
}
@@ -595,6 +618,7 @@ impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
}
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
match self {
Some(val) => val.to_lua(lua),
@@ -604,6 +628,7 @@ impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
}
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Option<T> {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
match value {
Nil => Ok(None),
+3 -3
View File
@@ -5,7 +5,7 @@ use std::slice;
use crate::error::{Error, Result};
use crate::ffi;
use crate::types::LuaRef;
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, protect_lua, StackGuard};
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, StackGuard};
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
#[cfg(feature = "async")]
@@ -198,8 +198,8 @@ impl<'lua> Function<'lua> {
for arg in args {
lua.push_value(arg)?;
}
protect_lua(lua.state, nargs + 2, 1, |state| {
ffi::lua_pushcclosure(state, bind_call_impl, nargs + 2);
protect_lua!(lua.state, nargs + 2, 1, fn(state) {
ffi::lua_pushcclosure(state, bind_call_impl, ffi::lua_gettop(state));
})?;
Ok(Function(lua.pop_ref()))
+2 -1
View File
@@ -72,7 +72,7 @@
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
// mlua types in rustdoc of other crates get linked to here.
#![doc(html_root_url = "https://docs.rs/mlua/0.6.3")]
#![doc(html_root_url = "https://docs.rs/mlua/0.6.4")]
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
// warnings at all.
#![doc(test(attr(deny(warnings))))]
@@ -120,6 +120,7 @@ pub use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti
pub use crate::thread::AsyncThread;
#[cfg(feature = "serialize")]
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
#[doc(inline)]
pub use crate::serde::{
de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt,
+192 -134
View File
@@ -1,6 +1,6 @@
use std::any::TypeId;
use std::cell::{RefCell, UnsafeCell};
use std::collections::{HashMap, HashSet};
use std::cell::{Ref, RefCell, RefMut, UnsafeCell};
use std::collections::HashMap;
use std::ffi::CString;
use std::fmt;
use std::marker::PhantomData;
@@ -19,8 +19,8 @@ use crate::string::String;
use crate::table::Table;
use crate::thread::Thread;
use crate::types::{
Callback, CallbackUpvalue, HookCallback, Integer, LightUserData, LuaRef, MaybeSend, Number,
RegistryKey,
Callback, CallbackUpvalue, DestructedUserdataMT, HookCallback, Integer, LightUserData, LuaRef,
MaybeSend, Number, RegistryKey,
};
use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
@@ -28,9 +28,8 @@ use crate::userdata::{
use crate::util::{
self, assert_stack, callback_error, check_stack, get_destructed_userdata_metatable,
get_gc_metatable, get_gc_userdata, get_main_state, get_userdata, init_error_registry,
init_gc_metatable, init_userdata_metatable, pop_error, protect_lua, push_gc_userdata,
push_string, push_table, push_userdata, rawset_field, safe_pcall, safe_xpcall, StackGuard,
WrappedFailure,
init_gc_metatable, init_userdata_metatable, pop_error, push_gc_userdata, push_string,
push_table, push_userdata, rawset_field, safe_pcall, safe_xpcall, StackGuard, WrappedFailure,
};
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
@@ -65,7 +64,7 @@ pub struct Lua {
// Data associated with the Lua.
struct ExtraData {
registered_userdata: HashMap<TypeId, c_int>,
registered_userdata_mt: HashSet<isize>,
registered_userdata_mt: HashMap<*const c_void, Option<TypeId>>,
registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
libs: StdLib,
@@ -77,9 +76,8 @@ struct ExtraData {
ref_stack_top: c_int,
ref_free: Vec<c_int>,
// Vec of preallocated `WrappedFailure` enums
// Used for callbacks optimization
prealloc_wrapped_failures: Vec<c_int>,
// Pool of preallocated `WrappedFailure` enums
wrapped_failures_pool: Vec<c_int>,
hook_callback: Option<HookCallback>,
}
@@ -154,6 +152,8 @@ pub(crate) static ASYNC_POLL_PENDING: u8 = 0;
pub(crate) static WAKER_REGISTRY_KEY: u8 = 0;
pub(crate) static EXTRA_REGISTRY_KEY: u8 = 0;
const WRAPPED_FAILURES_POOL_SIZE: usize = 16;
/// Requires `feature = "send"`
#[cfg(feature = "send")]
#[cfg_attr(docsrs, doc(cfg(feature = "send")))]
@@ -164,7 +164,7 @@ impl Drop for Lua {
unsafe {
if !self.ephemeral {
let extra = &mut *self.extra.get();
for index in extra.prealloc_wrapped_failures.clone() {
for index in extra.wrapped_failures_pool.drain(..) {
ffi::lua_pushnil(extra.ref_thread);
ffi::lua_replace(extra.ref_thread, index);
extra.ref_free.push(index);
@@ -414,7 +414,7 @@ impl Lua {
// Create empty Waker slot
push_gc_userdata::<Option<Waker>>(state, None)?;
protect_lua(state, 1, 0, |state| {
protect_lua!(state, 1, 0, fn(state) {
let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, waker_key);
})?;
@@ -432,7 +432,7 @@ impl Lua {
// Create ref stack thread and place it in the registry to prevent it from being garbage
// collected.
let ref_thread = mlua_expect!(
protect_lua(state, 0, 0, |state| {
protect_lua!(state, 0, 0, |state| {
let thread = ffi::lua_newthread(state);
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
thread
@@ -444,7 +444,7 @@ impl Lua {
let extra = Arc::new(UnsafeCell::new(ExtraData {
registered_userdata: HashMap::new(),
registered_userdata_mt: HashSet::new(),
registered_userdata_mt: HashMap::new(),
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
ref_thread,
libs: StdLib::NONE,
@@ -454,14 +454,14 @@ impl Lua {
ref_stack_size: ffi::LUA_MINSTACK - 1,
ref_stack_top: 0,
ref_free: Vec::new(),
prealloc_wrapped_failures: Vec::new(),
wrapped_failures_pool: Vec::new(),
hook_callback: None,
}));
mlua_expect!(
(|state| {
push_gc_userdata(state, Arc::clone(&extra))?;
protect_lua(main_state, 1, 0, |state| {
protect_lua!(main_state, 1, 0, fn(state) {
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key);
})
@@ -469,6 +469,15 @@ impl Lua {
"Error while storing extra data",
);
// Register `DestructedUserdataMT` type
get_destructed_userdata_metatable(main_state);
let destructed_mt_ptr = ffi::lua_topointer(main_state, -1);
(*extra.get()).registered_userdata_mt.insert(
destructed_mt_ptr,
Some(TypeId::of::<DestructedUserdataMT>()),
);
ffi::lua_pop(main_state, 1);
mlua_debug_assert!(
ffi::lua_gettop(main_state) == main_state_top,
"stack leak during creation"
@@ -545,8 +554,8 @@ impl Lua {
{
let loaded = unsafe {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 3)?;
protect_lua(self.state, 0, 1, |state| {
check_stack(self.state, 2)?;
protect_lua!(self.state, 0, 1, fn(state) {
ffi::luaL_getsubtable(state, ffi::LUA_REGISTRYINDEX, cstr!("_LOADED"));
})?;
Table(self.pop_ref())
@@ -772,9 +781,7 @@ impl Lua {
let state = self.main_state.unwrap_or(self.state);
unsafe {
check_stack(state, 3)?;
protect_lua(state, 0, 0, |state| {
ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0);
})
protect_lua!(state, 0, 0, fn(state) ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0))
}
}
@@ -793,7 +800,7 @@ impl Lua {
let state = self.main_state.unwrap_or(self.state);
unsafe {
check_stack(state, 3)?;
protect_lua(state, 0, 0, |state| {
protect_lua!(state, 0, 0, |state| {
ffi::lua_gc(state, ffi::LUA_GCSTEP, kbytes) != 0
})
}
@@ -969,8 +976,8 @@ impl Lua {
pub fn create_table(&self) -> Result<Table> {
unsafe {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 3)?;
push_table(self.state, 0, 0)?;
check_stack(self.state, 2)?;
protect_lua!(self.state, 0, 1, fn(state) ffi::lua_newtable(state))?;
Ok(Table(self.pop_ref()))
}
}
@@ -1005,7 +1012,7 @@ impl Lua {
for (k, v) in iter {
self.push_value(k.to_lua(self)?)?;
self.push_value(v.to_lua(self)?)?;
protect_lua(self.state, 3, 1, |state| ffi::lua_rawset(state, -3))?;
protect_lua!(self.state, 3, 1, fn(state) ffi::lua_rawset(state, -3))?;
}
Ok(Table(self.pop_ref()))
@@ -1027,7 +1034,7 @@ impl Lua {
push_table(self.state, lower_bound as c_int, 0)?;
for (i, v) in iter.enumerate() {
self.push_value(v.to_lua(self)?)?;
protect_lua(self.state, 2, 1, |state| {
protect_lua!(self.state, 2, 1, |state| {
ffi::lua_rawseti(state, -2, (i + 1) as Integer);
})?;
}
@@ -1197,7 +1204,7 @@ impl Lua {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 3)?;
let thread_state = protect_lua(self.state, 0, 1, |state| ffi::lua_newthread(state))?;
let thread_state = protect_lua!(self.state, 0, 1, |state| ffi::lua_newthread(state))?;
self.push_ref(&func.0);
ffi::lua_xmove(self.state, thread_state, 1);
@@ -1312,7 +1319,7 @@ impl Lua {
check_stack(self.state, 4)?;
self.push_value(v)?;
let res = protect_lua(self.state, 1, 1, |state| {
let res = protect_lua!(self.state, 1, 1, |state| {
ffi::lua_tolstring(state, -1, ptr::null_mut())
})?;
if !res.is_null() {
@@ -1466,7 +1473,7 @@ impl Lua {
check_stack(self.state, 4)?;
self.push_value(t)?;
let registry_id = protect_lua(self.state, 1, 0, |state| {
let registry_id = protect_lua!(self.state, 1, 0, |state| {
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
})?;
@@ -1714,6 +1721,7 @@ impl Lua {
}
#[cfg(feature = "serialize")]
#[inline]
pub(crate) unsafe fn get_ref_ptr(&self, lref: &LuaRef) -> *const c_void {
ffi::lua_topointer((*self.extra.get()).ref_thread, lref.index)
}
@@ -1746,12 +1754,6 @@ impl Lua {
self.push_value(f(self)?)?;
rawset_field(self.state, -2, k.validate()?.name())?;
}
// Add special `__mlua_type_id` field
let type_id_ptr = protect_lua(self.state, 0, 1, |state| {
ffi::lua_newuserdata(state, mem::size_of::<TypeId>()) as *mut TypeId
})?;
ptr::write(type_id_ptr, type_id);
rawset_field(self.state, -2, "__mlua_type_id")?;
let metatable_index = ffi::lua_absindex(self.state, -1);
let mut extra_tables_count = 0;
@@ -1811,51 +1813,60 @@ impl Lua {
// Pop extra tables to get metatable on top of the stack
ffi::lua_pop(self.state, extra_tables_count);
let ptr = ffi::lua_topointer(self.state, -1);
let mt_ptr = ffi::lua_topointer(self.state, -1);
ffi::lua_pushvalue(self.state, -1);
let id = protect_lua(self.state, 1, 0, |state| {
let id = protect_lua!(self.state, 1, 0, |state| {
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
})?;
extra.registered_userdata.insert(type_id, id);
extra.registered_userdata_mt.insert(ptr as isize);
extra.registered_userdata_mt.insert(mt_ptr, Some(type_id));
Ok(())
}
pub(crate) unsafe fn register_userdata_metatable(&self, id: isize) {
(*self.extra.get()).registered_userdata_mt.insert(id);
pub(crate) unsafe fn register_userdata_metatable(
&self,
ptr: *const c_void,
type_id: Option<TypeId>,
) {
let extra = &mut *self.extra.get();
extra.registered_userdata_mt.insert(ptr, type_id);
}
pub(crate) unsafe fn deregister_userdata_metatable(&self, id: isize) {
(*self.extra.get()).registered_userdata_mt.remove(&id);
pub(crate) unsafe fn deregister_userdata_metatable(&self, ptr: *const c_void) {
(*self.extra.get()).registered_userdata_mt.remove(&ptr);
}
// Pushes a LuaRef value onto the stack, checking that it's a registered
// and not destructed UserData.
// Uses 3 stack spaces, does not call checkstack.
pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef, with_mt: bool) -> Result<()> {
// Uses 2 stack spaces, does not call checkstack.
pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<Option<TypeId>> {
self.push_ref(lref);
if ffi::lua_getmetatable(self.state, -1) == 0 {
return Err(Error::UserDataTypeMismatch);
}
// Check that userdata is registered
let ptr = ffi::lua_topointer(self.state, -1);
let mt_ptr = ffi::lua_topointer(self.state, -1);
ffi::lua_pop(self.state, 1);
let extra = &*self.extra.get();
if extra.registered_userdata_mt.contains(&(ptr as isize)) {
if !with_mt {
ffi::lua_pop(self.state, 1);
match extra.registered_userdata_mt.get(&mt_ptr) {
Some(&type_id) if type_id == Some(TypeId::of::<DestructedUserdataMT>()) => {
Err(Error::UserDataDestructed)
}
return Ok(());
Some(&type_id) => Ok(type_id),
None => Err(Error::UserDataTypeMismatch),
}
// Maybe userdata was destructed?
get_destructed_userdata_metatable(self.state);
if ffi::lua_rawequal(self.state, -1, -2) != 0 {
ffi::lua_pop(self.state, 2);
return Err(Error::UserDataDestructed);
}
ffi::lua_pop(self.state, 2);
Err(Error::UserDataTypeMismatch)
}
#[inline]
unsafe fn get_userdata_ref<T>(&self) -> Result<Ref<T>> {
(*get_userdata::<UserDataCell<T>>(self.state, -1)).try_borrow()
}
#[inline]
unsafe fn get_userdata_mut<T>(&self) -> Result<RefMut<T>> {
(*get_userdata::<UserDataCell<T>>(self.state, -1)).try_borrow_mut()
}
// Creates a Function out of a Callback containing a 'static Fn. This is safe ONLY because the
@@ -1917,7 +1928,7 @@ impl Lua {
let lua = self.clone();
let func = mem::transmute(func);
push_gc_userdata(self.state, CallbackUpvalue { lua, func })?;
protect_lua(self.state, 1, 1, |state| {
protect_lua!(self.state, 1, 1, fn(state) {
ffi::lua_pushcclosure(state, call_callback, 1);
})?;
@@ -1969,7 +1980,7 @@ impl Lua {
let fut = ((*upvalue).func)(lua, args);
let lua = lua.clone();
push_gc_userdata(state, AsyncPollUpvalue { lua, fut })?;
protect_lua(state, 1, 1, |state| {
protect_lua!(state, 1, 1, fn(state) {
ffi::lua_pushcclosure(state, poll_future, 1);
})?;
@@ -2035,7 +2046,7 @@ impl Lua {
let lua = self.clone();
let func = mem::transmute(func);
push_gc_userdata(self.state, AsyncCallbackUpvalue { lua, func })?;
protect_lua(self.state, 1, 1, |state| {
protect_lua!(self.state, 1, 1, fn(state) {
ffi::lua_pushcclosure(state, call_callback, 1);
})?;
@@ -2084,7 +2095,7 @@ impl Lua {
T: 'static + UserData,
{
let _sg = StackGuard::new(self.state);
check_stack(self.state, 2)?;
check_stack(self.state, 3)?;
// It's safe to push userdata first and then metatable.
// If the first push failed, unlikely we moved `data` to allocated memory.
@@ -2379,6 +2390,21 @@ impl<'lua, T: AsRef<[u8]> + ?Sized> AsChunk<'lua> for T {
}
}
// Creates required entries in the metatable cache (see `util::METATABLE_CACHE`)
pub(crate) fn init_metatable_cache(cache: &mut HashMap<TypeId, u8>) {
cache.insert(TypeId::of::<Arc<UnsafeCell<ExtraData>>>(), 0);
cache.insert(TypeId::of::<Callback>(), 0);
cache.insert(TypeId::of::<CallbackUpvalue>(), 0);
#[cfg(feature = "async")]
{
cache.insert(TypeId::of::<AsyncCallback>(), 0);
cache.insert(TypeId::of::<AsyncCallbackUpvalue>(), 0);
cache.insert(TypeId::of::<AsyncPollUpvalue>(), 0);
cache.insert(TypeId::of::<Option<Waker>>(), 0);
}
}
// An optimized version of `callback_error` that does not allocate `WrappedFailure` userdata
// and instead reuses unsed and cached values from previous calls (or allocates new).
// It requires `get_extra` function to return `ExtraData` value.
@@ -2410,18 +2436,16 @@ where
// We cannot shadow Rust errors with Lua ones, so we need to obtain pre-allocated memory
// to store a wrapped error or panic *before* we proceed.
let extra = &mut *get_extra(state);
let prealloc_failure = {
match extra.prealloc_wrapped_failures.pop() {
Some(index) => PreallocatedFailure::Cached(index),
None => {
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedFailure>());
ffi::lua_rotate(state, 1, 1);
PreallocatedFailure::New(ud as *mut WrappedFailure)
}
let prealloc_failure = match extra.wrapped_failures_pool.pop() {
Some(index) => PreallocatedFailure::Cached(index),
None => {
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedFailure>());
ffi::lua_rotate(state, 1, 1);
PreallocatedFailure::New(ud as *mut WrappedFailure)
}
};
let mut get_prealloc_failure = || match prealloc_failure {
let mut get_wrapped_failure = || match prealloc_failure {
PreallocatedFailure::New(ud) => {
ffi::lua_settop(state, 1);
ud
@@ -2441,19 +2465,21 @@ where
Ok(Ok(r)) => {
// Return unused WrappedFailure to the cache
match prealloc_failure {
PreallocatedFailure::New(_) if extra.prealloc_wrapped_failures.len() < 16 => {
PreallocatedFailure::New(_)
if extra.wrapped_failures_pool.len() < WRAPPED_FAILURES_POOL_SIZE =>
{
ffi::lua_rotate(state, 1, -1);
ffi::lua_xmove(state, extra.ref_thread, 1);
let index = ref_stack_pop(extra);
extra.prealloc_wrapped_failures.push(index);
extra.wrapped_failures_pool.push(index);
}
PreallocatedFailure::New(_) => {
ffi::lua_remove(state, 1);
}
PreallocatedFailure::Cached(index)
if extra.prealloc_wrapped_failures.len() < 16 =>
if extra.wrapped_failures_pool.len() < WRAPPED_FAILURES_POOL_SIZE =>
{
extra.prealloc_wrapped_failures.push(index);
extra.wrapped_failures_pool.push(index);
}
PreallocatedFailure::Cached(index) => {
ffi::lua_pushnil(extra.ref_thread);
@@ -2464,7 +2490,7 @@ where
r
}
Ok(Err(err)) => {
let wrapped_error = get_prealloc_failure();
let wrapped_error = get_wrapped_failure();
ptr::write(wrapped_error, WrappedFailure::Error(err));
get_gc_metatable::<WrappedFailure>(state);
ffi::lua_setmetatable(state, -2);
@@ -2486,7 +2512,7 @@ where
ffi::lua_error(state)
}
Err(p) => {
let wrapped_panic = get_prealloc_failure();
let wrapped_panic = get_wrapped_failure();
ptr::write(wrapped_panic, WrappedFailure::Panic(Some(p)));
get_gc_metatable::<WrappedFailure>(state);
ffi::lua_setmetatable(state, -2);
@@ -2505,7 +2531,7 @@ unsafe fn load_from_std_lib(state: *mut ffi::lua_State, libs: StdLib) -> Result<
glb: c_int,
) -> Result<()> {
let modname = mlua_expect!(CString::new(modname.as_ref()), "modname contains nil bytes");
protect_lua(state, 0, 1, |state| {
protect_lua!(state, 0, 1, |state| {
ffi::luaL_requiref(state, modname.as_ptr() as *const c_char, openf, glb)
})
}
@@ -2809,32 +2835,34 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
Box::new(move |lua, mut args| {
if let Some(front) = args.pop_front() {
let userdata = AnyUserData::from_lua(front, lua)?;
// Try normal userdata first
let err = match userdata.borrow::<T>() {
Ok(ud) => {
return method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
let type_id = lua.push_userdata_ref(&userdata.0)?;
match type_id {
Some(id) if id == TypeId::of::<T>() => {
let ud = lua.get_userdata_ref::<T>()?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud = lua.get_userdata_ref::<Rc<RefCell<T>>>()?;
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = lua.get_userdata_ref::<Arc<Mutex<T>>>()?;
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = lua.get_userdata_ref::<Arc<RwLock<T>>>()?;
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
_ => Err(Error::UserDataTypeMismatch),
}
Err(err) => err,
};
match userdata.type_id()? {
id if id == TypeId::of::<T>() => Err(err),
#[cfg(not(feature = "send"))]
id if id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud = userdata.borrow::<Rc<RefCell<T>>>()?;
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
id if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = userdata.borrow::<Arc<Mutex<T>>>()?;
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
id if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = userdata.borrow::<Arc<RwLock<T>>>()?;
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
_ => Err(Error::UserDataTypeMismatch),
}
} else {
Err(Error::FromLuaConversionError {
@@ -2859,35 +2887,38 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
let mut method = method
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
// Try normal userdata first
let err = match userdata.borrow_mut::<T>() {
Ok(mut ud) => {
return method(lua, &mut ud, A::from_lua_multi(args, lua)?)?
.to_lua_multi(lua)
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
let type_id = lua.push_userdata_ref(&userdata.0)?;
match type_id {
Some(id) if id == TypeId::of::<T>() => {
let mut ud = lua.get_userdata_mut::<T>()?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud = lua.get_userdata_mut::<Rc<RefCell<T>>>()?;
let mut ud = ud
.try_borrow_mut()
.map_err(|_| Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = lua.get_userdata_mut::<Arc<Mutex<T>>>()?;
let mut ud =
ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = lua.get_userdata_mut::<Arc<RwLock<T>>>()?;
let mut ud =
ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
_ => Err(Error::UserDataTypeMismatch),
}
Err(err) => err,
};
match userdata.type_id()? {
id if id == TypeId::of::<T>() => Err(err),
#[cfg(not(feature = "send"))]
id if id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud = userdata.borrow::<Rc<RefCell<T>>>()?;
let mut ud = ud
.try_borrow_mut()
.map_err(|_| Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
id if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = userdata.borrow::<Arc<Mutex<T>>>()?;
let mut ud = ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
id if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = userdata.borrow::<Arc<RwLock<T>>>()?;
let mut ud = ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?;
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
}
_ => Err(Error::UserDataTypeMismatch),
}
} else {
Err(Error::FromLuaConversionError {
@@ -2912,8 +2943,35 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
let fut_res = || {
if let Some(front) = args.pop_front() {
let userdata = AnyUserData::from_lua(front, lua)?;
let userdata = userdata.borrow::<T>()?.clone();
Ok(method(lua, userdata, A::from_lua_multi(args, lua)?))
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 2)?;
let type_id = lua.push_userdata_ref(&userdata.0)?;
match type_id {
Some(id) if id == TypeId::of::<T>() => {
let ud = lua.get_userdata_ref::<T>()?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
#[cfg(not(feature = "send"))]
Some(id) if id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud = lua.get_userdata_ref::<Rc<RefCell<T>>>()?;
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
Some(id) if id == TypeId::of::<Arc<Mutex<T>>>() => {
let ud = lua.get_userdata_ref::<Arc<Mutex<T>>>()?;
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
Some(id) if id == TypeId::of::<Arc<RwLock<T>>>() => {
let ud = lua.get_userdata_ref::<Arc<RwLock<T>>>()?;
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
Ok(method(lua, ud.clone(), A::from_lua_multi(args, lua)?))
}
_ => Err(Error::UserDataTypeMismatch),
}
}
} else {
Err(Error::FromLuaConversionError {
from: "missing argument",
+15
View File
@@ -94,3 +94,18 @@ macro_rules! require_module_feature {
compile_error!("Feature `module` must be enabled in the `mlua` crate");
};
}
macro_rules! protect_lua {
($state:expr, $nargs:expr, $nresults:expr, $f:expr) => {
crate::util::protect_lua_closure($state, $nargs, $nresults, $f)
};
($state:expr, $nargs:expr, $nresults:expr, fn($state_inner:ident) $code:expr) => {{
unsafe extern "C" fn do_call($state_inner: *mut ffi::lua_State) -> ::std::os::raw::c_int {
$code;
$nresults
}
crate::util::protect_lua_call($state, $nargs, do_call)
}};
}
+11 -11
View File
@@ -18,8 +18,8 @@ use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
};
use crate::util::{
assert_stack, check_stack, get_userdata, init_userdata_metatable, protect_lua, push_table,
rawset_field, take_userdata, StackGuard,
assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table, rawset_field,
take_userdata, StackGuard,
};
use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
@@ -250,7 +250,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
scope: &Scope<'lua, 'scope>,
data: Rc<RefCell<T>>,
data_ptr: *mut c_void,
data_ptr: *const c_void,
method: NonStaticMethod<'callback, T>,
) -> Result<Function<'lua>> {
// On methods that actually receive the userdata, we fake a type check on the passed in
@@ -264,9 +264,9 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
if let Some(Value::UserData(ud)) = value {
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 3)?;
lua.push_userdata_ref(&ud.0, false)?;
if get_userdata(lua.state, -1) == data_ptr {
check_stack(lua.state, 2)?;
lua.push_userdata_ref(&ud.0)?;
if get_userdata(lua.state, -1) as *const _ == data_ptr {
return Ok(());
}
}
@@ -322,7 +322,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 13)?;
let data_ptr = protect_lua(lua.state, 0, 1, |state| {
let data_ptr = protect_lua!(lua.state, 0, 1, |state| {
ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<Rc<RefCell<T>>>>())
})?;
// Prepare metatable, add meta methods first and then meta fields
@@ -390,12 +390,12 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
+ methods_index.map(|_| 1).unwrap_or(0);
ffi::lua_pop(lua.state, count);
let mt_id = ffi::lua_topointer(lua.state, -1);
let mt_ptr = ffi::lua_topointer(lua.state, -1);
// Write userdata just before attaching metatable with `__gc` metamethod
ptr::write(data_ptr as _, UserDataCell::new(data));
ffi::lua_setmetatable(lua.state, -2);
let ud = AnyUserData(lua.pop_ref());
lua.register_userdata_metatable(mt_id as isize);
lua.register_userdata_metatable(mt_ptr, None);
#[cfg(any(feature = "lua51", feature = "luajit"))]
let newtable = lua.create_table()?;
@@ -410,9 +410,9 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
// Deregister metatable
ffi::lua_getmetatable(state, -1);
let mt_id = ffi::lua_topointer(state, -1);
let mt_ptr = ffi::lua_topointer(state, -1);
ffi::lua_pop(state, 1);
ud.lua.deregister_userdata_metatable(mt_id as isize);
ud.lua.deregister_userdata_metatable(mt_ptr);
// Clear uservalue
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
+32 -14
View File
@@ -7,7 +7,7 @@ use std::string::String as StdString;
use serde::de::{self, IntoDeserializer};
use crate::error::{Error, Result};
use crate::table::{TablePairs, TableSequence};
use crate::table::{Table, TablePairs, TableSequence};
use crate::value::Value;
/// A struct for deserializing Lua values into Rust values.
@@ -158,11 +158,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
where
V: de::Visitor<'de>,
{
let (variant, value) = match self.value {
let (variant, value, _guard) = match self.value {
Value::Table(table) => {
let lua = table.0.lua;
let ptr = unsafe { lua.get_ref_ptr(&table.0) };
self.visited.borrow_mut().insert(ptr);
let _guard = RecursionGuard::new(&table, &self.visited);
let mut iter = table.pairs::<StdString, Value>();
let (variant, value) = match iter.next() {
@@ -185,9 +183,9 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
return Err(de::Error::custom("bad enum value"));
}
(variant, Some(value))
(variant, Some(value), Some(_guard))
}
Value::String(variant) => (variant.to_str()?.to_owned(), None),
Value::String(variant) => (variant.to_str()?.to_owned(), None, None),
_ => return Err(de::Error::custom("bad enum value")),
};
@@ -206,9 +204,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
{
match self.value {
Value::Table(t) => {
let lua = t.0.lua;
let ptr = unsafe { lua.get_ref_ptr(&t.0) };
self.visited.borrow_mut().insert(ptr);
let _guard = RecursionGuard::new(&t, &self.visited);
let len = t.raw_len() as usize;
let mut deserializer = SeqDeserializer {
@@ -261,9 +257,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
{
match self.value {
Value::Table(t) => {
let lua = t.0.lua;
let ptr = unsafe { lua.get_ref_ptr(&t.0) };
self.visited.borrow_mut().insert(ptr);
let _guard = RecursionGuard::new(&t, &self.visited);
let mut deserializer = MapDeserializer {
pairs: t.pairs(),
@@ -495,10 +489,34 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
}
}
// Adds `ptr` to the `visited` map and removes on drop
// Used to track recursive tables but allow to traverse same tables multiple times
struct RecursionGuard {
ptr: *const c_void,
visited: Rc<RefCell<HashSet<*const c_void>>>,
}
impl RecursionGuard {
#[inline]
fn new(table: &Table, visited: &Rc<RefCell<HashSet<*const c_void>>>) -> Self {
let visited = Rc::clone(visited);
let ptr = unsafe { table.0.lua.get_ref_ptr(&table.0) };
visited.borrow_mut().insert(ptr);
RecursionGuard { ptr, visited }
}
}
impl Drop for RecursionGuard {
fn drop(&mut self) {
self.visited.borrow_mut().remove(&self.ptr);
}
}
// Checks `options` and decides should we emit an error or skip next element
fn check_value_if_skip(
value: &Value,
options: Options,
visited: &Rc<RefCell<HashSet<*const c_void>>>,
visited: &RefCell<HashSet<*const c_void>>,
) -> Result<bool> {
match value {
Value::Table(table) => {
+4 -4
View File
@@ -10,7 +10,7 @@ use crate::ffi;
use crate::lua::Lua;
use crate::table::Table;
use crate::types::LightUserData;
use crate::util::{assert_stack, check_stack, protect_lua, StackGuard};
use crate::util::{assert_stack, check_stack, StackGuard};
use crate::value::Value;
/// Trait for serializing/deserializing Lua values using Serde.
@@ -240,10 +240,10 @@ impl<'lua> LuaSerdeExt<'lua> for Lua {
}
}
// Uses 6 stack spaces and calls checkstack.
// Uses 2 stack spaces and calls checkstack.
pub(crate) unsafe fn init_metatables(state: *mut ffi::lua_State) -> Result<()> {
check_stack(state, 3)?;
protect_lua(state, 0, 0, |state| {
check_stack(state, 2)?;
protect_lua!(state, 0, 0, fn(state) {
ffi::lua_createtable(state, 0, 1);
ffi::lua_pushstring(state, cstr!("__metatable"));
+4 -4
View File
@@ -9,7 +9,7 @@ use crate::lua::Lua;
use crate::string::String;
use crate::table::Table;
use crate::types::Integer;
use crate::util::{check_stack, protect_lua, StackGuard};
use crate::util::{check_stack, StackGuard};
use crate::value::{ToLua, Value};
/// A struct for serializing Rust values into Lua values.
@@ -318,12 +318,12 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
let value = lua.to_value_with(value, self.options)?;
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 5)?;
check_stack(lua.state, 4)?;
lua.push_ref(&self.table.0);
lua.push_value(value)?;
let len = ffi::lua_rawlen(lua.state, -2) as Integer;
protect_lua(lua.state, 2, 0, |state| {
protect_lua!(lua.state, 2, 0, fn(state) {
let len = ffi::lua_rawlen(state, -2) as Integer;
ffi::lua_rawseti(state, -2, len + 1);
})
}
+15 -16
View File
@@ -10,7 +10,7 @@ use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::types::{Integer, LuaRef};
use crate::util::{assert_stack, check_stack, protect_lua, StackGuard};
use crate::util::{assert_stack, check_stack, StackGuard};
use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
#[cfg(feature = "async")]
@@ -62,12 +62,12 @@ impl<'lua> Table<'lua> {
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 6)?;
check_stack(lua.state, 5)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
lua.push_value(value)?;
protect_lua(lua.state, 3, 0, |state| ffi::lua_settable(state, -3))
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_settable(state, -3))
}
}
@@ -101,11 +101,11 @@ impl<'lua> Table<'lua> {
let value = unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 5)?;
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
protect_lua(lua.state, 2, 1, |state| ffi::lua_gettable(state, -2))?;
protect_lua!(lua.state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
lua.pop_value()
};
@@ -119,13 +119,12 @@ impl<'lua> Table<'lua> {
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 5)?;
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
protect_lua(lua.state, 2, 1, |state| {
ffi::lua_gettable(state, -2) != ffi::LUA_TNIL
})
protect_lua!(lua.state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
Ok(ffi::lua_isnil(lua.state, -1) == 0)
}
}
@@ -193,12 +192,12 @@ impl<'lua> Table<'lua> {
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 6)?;
check_stack(lua.state, 5)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
lua.push_value(value)?;
protect_lua(lua.state, 3, 0, |state| ffi::lua_rawset(state, -3))
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
}
}
@@ -236,7 +235,7 @@ impl<'lua> Table<'lua> {
lua.push_ref(&self.0);
lua.push_value(value)?;
protect_lua(lua.state, 2, 0, |state| {
protect_lua!(lua.state, 2, 0, |state| {
for i in (idx..=size).rev() {
// table[i+1] = table[i]
ffi::lua_rawgeti(state, -2, i);
@@ -268,7 +267,7 @@ impl<'lua> Table<'lua> {
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
protect_lua(lua.state, 1, 0, |state| {
protect_lua!(lua.state, 1, 0, |state| {
for i in idx..size {
ffi::lua_rawgeti(state, -1, i + 1);
ffi::lua_rawseti(state, -2, i);
@@ -294,7 +293,7 @@ impl<'lua> Table<'lua> {
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
protect_lua(lua.state, 1, 0, |state| ffi::luaL_len(state, -1))
protect_lua!(lua.state, 1, 0, |state| ffi::luaL_len(state, -1))
}
}
@@ -671,7 +670,7 @@ where
lua.push_ref(&self.table);
lua.push_value(prev_key)?;
let next = protect_lua(lua.state, 2, ffi::LUA_MULTRET, |state| {
let next = protect_lua!(lua.state, 2, ffi::LUA_MULTRET, |state| {
ffi::lua_next(state, -2)
})?;
if next != 0 {
@@ -732,7 +731,7 @@ where
let res = if self.raw {
ffi::lua_rawgeti(lua.state, -1, index)
} else {
protect_lua(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index))?
protect_lua!(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index))?
};
match res {
ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None),
+3 -2
View File
@@ -4,7 +4,7 @@ use std::os::raw::c_int;
use crate::error::{Error, Result};
use crate::ffi;
use crate::types::LuaRef;
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, protect_lua, StackGuard};
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, StackGuard};
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored"), doc))]
@@ -135,7 +135,7 @@ impl<'lua> Thread<'lua> {
let ret = ffi::lua_resume(thread_state, lua.state, nargs, &mut nresults as *mut c_int);
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
protect_lua(lua.state, 0, 0, |_| error_traceback(thread_state))?;
protect_lua!(lua.state, 0, 0, |_| error_traceback(thread_state))?;
return Err(pop_error(thread_state, ret));
}
@@ -341,6 +341,7 @@ where
}
#[cfg(feature = "async")]
#[inline(always)]
fn is_poll_pending(val: &MultiValue) -> bool {
match val.iter().enumerate().last() {
Some((1, Value::LightUserData(ud))) => {
+6
View File
@@ -47,6 +47,10 @@ pub(crate) struct AsyncPollUpvalue<'lua> {
pub(crate) fut: LocalBoxFuture<'lua, Result<MultiValue<'lua>>>,
}
#[cfg(feature = "send")]
pub(crate) type HookCallback = Arc<RefCell<dyn FnMut(&Lua, Debug) -> Result<()> + Send>>;
#[cfg(not(feature = "send"))]
pub(crate) type HookCallback = Arc<RefCell<dyn FnMut(&Lua, Debug) -> Result<()>>>;
#[cfg(feature = "send")]
@@ -59,6 +63,8 @@ pub trait MaybeSend {}
#[cfg(not(feature = "send"))]
impl<T> MaybeSend for T {}
pub(crate) struct DestructedUserdataMT;
/// An auto generated key into the Lua registry.
///
/// This is a handle to a value stored inside the Lua registry. It is not automatically
+23 -46
View File
@@ -21,9 +21,7 @@ use crate::function::Function;
use crate::lua::Lua;
use crate::table::{Table, TablePairs};
use crate::types::{Callback, LuaRef, MaybeSend};
use crate::util::{
check_stack, get_destructed_userdata_metatable, get_userdata, push_string, StackGuard,
};
use crate::util::{check_stack, get_userdata, StackGuard};
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
@@ -597,11 +595,13 @@ pub trait UserData: Sized {
pub(crate) struct UserDataCell<T>(RefCell<UserDataWrapped<T>>);
impl<T> UserDataCell<T> {
#[inline]
pub(crate) fn new(data: T) -> Self {
UserDataCell(RefCell::new(UserDataWrapped::new(data)))
}
#[cfg(feature = "serialize")]
#[inline]
pub(crate) fn new_ser(data: T) -> Self
where
T: 'static + Serialize,
@@ -610,7 +610,8 @@ impl<T> UserDataCell<T> {
}
// Immutably borrows the wrapped value.
fn try_borrow(&self) -> Result<Ref<T>> {
#[inline]
pub(crate) fn try_borrow(&self) -> Result<Ref<T>> {
self.0
.try_borrow()
.map(|r| Ref::map(r, |r| r.deref()))
@@ -618,7 +619,8 @@ impl<T> UserDataCell<T> {
}
// Mutably borrows the wrapped value.
fn try_borrow_mut(&self) -> Result<RefMut<T>> {
#[inline]
pub(crate) fn try_borrow_mut(&self) -> Result<RefMut<T>> {
self.0
.try_borrow_mut()
.map(|r| RefMut::map(r, |r| r.deref_mut()))
@@ -633,11 +635,13 @@ pub(crate) enum UserDataWrapped<T> {
}
impl<T> UserDataWrapped<T> {
#[inline]
fn new(data: T) -> Self {
UserDataWrapped::Default(data)
}
#[cfg(feature = "serialize")]
#[inline]
fn new_ser(data: T) -> Self
where
T: 'static + Serialize,
@@ -659,6 +663,7 @@ impl<T> Drop for UserDataWrapped<T> {
impl<T> Deref for UserDataWrapped<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
match self {
Self::Default(data) => data,
@@ -669,6 +674,7 @@ impl<T> Deref for UserDataWrapped<T> {
}
impl<T> DerefMut for UserDataWrapped<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
Self::Default(data) => data,
@@ -726,6 +732,7 @@ impl<'lua> AnyUserData<'lua> {
///
/// Returns a `UserDataBorrowError` if the userdata is already mutably borrowed. Returns a
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
#[inline]
pub fn borrow<T: 'static + UserData>(&self) -> Result<Ref<T>> {
self.inspect(|cell| cell.try_borrow())
}
@@ -736,6 +743,7 @@ impl<'lua> AnyUserData<'lua> {
///
/// Returns a `UserDataBorrowMutError` if the userdata cannot be mutably borrowed.
/// Returns a `UserDataTypeMismatch` if the userdata is not of type `T`.
#[inline]
pub fn borrow_mut<T: 'static + UserData>(&self) -> Result<RefMut<T>> {
self.inspect(|cell| cell.try_borrow_mut())
}
@@ -761,7 +769,7 @@ impl<'lua> AnyUserData<'lua> {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 3)?;
lua.push_userdata_ref(&self.0, false)?;
lua.push_userdata_ref(&self.0)?;
lua.push_value(v)?;
ffi::lua_setuservalue(lua.state, -2);
@@ -780,7 +788,7 @@ impl<'lua> AnyUserData<'lua> {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 3)?;
lua.push_userdata_ref(&self.0, false)?;
lua.push_userdata_ref(&self.0)?;
ffi::lua_getuservalue(lua.state, -1);
lua.pop_value()
};
@@ -811,7 +819,7 @@ impl<'lua> AnyUserData<'lua> {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 3)?;
lua.push_userdata_ref(&self.0, false)?;
lua.push_userdata_ref(&self.0)?;
ffi::lua_getmetatable(lua.state, -1); // Checked that non-empty on the previous call
Ok(Table(lua.pop_ref()))
}
@@ -838,25 +846,6 @@ impl<'lua> AnyUserData<'lua> {
Ok(false)
}
pub(crate) fn type_id(&self) -> Result<TypeId> {
let lua = self.0.lua;
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 5)?;
// Push userdata with metatable
lua.push_userdata_ref(&self.0, true)?;
// Get the special `__mlua_type_id`
push_string(lua.state, "__mlua_type_id")?;
if ffi::lua_rawget(lua.state, -2) != ffi::LUA_TUSERDATA {
return Err(Error::UserDataTypeMismatch);
}
Ok(*(ffi::lua_touserdata(lua.state, -1) as *const TypeId))
}
}
fn inspect<'a, T, R, F>(&'a self, func: F) -> Result<R>
where
T: 'static + UserData,
@@ -865,25 +854,14 @@ impl<'lua> AnyUserData<'lua> {
let lua = self.0.lua;
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 3)?;
check_stack(lua.state, 2)?;
lua.push_ref(&self.0);
if ffi::lua_getmetatable(lua.state, -1) == 0 {
return Err(Error::UserDataTypeMismatch);
}
lua.push_userdata_metatable::<T>()?;
if ffi::lua_rawequal(lua.state, -1, -2) == 0 {
// Maybe UserData destructed?
ffi::lua_pop(lua.state, 1);
get_destructed_userdata_metatable(lua.state);
if ffi::lua_rawequal(lua.state, -1, -2) == 1 {
Err(Error::UserDataDestructed)
} else {
Err(Error::UserDataTypeMismatch)
let type_id = lua.push_userdata_ref(&self.0)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
func(&*get_userdata::<UserDataCell<T>>(lua.state, -1))
}
} else {
func(&*get_userdata::<UserDataCell<T>>(lua.state, -3))
_ => Err(Error::UserDataTypeMismatch),
}
}
}
@@ -987,8 +965,7 @@ impl<'lua> Serialize for AnyUserData<'lua> {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 3).map_err(ser::Error::custom)?;
lua.push_userdata_ref(&self.0, false)
.map_err(ser::Error::custom)?;
lua.push_userdata_ref(&self.0).map_err(ser::Error::custom)?;
let ud = &*get_userdata::<UserDataCell<c_void>>(lua.state, -1);
let data =
ud.0.try_borrow()
+62 -33
View File
@@ -4,7 +4,7 @@ use std::error::Error as StdError;
use std::fmt::Write;
use std::os::raw::{c_char, c_int, c_void};
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::{mem, ptr, slice};
use once_cell::sync::Lazy;
@@ -12,13 +12,17 @@ use once_cell::sync::Lazy;
use crate::error::{Error, Result};
use crate::ffi;
static METATABLE_CACHE: Lazy<Mutex<HashMap<TypeId, u8>>> = Lazy::new(|| {
// The capacity must(!) be greater than number of stored keys
Mutex::new(HashMap::with_capacity(32))
static METATABLE_CACHE: Lazy<HashMap<TypeId, u8>> = Lazy::new(|| {
let mut map = HashMap::with_capacity(32);
crate::lua::init_metatable_cache(&mut map);
map.insert(TypeId::of::<WrappedFailure>(), 0);
map.insert(TypeId::of::<String>(), 0);
map
});
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
// panic with an internal error message.
#[inline]
pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
// TODO: This should only be triggered when there is a logic error in `mlua`. In the future,
// when there is a way to be confident about stack safety and test it, this could be enabled
@@ -30,6 +34,7 @@ pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
}
// Checks that Lua has enough free stack space and returns `Error::StackError` on failure.
#[inline]
pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<()> {
if ffi::lua_checkstack(state, amount) == 0 {
Err(Error::StackError)
@@ -48,6 +53,7 @@ impl StackGuard {
// Creates a StackGuard instance with record of the stack size, and on Drop will check the
// stack size and drop any extra elements. If the stack size at the end is *smaller* than at
// the beginning, this is considered a fatal logic error and will result in a panic.
#[inline]
pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard {
StackGuard {
state,
@@ -57,6 +63,7 @@ impl StackGuard {
}
// Similar to `new`, but checks and keeps `extra` elements from top of the stack on Drop.
#[inline]
pub unsafe fn new_extra(state: *mut ffi::lua_State, extra: c_int) -> StackGuard {
StackGuard {
state,
@@ -83,6 +90,35 @@ impl Drop for StackGuard {
}
}
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
// limited lua stack. `nargs` is the same as the the parameter to `lua_pcall`, and `nresults` is
// always `LUA_MULTRET`. Provided function must *not* panic, and since it will generally be lonjmping,
// should not contain any values that implements Drop.
// Internally uses 2 extra stack spaces, and does not call checkstack.
pub unsafe fn protect_lua_call(
state: *mut ffi::lua_State,
nargs: c_int,
f: unsafe extern "C" fn(*mut ffi::lua_State) -> c_int,
) -> Result<()> {
let stack_start = ffi::lua_gettop(state) - nargs;
ffi::lua_pushcfunction(state, error_traceback);
ffi::lua_pushcfunction(state, f);
if nargs > 0 {
ffi::lua_rotate(state, stack_start + 1, 2);
}
let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start + 1);
ffi::lua_remove(state, stack_start + 1);
if ret == ffi::LUA_OK {
Ok(())
} else {
Err(pop_error(state, ret))
}
}
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
// limited lua stack. `nargs` and `nresults` are similar to the parameters of `lua_pcall`, but the
@@ -90,7 +126,7 @@ impl Drop for StackGuard {
// values are assumed to match the `nresults` param. Provided function must *not* panic, and since it
// will generally be lonjmping, should not contain any values that implements Drop.
// Internally uses 3 extra stack spaces, and does not call checkstack.
pub unsafe fn protect_lua<F, R>(
pub unsafe fn protect_lua_closure<F, R>(
state: *mut ffi::lua_State,
nargs: c_int,
nresults: c_int,
@@ -210,30 +246,32 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
}
}
// Uses 3 stack spaces
// Uses 3 stack spaces, does not call checkstack.
#[inline]
pub unsafe fn push_string<S: AsRef<[u8]> + ?Sized>(
state: *mut ffi::lua_State,
s: &S,
) -> Result<()> {
let s = s.as_ref();
protect_lua(state, 0, 1, |state| {
protect_lua!(state, 0, 1, |state| {
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
})
}
// Uses 3 stack spaces
// Uses 3 stack spaces, does not call checkstack.
#[inline]
pub unsafe fn push_table(state: *mut ffi::lua_State, narr: c_int, nrec: c_int) -> Result<()> {
protect_lua(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
}
// Uses 4 stack spaces
// Uses 4 stack spaces, does not call checkstack.
pub unsafe fn rawset_field<S>(state: *mut ffi::lua_State, table: c_int, field: &S) -> Result<()>
where
S: AsRef<[u8]> + ?Sized,
{
let field = field.as_ref();
ffi::lua_pushvalue(state, table);
protect_lua(state, 2, 0, |state| {
protect_lua!(state, 2, 0, |state| {
ffi::lua_pushlstring(state, field.as_ptr() as *const c_char, field.len());
ffi::lua_rotate(state, -3, 2);
ffi::lua_rawset(state, -3);
@@ -241,14 +279,16 @@ where
}
// Internally uses 3 stack spaces, does not call checkstack.
#[inline]
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
let ud = protect_lua(state, 0, 1, |state| {
let ud = protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
})?;
ptr::write(ud, t);
Ok(())
}
#[inline]
pub unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
let ud = ffi::lua_touserdata(state, index) as *mut T;
mlua_debug_assert!(!ud.is_null(), "userdata pointer is null");
@@ -412,7 +452,7 @@ pub unsafe fn init_userdata_metatable<T>(
ffi::lua_pushnil(state);
}
}
protect_lua(state, 3, 1, |state| {
protect_lua!(state, 3, 1, fn(state) {
ffi::lua_pushcclosure(state, meta_index_impl, 3);
})?;
}
@@ -428,7 +468,7 @@ pub unsafe fn init_userdata_metatable<T>(
match newindex_type {
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
ffi::lua_pushvalue(state, field_setters);
protect_lua(state, 2, 1, |state| {
protect_lua!(state, 2, 1, fn(state) {
ffi::lua_pushcclosure(state, meta_newindex_impl, 2);
})?;
}
@@ -646,17 +686,6 @@ pub unsafe fn init_gc_metatable<T: Any>(
) -> Result<()> {
check_stack(state, 6)?;
let type_id = TypeId::of::<T>();
let ref_addr = {
let mut mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache");
mlua_assert!(
mt_cache.capacity() - mt_cache.len() > 0,
"out of metatable cache capacity"
);
mt_cache.insert(type_id, 0);
&mt_cache[&type_id] as *const u8
};
push_table(state, 0, 3)?;
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
@@ -669,8 +698,10 @@ pub unsafe fn init_gc_metatable<T: Any>(
f(state)?;
}
protect_lua(state, 1, 0, |state| {
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void);
let type_id = TypeId::of::<T>();
let ref_addr = &METATABLE_CACHE[&type_id] as *const u8;
protect_lua!(state, 1, 0, |state| {
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *const c_void);
})?;
Ok(())
@@ -678,10 +709,8 @@ pub unsafe fn init_gc_metatable<T: Any>(
pub unsafe fn get_gc_metatable<T: Any>(state: *mut ffi::lua_State) {
let type_id = TypeId::of::<T>();
let ref_addr = {
let mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache");
mlua_expect!(mt_cache.get(&type_id), "gc metatable does not exist") as *const u8
};
let ref_addr =
mlua_expect!(METATABLE_CACHE.get(&type_id), "gc metatable does not exist") as *const u8;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *const c_void);
}
@@ -824,7 +853,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
}
ffi::lua_pop(state, 1);
protect_lua(state, 1, 0, |state| {
protect_lua!(state, 1, 0, fn(state) {
let destructed_mt_key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, destructed_mt_key);
})?;
@@ -832,7 +861,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
// Create error print buffer
init_gc_metatable::<String>(state, None)?;
push_gc_userdata(state, String::new())?;
protect_lua(state, 1, 0, |state| {
protect_lua!(state, 1, 0, fn(state) {
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
})?;
+13
View File
@@ -157,18 +157,21 @@ pub struct MultiValue<'lua>(Vec<Value<'lua>>);
impl<'lua> MultiValue<'lua> {
/// Creates an empty `MultiValue` containing no values.
#[inline]
pub fn new() -> MultiValue<'lua> {
MultiValue(Vec::new())
}
}
impl<'lua> Default for MultiValue<'lua> {
#[inline]
fn default() -> MultiValue<'lua> {
MultiValue::new()
}
}
impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> {
#[inline]
fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self {
MultiValue::from_vec(Vec::from_iter(iter))
}
@@ -178,6 +181,7 @@ impl<'lua> IntoIterator for MultiValue<'lua> {
type Item = Value<'lua>;
type IntoIter = iter::Rev<vec::IntoIter<Value<'lua>>>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter().rev()
}
@@ -187,43 +191,52 @@ impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
type Item = &'a Value<'lua>;
type IntoIter = iter::Rev<slice::Iter<'a, Value<'lua>>>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
(&self.0).iter().rev()
}
}
impl<'lua> MultiValue<'lua> {
#[inline]
pub fn from_vec(mut v: Vec<Value<'lua>>) -> MultiValue<'lua> {
v.reverse();
MultiValue(v)
}
#[inline]
pub fn into_vec(self) -> Vec<Value<'lua>> {
let mut v = self.0;
v.reverse();
v
}
#[inline]
pub(crate) fn reserve(&mut self, size: usize) {
self.0.reserve(size);
}
#[inline]
pub(crate) fn push_front(&mut self, value: Value<'lua>) {
self.0.push(value);
}
#[inline]
pub(crate) fn pop_front(&mut self) -> Option<Value<'lua>> {
self.0.pop()
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.len() == 0
}
#[inline]
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
self.0.iter().rev()
}
+30
View File
@@ -305,6 +305,36 @@ fn test_to_value_with_options() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
#[test]
fn test_from_value_nested_tables() -> Result<(), Box<dyn std::error::Error>> {
let lua = Lua::new();
let value = lua
.load(
r#"
local table_a = {a = "a"}
local table_b = {"b"}
return {
a = table_a,
b = {table_b, table_b},
ab = {a = table_a, b = table_b}
}
"#,
)
.eval::<Value>()?;
let got = lua.from_value::<serde_json::Value>(value)?;
assert_eq!(
got,
serde_json::json!({
"a": {"a": "a"},
"b": [["b"], ["b"]],
"ab": {"a": {"a": "a"}, "b": ["b"]},
})
);
Ok(())
}
#[test]
fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
let lua = Lua::new();