mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 584b377640 | |||
| 1141073a65 | |||
| 5b1483bd56 | |||
| a74b637ed4 | |||
| 7623016d4a | |||
| bdd3c923ba | |||
| d586eef0f5 | |||
| 01154c0616 | |||
| e42d67c70d | |||
| 771a7775c5 | |||
| ee1c8a1a3d | |||
| 3597e34ffb |
@@ -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
@@ -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"
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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",
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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))) => {
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
})?;
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user