mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 35eedd5a5e | |||
| 53f873a482 | |||
| fc1fe2c15e | |||
| 7e7a44f4cd | |||
| 63c4861520 | |||
| 27e7facf9b | |||
| 31d32f2dda | |||
| 7d1b322e18 | |||
| d906405818 | |||
| 60fd060d47 | |||
| 9f02a9ca09 | |||
| 1d7f105585 | |||
| 1020315a9b |
@@ -0,0 +1 @@
|
||||
github: khvzak
|
||||
@@ -1,3 +1,11 @@
|
||||
## v0.6.3
|
||||
|
||||
- Disabled catching Rust panics in userdata finalizers on drop. It also has positive performance impact.
|
||||
- Added `Debug::event()` to the hook's Debug structure
|
||||
- Simplified interface of `hook::HookTriggers`
|
||||
- Added finalizer to `ExtraData` in module mode. This helps avoiding memory leak on closing state when Lua unloads modules and frees memory.
|
||||
- Added `DeserializeOptions` struct to control deserializer behavior (`from_value_with` function).
|
||||
|
||||
## v0.6.2
|
||||
|
||||
- New functionality: `Lua::load_from_function()` and `Lua::create_c_function()`
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.6.2" # remember to update html_root_url and mlua_derive
|
||||
version = "0.6.3" # 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"
|
||||
|
||||
+90
@@ -1,5 +1,6 @@
|
||||
use std::ffi::CStr;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::{BitOr, BitOrAssign};
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
use crate::ffi::{self, lua_Debug, lua_State};
|
||||
@@ -23,6 +24,25 @@ pub struct Debug<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Debug<'a> {
|
||||
/// Returns the specific event that triggered the hook.
|
||||
///
|
||||
/// For [Lua 5.1] `DebugEvent::TailCall` is used for return events to indicate a return
|
||||
/// from a function that did a tail call.
|
||||
///
|
||||
/// [Lua 5.1]: https://www.lua.org/manual/5.1/manual.html#pdf-LUA_HOOKTAILRET
|
||||
pub fn event(&self) -> DebugEvent {
|
||||
unsafe {
|
||||
match (*self.ar).event {
|
||||
ffi::LUA_HOOKCALL => DebugEvent::Call,
|
||||
ffi::LUA_HOOKRET => DebugEvent::Ret,
|
||||
ffi::LUA_HOOKTAILCALL => DebugEvent::TailCall,
|
||||
ffi::LUA_HOOKLINE => DebugEvent::Line,
|
||||
ffi::LUA_HOOKCOUNT => DebugEvent::Count,
|
||||
event => mlua_panic!("Unknown Lua event code: {}", event),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to the `n` what mask.
|
||||
pub fn names(&self) -> DebugNames<'a> {
|
||||
unsafe {
|
||||
@@ -95,6 +115,16 @@ impl<'a> Debug<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a specific event that triggered the hook.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum DebugEvent {
|
||||
Call,
|
||||
Ret,
|
||||
TailCall,
|
||||
Line,
|
||||
Count,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DebugNames<'a> {
|
||||
pub name: Option<&'a [u8]>,
|
||||
@@ -140,6 +170,46 @@ pub struct HookTriggers {
|
||||
}
|
||||
|
||||
impl HookTriggers {
|
||||
/// Returns a new instance of `HookTriggers` with [`on_calls`] trigger set.
|
||||
///
|
||||
/// [`on_calls`]: #structfield.on_calls
|
||||
pub fn on_calls() -> Self {
|
||||
HookTriggers {
|
||||
on_calls: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with [`on_returns`] trigger set.
|
||||
///
|
||||
/// [`on_returns`]: #structfield.on_returns
|
||||
pub fn on_returns() -> Self {
|
||||
HookTriggers {
|
||||
on_returns: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with [`every_line`] trigger set.
|
||||
///
|
||||
/// [`every_line`]: #structfield.every_line
|
||||
pub fn every_line() -> Self {
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a new instance of `HookTriggers` with [`every_nth_instruction`] trigger set.
|
||||
///
|
||||
/// [`every_nth_instruction`]: #structfield.every_nth_instruction
|
||||
pub fn every_nth_instruction(n: u32) -> Self {
|
||||
HookTriggers {
|
||||
every_nth_instruction: Some(n),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the mask to pass to `lua_sethook`.
|
||||
pub(crate) fn mask(&self) -> c_int {
|
||||
let mut mask: c_int = 0;
|
||||
@@ -165,6 +235,26 @@ impl HookTriggers {
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr for HookTriggers {
|
||||
type Output = Self;
|
||||
|
||||
fn bitor(mut self, rhs: Self) -> Self::Output {
|
||||
self.on_calls |= rhs.on_calls;
|
||||
self.on_returns |= rhs.on_returns;
|
||||
self.every_line |= rhs.every_line;
|
||||
if self.every_nth_instruction.is_none() && rhs.every_nth_instruction.is_some() {
|
||||
self.every_nth_instruction = rhs.every_nth_instruction;
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOrAssign for HookTriggers {
|
||||
fn bitor_assign(&mut self, rhs: Self) {
|
||||
*self = *self | rhs;
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe extern "C" fn hook_proc(state: *mut lua_State, ar: *mut lua_Debug) {
|
||||
callback_error(state, |_| {
|
||||
let debug = Debug {
|
||||
|
||||
+5
-3
@@ -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.2")]
|
||||
#![doc(html_root_url = "https://docs.rs/mlua/0.6.3")]
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
@@ -102,7 +102,7 @@ pub use crate::{ffi::lua_CFunction, ffi::lua_State};
|
||||
|
||||
pub use crate::error::{Error, ExternalError, ExternalResult, Result};
|
||||
pub use crate::function::Function;
|
||||
pub use crate::hook::{Debug, DebugNames, DebugSource, DebugStack, HookTriggers};
|
||||
pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack, HookTriggers};
|
||||
pub use crate::lua::{AsChunk, Chunk, ChunkMode, GCMode, Lua, LuaOptions};
|
||||
pub use crate::multi::Variadic;
|
||||
pub use crate::scope::Scope;
|
||||
@@ -121,7 +121,9 @@ pub use crate::thread::AsyncThread;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[doc(inline)]
|
||||
pub use crate::serde::{ser::Options as SerializeOptions, LuaSerdeExt};
|
||||
pub use crate::serde::{
|
||||
de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt,
|
||||
};
|
||||
|
||||
pub mod prelude;
|
||||
#[cfg(feature = "serialize")]
|
||||
|
||||
+133
-88
@@ -55,7 +55,7 @@ use serde::Serialize;
|
||||
pub struct Lua {
|
||||
pub(crate) state: *mut ffi::lua_State,
|
||||
main_state: Option<*mut ffi::lua_State>,
|
||||
extra: *mut ExtraData,
|
||||
extra: Arc<UnsafeCell<ExtraData>>,
|
||||
ephemeral: bool,
|
||||
safe: bool,
|
||||
// Lua has lots of interior mutability, should not be RefUnwindSafe
|
||||
@@ -69,7 +69,7 @@ struct ExtraData {
|
||||
registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
|
||||
libs: StdLib,
|
||||
mem_info: *mut MemoryInfo,
|
||||
mem_info: Option<Box<MemoryInfo>>,
|
||||
safe: bool, // Same as in the Lua struct
|
||||
|
||||
ref_thread: *mut ffi::lua_State,
|
||||
@@ -77,8 +77,8 @@ struct ExtraData {
|
||||
ref_stack_top: c_int,
|
||||
ref_free: Vec<c_int>,
|
||||
|
||||
// Vec of preallocated WrappedFailure enums
|
||||
// Used for callback optimization
|
||||
// Vec of preallocated `WrappedFailure` enums
|
||||
// Used for callbacks optimization
|
||||
prealloc_wrapped_failures: Vec<c_int>,
|
||||
|
||||
hook_callback: Option<HookCallback>,
|
||||
@@ -163,7 +163,7 @@ impl Drop for Lua {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if !self.ephemeral {
|
||||
let extra = &mut *self.extra;
|
||||
let extra = &mut *self.extra.get();
|
||||
for index in extra.prealloc_wrapped_failures.clone() {
|
||||
ffi::lua_pushnil(extra.ref_thread);
|
||||
ffi::lua_replace(extra.ref_thread, index);
|
||||
@@ -174,17 +174,18 @@ impl Drop for Lua {
|
||||
&& extra.ref_stack_top as usize == extra.ref_free.len(),
|
||||
"reference leak detected"
|
||||
);
|
||||
*mlua_expect!(extra.registry_unref_list.lock(), "unref list poisoned") = None;
|
||||
ffi::lua_close(mlua_expect!(self.main_state, "main_state is null"));
|
||||
if !extra.mem_info.is_null() {
|
||||
Box::from_raw(extra.mem_info);
|
||||
}
|
||||
Box::from_raw(extra);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ExtraData {
|
||||
fn drop(&mut self) {
|
||||
*mlua_expect!(self.registry_unref_list.lock(), "unref list poisoned") = None;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Lua {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Lua({:p})", self.state)
|
||||
@@ -249,7 +250,7 @@ impl Lua {
|
||||
mlua_expect!(lua.disable_c_modules(), "Error during disabling C modules");
|
||||
}
|
||||
lua.safe = true;
|
||||
unsafe { (*lua.extra).safe = true };
|
||||
unsafe { (*lua.extra.get()).safe = true };
|
||||
|
||||
Ok(lua)
|
||||
}
|
||||
@@ -326,13 +327,13 @@ impl Lua {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
let mem_info = Box::into_raw(Box::new(MemoryInfo {
|
||||
let mut mem_info = Box::new(MemoryInfo {
|
||||
used_memory: 0,
|
||||
memory_limit: 0,
|
||||
}));
|
||||
});
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
let state = ffi::lua_newstate(allocator, mem_info as *mut c_void);
|
||||
let state = ffi::lua_newstate(allocator, &mut *mem_info as *mut MemoryInfo as *mut c_void);
|
||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||
let state = ffi::luaL_newstate();
|
||||
|
||||
@@ -342,11 +343,11 @@ impl Lua {
|
||||
let mut lua = Lua::init_from_ptr(state);
|
||||
lua.ephemeral = false;
|
||||
|
||||
let extra = &mut *lua.extra;
|
||||
let extra = &mut *lua.extra.get();
|
||||
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
extra.mem_info = mem_info;
|
||||
extra.mem_info = Some(mem_info);
|
||||
}
|
||||
|
||||
mlua_expect!(
|
||||
@@ -381,6 +382,9 @@ impl Lua {
|
||||
}
|
||||
|
||||
/// Constructs a new Lua instance from an existing raw state.
|
||||
///
|
||||
/// Once called, a returned Lua state is cached in the registry and can be retrieved
|
||||
/// by calling this function again.
|
||||
#[allow(clippy::missing_safety_doc)]
|
||||
pub unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> Lua {
|
||||
let maybe_main_state = get_main_state(state);
|
||||
@@ -391,13 +395,14 @@ impl Lua {
|
||||
return lua;
|
||||
}
|
||||
|
||||
let ref_thread = mlua_expect!(
|
||||
mlua_expect!(
|
||||
(|state| {
|
||||
init_error_registry(state)?;
|
||||
|
||||
// Create the internal metatables and place them in the registry
|
||||
// to prevent them from being garbage collected.
|
||||
|
||||
init_gc_metatable::<Arc<UnsafeCell<ExtraData>>>(state, None)?;
|
||||
init_gc_metatable::<Callback>(state, None)?;
|
||||
init_gc_metatable::<CallbackUpvalue>(state, None)?;
|
||||
#[cfg(feature = "async")]
|
||||
@@ -419,28 +424,31 @@ impl Lua {
|
||||
#[cfg(feature = "serialize")]
|
||||
crate::serde::init_metatables(state)?;
|
||||
|
||||
// Create ref stack thread and place it in the registry to prevent it from being garbage
|
||||
// collected.
|
||||
let ref_thread = protect_lua(state, 0, 0, |state| {
|
||||
let thread = ffi::lua_newthread(state);
|
||||
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
|
||||
thread
|
||||
})?;
|
||||
|
||||
Ok::<_, Error>(ref_thread)
|
||||
Ok::<_, Error>(())
|
||||
})(main_state),
|
||||
"Error during Lua construction",
|
||||
);
|
||||
|
||||
// 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| {
|
||||
let thread = ffi::lua_newthread(state);
|
||||
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX);
|
||||
thread
|
||||
}),
|
||||
"Error while creating ref thread",
|
||||
);
|
||||
|
||||
// Create ExtraData
|
||||
|
||||
let extra = Box::into_raw(Box::new(ExtraData {
|
||||
let extra = Arc::new(UnsafeCell::new(ExtraData {
|
||||
registered_userdata: HashMap::new(),
|
||||
registered_userdata_mt: HashSet::new(),
|
||||
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
|
||||
ref_thread,
|
||||
libs: StdLib::NONE,
|
||||
mem_info: ptr::null_mut(),
|
||||
mem_info: None,
|
||||
safe: false,
|
||||
// We need 1 extra stack space to move values in and out of the ref stack.
|
||||
ref_stack_size: ffi::LUA_MINSTACK - 1,
|
||||
@@ -450,12 +458,14 @@ impl Lua {
|
||||
hook_callback: None,
|
||||
}));
|
||||
|
||||
ffi::lua_pushlightuserdata(main_state, extra as *mut c_void);
|
||||
mlua_expect!(
|
||||
protect_lua(main_state, 1, 0, |state| {
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key)
|
||||
}),
|
||||
(|state| {
|
||||
push_gc_userdata(state, Arc::clone(&extra))?;
|
||||
protect_lua(main_state, 1, 0, |state| {
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key);
|
||||
})
|
||||
})(main_state),
|
||||
"Error while storing extra data",
|
||||
);
|
||||
|
||||
@@ -499,7 +509,7 @@ impl Lua {
|
||||
let res = unsafe { load_from_std_lib(state, libs) };
|
||||
|
||||
// If `package` library loaded into a safe lua state then disable C modules
|
||||
let extra = unsafe { &mut *self.extra };
|
||||
let extra = unsafe { &mut *self.extra.get() };
|
||||
let curr_libs = extra.libs;
|
||||
if self.safe && (curr_libs ^ (curr_libs | libs)).contains(StdLib::PACKAGE) {
|
||||
mlua_expect!(self.disable_c_modules(), "Error during disabling C modules");
|
||||
@@ -582,29 +592,57 @@ impl Lua {
|
||||
}
|
||||
|
||||
// Executes module entrypoint function, which returns only one Value.
|
||||
// The returned value then pushed to the Lua stack.
|
||||
// The returned value then pushed onto the stack.
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
pub fn entrypoint1<'lua, 'callback, R, F>(&'lua self, func: F) -> Result<c_int>
|
||||
pub unsafe fn entrypoint<'lua, A, R, F>(self, func: F) -> Result<c_int>
|
||||
where
|
||||
'lua: 'callback,
|
||||
R: ToLua<'callback>,
|
||||
F: 'static + MaybeSend + Fn(&'callback Lua) -> Result<R>,
|
||||
A: FromLuaMulti<'lua>,
|
||||
R: ToLua<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua, A) -> Result<R>,
|
||||
{
|
||||
let cb = self.create_callback(Box::new(move |lua, _| func(lua)?.to_lua_multi(lua)))?;
|
||||
let res = cb.call(());
|
||||
unsafe {
|
||||
check_stack(self.state, 2)?;
|
||||
match res {
|
||||
Ok(res) => self.push_value(res)?,
|
||||
Err(err) => {
|
||||
self.push_value(Value::Error(err))?;
|
||||
// This longjmp is undesired, but we cannot wrap it to a C
|
||||
ffi::lua_error(self.state)
|
||||
}
|
||||
let entrypoint_inner = |lua: &'lua Lua, func: F| {
|
||||
let nargs = ffi::lua_gettop(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
let mut args = MultiValue::new();
|
||||
args.reserve(nargs as usize);
|
||||
for _ in 0..nargs {
|
||||
args.push_front(lua.pop_value());
|
||||
}
|
||||
|
||||
// We create callback rather than call `func` directly to catch errors
|
||||
// with attached stacktrace.
|
||||
let callback = lua.create_callback(Box::new(move |lua, args| {
|
||||
func(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}))?;
|
||||
callback.call(args)
|
||||
};
|
||||
Ok(1)
|
||||
|
||||
match entrypoint_inner(mem::transmute(&self), func) {
|
||||
Ok(res) => {
|
||||
self.push_value(res)?;
|
||||
Ok(1)
|
||||
}
|
||||
Err(err) => {
|
||||
self.push_value(Value::Error(err))?;
|
||||
let state = self.state;
|
||||
// Lua (self) must be dropped before triggering longjmp
|
||||
drop(self);
|
||||
ffi::lua_error(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A simple module entrypoint without arguments
|
||||
#[doc(hidden)]
|
||||
#[cfg(not(tarpaulin_include))]
|
||||
pub unsafe fn entrypoint1<'lua, R, F>(self, func: F) -> Result<c_int>
|
||||
where
|
||||
R: ToLua<'lua>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua) -> Result<R>,
|
||||
{
|
||||
self.entrypoint(move |lua, _: ()| func(lua))
|
||||
}
|
||||
|
||||
/// Sets a 'hook' function that will periodically be called as Lua code executes.
|
||||
@@ -625,9 +663,7 @@ impl Lua {
|
||||
/// # use mlua::{Lua, HookTriggers, Result};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// lua.set_hook(HookTriggers {
|
||||
/// every_line: true, ..Default::default()
|
||||
/// }, |_lua, debug| {
|
||||
/// lua.set_hook(HookTriggers::every_line(), |_lua, debug| {
|
||||
/// println!("line {}", debug.curr_line());
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
@@ -648,7 +684,7 @@ impl Lua {
|
||||
{
|
||||
let state = self.main_state.ok_or(Error::MainThreadNotAvailable)?;
|
||||
unsafe {
|
||||
(*self.extra).hook_callback = Some(Arc::new(RefCell::new(callback)));
|
||||
(*self.extra.get()).hook_callback = Some(Arc::new(RefCell::new(callback)));
|
||||
ffi::lua_sethook(state, Some(hook_proc), triggers.mask(), triggers.count());
|
||||
}
|
||||
Ok(())
|
||||
@@ -663,7 +699,7 @@ impl Lua {
|
||||
None => return,
|
||||
};
|
||||
unsafe {
|
||||
(*self.extra).hook_callback = None;
|
||||
(*self.extra.get()).hook_callback = None;
|
||||
ffi::lua_sethook(state, None, 0, 0);
|
||||
}
|
||||
}
|
||||
@@ -672,14 +708,14 @@ impl Lua {
|
||||
pub fn used_memory(&self) -> usize {
|
||||
unsafe {
|
||||
let state = self.main_state.unwrap_or(self.state);
|
||||
match (*self.extra).mem_info {
|
||||
mem_info if mem_info.is_null() => {
|
||||
match &(*self.extra.get()).mem_info {
|
||||
Some(mem_info) => mem_info.used_memory as usize,
|
||||
None => {
|
||||
// Get data from the Lua GC
|
||||
let used_kbytes = ffi::lua_gc(state, ffi::LUA_GCCOUNT, 0);
|
||||
let used_kbytes_rem = ffi::lua_gc(state, ffi::LUA_GCCOUNTB, 0);
|
||||
(used_kbytes as usize) * 1024 + (used_kbytes_rem as usize)
|
||||
}
|
||||
mem_info => (*mem_info).used_memory as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -696,13 +732,13 @@ impl Lua {
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", doc))]
|
||||
pub fn set_memory_limit(&self, memory_limit: usize) -> Result<usize> {
|
||||
unsafe {
|
||||
match (*self.extra).mem_info {
|
||||
mem_info if mem_info.is_null() => Err(Error::MemoryLimitNotAvailable),
|
||||
mem_info => {
|
||||
let prev_limit = (*mem_info).memory_limit as usize;
|
||||
(*mem_info).memory_limit = memory_limit as isize;
|
||||
match &mut (*self.extra.get()).mem_info {
|
||||
Some(mem_info) => {
|
||||
let prev_limit = mem_info.memory_limit as usize;
|
||||
mem_info.memory_limit = memory_limit as isize;
|
||||
Ok(prev_limit)
|
||||
}
|
||||
None => Err(Error::MemoryLimitNotAvailable),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1436,7 +1472,7 @@ impl Lua {
|
||||
|
||||
Ok(RegistryKey {
|
||||
registry_id,
|
||||
unref_list: (*self.extra).registry_unref_list.clone(),
|
||||
unref_list: (*self.extra.get()).registry_unref_list.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1492,7 +1528,7 @@ impl Lua {
|
||||
/// `Error::MismatchedRegistryKey` if passed a `RegistryKey` that was not created with a
|
||||
/// matching `Lua` state.
|
||||
pub fn owns_registry_value(&self, key: &RegistryKey) -> bool {
|
||||
let registry_unref_list = unsafe { &(*self.extra).registry_unref_list };
|
||||
let registry_unref_list = unsafe { &(*self.extra.get()).registry_unref_list };
|
||||
Arc::ptr_eq(&key.unref_list, registry_unref_list)
|
||||
}
|
||||
|
||||
@@ -1504,7 +1540,7 @@ impl Lua {
|
||||
pub fn expire_registry_values(&self) {
|
||||
unsafe {
|
||||
let mut unref_list = mlua_expect!(
|
||||
(*self.extra).registry_unref_list.lock(),
|
||||
(*self.extra.get()).registry_unref_list.lock(),
|
||||
"unref list poisoned"
|
||||
);
|
||||
let unref_list = mem::replace(&mut *unref_list, Some(Vec::new()));
|
||||
@@ -1635,11 +1671,12 @@ impl Lua {
|
||||
// Pushes a LuaRef value onto the stack, uses 1 stack space, does not call checkstack
|
||||
pub(crate) unsafe fn push_ref<'lua>(&'lua self, lref: &LuaRef<'lua>) {
|
||||
assert!(
|
||||
lref.lua.extra == self.extra,
|
||||
Arc::ptr_eq(&lref.lua.extra, &self.extra),
|
||||
"Lua instance passed Value created from a different main Lua state"
|
||||
);
|
||||
ffi::lua_pushvalue((*self.extra).ref_thread, lref.index);
|
||||
ffi::lua_xmove((*self.extra).ref_thread, self.state, 1);
|
||||
let extra = &*self.extra.get();
|
||||
ffi::lua_pushvalue(extra.ref_thread, lref.index);
|
||||
ffi::lua_xmove(extra.ref_thread, self.state, 1);
|
||||
}
|
||||
|
||||
// Pops the topmost element of the stack and stores a reference to it. This pins the object,
|
||||
@@ -1652,7 +1689,7 @@ impl Lua {
|
||||
// number of short term references being created, and `RegistryKey` being used for long term
|
||||
// references.
|
||||
pub(crate) unsafe fn pop_ref(&self) -> LuaRef {
|
||||
let extra = &mut *self.extra;
|
||||
let extra = &mut *self.extra.get();
|
||||
ffi::lua_xmove(self.state, extra.ref_thread, 1);
|
||||
let index = ref_stack_pop(extra);
|
||||
LuaRef { lua: self, index }
|
||||
@@ -1660,7 +1697,7 @@ impl Lua {
|
||||
|
||||
pub(crate) fn clone_ref<'lua>(&'lua self, lref: &LuaRef<'lua>) -> LuaRef<'lua> {
|
||||
unsafe {
|
||||
let extra = &mut *self.extra;
|
||||
let extra = &mut *self.extra.get();
|
||||
ffi::lua_pushvalue(extra.ref_thread, lref.index);
|
||||
let index = ref_stack_pop(extra);
|
||||
LuaRef { lua: self, index }
|
||||
@@ -1669,16 +1706,23 @@ impl Lua {
|
||||
|
||||
pub(crate) fn drop_ref<'lua>(&'lua self, lref: &mut LuaRef<'lua>) {
|
||||
unsafe {
|
||||
let extra = &mut *self.extra;
|
||||
let extra = &mut *self.extra.get();
|
||||
ffi::lua_pushnil(extra.ref_thread);
|
||||
ffi::lua_replace(extra.ref_thread, lref.index);
|
||||
extra.ref_free.push(lref.index);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) unsafe fn get_ref_ptr(&self, lref: &LuaRef) -> *const c_void {
|
||||
ffi::lua_topointer((*self.extra.get()).ref_thread, lref.index)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn push_userdata_metatable<T: 'static + UserData>(&self) -> Result<()> {
|
||||
let extra = &mut *self.extra.get();
|
||||
|
||||
let type_id = TypeId::of::<T>();
|
||||
if let Some(&table_id) = (*self.extra).registered_userdata.get(&type_id) {
|
||||
if let Some(&table_id) = extra.registered_userdata.get(&type_id) {
|
||||
ffi::lua_rawgeti(self.state, ffi::LUA_REGISTRYINDEX, table_id as Integer);
|
||||
return Ok(());
|
||||
}
|
||||
@@ -1773,7 +1817,6 @@ impl Lua {
|
||||
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
|
||||
})?;
|
||||
|
||||
let extra = &mut *self.extra;
|
||||
extra.registered_userdata.insert(type_id, id);
|
||||
extra.registered_userdata_mt.insert(ptr as isize);
|
||||
|
||||
@@ -1781,11 +1824,11 @@ impl Lua {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn register_userdata_metatable(&self, id: isize) {
|
||||
(*self.extra).registered_userdata_mt.insert(id);
|
||||
(*self.extra.get()).registered_userdata_mt.insert(id);
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn deregister_userdata_metatable(&self, id: isize) {
|
||||
(*self.extra).registered_userdata_mt.remove(&id);
|
||||
(*self.extra.get()).registered_userdata_mt.remove(&id);
|
||||
}
|
||||
|
||||
// Pushes a LuaRef value onto the stack, checking that it's a registered
|
||||
@@ -1798,7 +1841,7 @@ impl Lua {
|
||||
}
|
||||
// Check that userdata is registered
|
||||
let ptr = ffi::lua_topointer(self.state, -1);
|
||||
let extra = &*self.extra;
|
||||
let extra = &*self.extra.get();
|
||||
if extra.registered_userdata_mt.contains(&(ptr as isize)) {
|
||||
if !with_mt {
|
||||
ffi::lua_pop(self.state, 1);
|
||||
@@ -1833,7 +1876,7 @@ impl Lua {
|
||||
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
|
||||
let get_extra = |state| {
|
||||
let upvalue = get_userdata::<CallbackUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
(*upvalue).lua.extra
|
||||
(*upvalue).lua.extra.get()
|
||||
};
|
||||
callback_error_ext(state, get_extra, |nargs| {
|
||||
let upvalue_idx = ffi::lua_upvalueindex(1);
|
||||
@@ -1891,8 +1934,8 @@ impl Lua {
|
||||
'lua: 'callback,
|
||||
{
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||
{
|
||||
let libs = unsafe { (*self.extra).libs };
|
||||
unsafe {
|
||||
let libs = (*self.extra.get()).libs;
|
||||
if !libs.contains(StdLib::COROUTINE) {
|
||||
self.load_from_std_lib(StdLib::COROUTINE)?;
|
||||
}
|
||||
@@ -1901,7 +1944,7 @@ impl Lua {
|
||||
unsafe extern "C" fn call_callback(state: *mut ffi::lua_State) -> c_int {
|
||||
let get_extra = |state| {
|
||||
let upvalue = get_userdata::<AsyncCallbackUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
(*upvalue).lua.extra
|
||||
(*upvalue).lua.extra.get()
|
||||
};
|
||||
callback_error_ext(state, get_extra, |nargs| {
|
||||
let upvalue_idx = ffi::lua_upvalueindex(1);
|
||||
@@ -1937,7 +1980,7 @@ impl Lua {
|
||||
unsafe extern "C" fn poll_future(state: *mut ffi::lua_State) -> c_int {
|
||||
let get_extra = |state| {
|
||||
let upvalue = get_userdata::<AsyncPollUpvalue>(state, ffi::lua_upvalueindex(1));
|
||||
(*upvalue).lua.extra
|
||||
(*upvalue).lua.extra.get()
|
||||
};
|
||||
callback_error_ext(state, get_extra, |nargs| {
|
||||
let upvalue_idx = ffi::lua_upvalueindex(1);
|
||||
@@ -2056,7 +2099,7 @@ impl Lua {
|
||||
Lua {
|
||||
state: self.state,
|
||||
main_state: self.main_state,
|
||||
extra: self.extra,
|
||||
extra: Arc::clone(&self.extra),
|
||||
ephemeral: true,
|
||||
safe: self.safe,
|
||||
_no_ref_unwind_safe: PhantomData,
|
||||
@@ -2094,24 +2137,26 @@ impl Lua {
|
||||
assert_stack(state, 1);
|
||||
|
||||
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key) != ffi::LUA_TLIGHTUSERDATA {
|
||||
if ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, extra_key) != ffi::LUA_TUSERDATA {
|
||||
return None;
|
||||
}
|
||||
let extra = ffi::lua_touserdata(state, -1) as *mut ExtraData;
|
||||
let extra_ptr = ffi::lua_touserdata(state, -1) as *mut Arc<UnsafeCell<ExtraData>>;
|
||||
let extra = Arc::clone(&*extra_ptr);
|
||||
ffi::lua_pop(state, 1);
|
||||
|
||||
let safe = (*extra.get()).safe;
|
||||
Some(Lua {
|
||||
state,
|
||||
main_state: get_main_state(state),
|
||||
extra,
|
||||
ephemeral: true,
|
||||
safe: (*extra).safe,
|
||||
safe,
|
||||
_no_ref_unwind_safe: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn hook_callback(&self) -> Option<HookCallback> {
|
||||
(*self.extra).hook_callback.clone()
|
||||
(*self.extra.get()).hook_callback.clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -17,19 +17,19 @@ macro_rules! cstr {
|
||||
|
||||
macro_rules! mlua_panic {
|
||||
($msg:expr) => {
|
||||
panic!(bug_msg!($msg));
|
||||
panic!(bug_msg!($msg))
|
||||
};
|
||||
|
||||
($msg:expr,) => {
|
||||
mlua_panic!($msg);
|
||||
mlua_panic!($msg)
|
||||
};
|
||||
|
||||
($msg:expr, $($arg:expr),+) => {
|
||||
panic!(bug_msg!($msg), $($arg),+);
|
||||
panic!(bug_msg!($msg), $($arg),+)
|
||||
};
|
||||
|
||||
($msg:expr, $($arg:expr),+,) => {
|
||||
mlua_panic!($msg, $($arg),+);
|
||||
mlua_panic!($msg, $($arg),+)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -18,4 +18,7 @@ pub use crate::AsyncThread as LuaAsyncThread;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
#[doc(inline)]
|
||||
pub use crate::{LuaSerdeExt, SerializeOptions as LuaSerializeOptions};
|
||||
pub use crate::{
|
||||
DeserializeOptions as LuaDeserializeOptions, LuaSerdeExt,
|
||||
SerializeOptions as LuaSerializeOptions,
|
||||
};
|
||||
|
||||
+233
-44
@@ -1,3 +1,7 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashSet;
|
||||
use std::os::raw::c_void;
|
||||
use std::rc::Rc;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use serde::de::{self, IntoDeserializer};
|
||||
@@ -8,12 +12,88 @@ use crate::value::Value;
|
||||
|
||||
/// A struct for deserializing Lua values into Rust values.
|
||||
#[derive(Debug)]
|
||||
pub struct Deserializer<'lua>(Value<'lua>);
|
||||
pub struct Deserializer<'lua> {
|
||||
value: Value<'lua>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
/// A struct with options to change default deserializer behavior.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[non_exhaustive]
|
||||
pub struct Options {
|
||||
/// If true, an attempt to serialize types such as `Thread`, `UserData`, `LightUserData`
|
||||
/// and `Error` will cause an error.
|
||||
/// Otherwise these types skipped when iterating or serialized as unit type.
|
||||
///
|
||||
/// Default: **true**
|
||||
pub deny_unsupported_types: bool,
|
||||
|
||||
/// If true, an attempt to serialize a recursive table (table that refers to itself)
|
||||
/// will cause an error.
|
||||
/// Otherwise subsequent attempts to serialize the same table will be ignored.
|
||||
///
|
||||
/// Default: **true**
|
||||
pub deny_recursive_tables: bool,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Options {
|
||||
deny_unsupported_types: true,
|
||||
deny_recursive_tables: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Options {
|
||||
/// Returns a new instance of `Options` with default parameters.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Sets [`deny_unsupported_types`] option.
|
||||
///
|
||||
/// [`deny_unsupported_types`]: #structfield.deny_unsupported_types
|
||||
pub fn deny_unsupported_types(mut self, enabled: bool) -> Self {
|
||||
self.deny_unsupported_types = enabled;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets [`deny_recursive_tables`] option.
|
||||
///
|
||||
/// [`deny_recursive_tables`]: #structfield.deny_recursive_tables
|
||||
pub fn deny_recursive_tables(mut self, enabled: bool) -> Self {
|
||||
self.deny_recursive_tables = enabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua> Deserializer<'lua> {
|
||||
/// Creates a new Lua Deserializer for the `Value`.
|
||||
pub fn new(value: Value<'lua>) -> Self {
|
||||
Deserializer(value)
|
||||
Self::new_with_options(value, Options::default())
|
||||
}
|
||||
|
||||
/// Creates a new Lua Deserializer for the `Value` with custom options.
|
||||
pub fn new_with_options(value: Value<'lua>, options: Options) -> Self {
|
||||
Deserializer {
|
||||
value,
|
||||
options,
|
||||
visited: Rc::new(RefCell::new(HashSet::new())),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_parts(
|
||||
value: Value<'lua>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
) -> Self {
|
||||
Deserializer {
|
||||
value,
|
||||
options,
|
||||
visited,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +105,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.0 {
|
||||
match self.value {
|
||||
Value::Nil => visitor.visit_unit(),
|
||||
Value::Boolean(b) => visitor.visit_bool(b),
|
||||
#[allow(clippy::useless_conversion)]
|
||||
@@ -43,7 +123,16 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
| Value::LightUserData(_)
|
||||
| Value::Error(_) => Err(de::Error::custom("invalid value type")),
|
||||
| Value::Error(_) => {
|
||||
if self.options.deny_unsupported_types {
|
||||
Err(de::Error::custom(format!(
|
||||
"unsupported value type `{}`",
|
||||
self.value.type_name()
|
||||
)))
|
||||
} else {
|
||||
visitor.visit_unit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +141,7 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.0 {
|
||||
match self.value {
|
||||
Value::Nil => visitor.visit_none(),
|
||||
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_none(),
|
||||
_ => visitor.visit_some(self),
|
||||
@@ -69,9 +158,13 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
let (variant, value) = match self.0 {
|
||||
Value::Table(value) => {
|
||||
let mut iter = value.pairs::<StdString, Value>();
|
||||
let (variant, value) = 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 mut iter = table.pairs::<StdString, Value>();
|
||||
let (variant, value) = match iter.next() {
|
||||
Some(v) => v?,
|
||||
None => {
|
||||
@@ -88,13 +181,22 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
&"map with a single key",
|
||||
));
|
||||
}
|
||||
if check_value_if_skip(&value, self.options, &self.visited)? {
|
||||
return Err(de::Error::custom("bad enum value"));
|
||||
}
|
||||
|
||||
(variant, Some(value))
|
||||
}
|
||||
Value::String(variant) => (variant.to_str()?.to_owned(), None),
|
||||
_ => return Err(de::Error::custom("bad enum value")),
|
||||
};
|
||||
|
||||
visitor.visit_enum(EnumDeserializer { variant, value })
|
||||
visitor.visit_enum(EnumDeserializer {
|
||||
variant,
|
||||
value,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -102,12 +204,20 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.0 {
|
||||
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 len = t.raw_len() as usize;
|
||||
let mut deserializer = SeqDeserializer(t.raw_sequence_values());
|
||||
let mut deserializer = SeqDeserializer {
|
||||
seq: t.raw_sequence_values(),
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
};
|
||||
let seq = visitor.visit_seq(&mut deserializer)?;
|
||||
if deserializer.0.count() == 0 {
|
||||
if deserializer.seq.count() == 0 {
|
||||
Ok(seq)
|
||||
} else {
|
||||
Err(de::Error::invalid_length(
|
||||
@@ -116,7 +226,10 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(de::Error::custom("invalid value type")),
|
||||
value => Err(de::Error::invalid_type(
|
||||
de::Unexpected::Other(value.type_name()),
|
||||
&"table",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,9 +259,19 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.0 {
|
||||
match self.value {
|
||||
Value::Table(t) => {
|
||||
let mut deserializer = MapDeserializer::new(t.pairs());
|
||||
let lua = t.0.lua;
|
||||
let ptr = unsafe { lua.get_ref_ptr(&t.0) };
|
||||
self.visited.borrow_mut().insert(ptr);
|
||||
|
||||
let mut deserializer = MapDeserializer {
|
||||
pairs: t.pairs(),
|
||||
value: None,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
processed: 0,
|
||||
};
|
||||
let map = visitor.visit_map(&mut deserializer)?;
|
||||
let count = deserializer.pairs.count();
|
||||
if count == 0 {
|
||||
@@ -160,7 +283,10 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(de::Error::custom("invalid value type")),
|
||||
value => Err(de::Error::invalid_type(
|
||||
de::Unexpected::Other(value.type_name()),
|
||||
&"table",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +310,11 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
|
||||
}
|
||||
}
|
||||
|
||||
struct SeqDeserializer<'lua>(TableSequence<'lua, Value<'lua>>);
|
||||
struct SeqDeserializer<'lua> {
|
||||
seq: TableSequence<'lua, Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
type Error = Error;
|
||||
@@ -193,14 +323,24 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.0.next() {
|
||||
Some(value) => seed.deserialize(Deserializer(value?)).map(Some),
|
||||
None => Ok(None),
|
||||
loop {
|
||||
match self.seq.next() {
|
||||
Some(value) => {
|
||||
let value = value?;
|
||||
if check_value_if_skip(&value, self.options, &self.visited)? {
|
||||
continue;
|
||||
}
|
||||
let visited = Rc::clone(&self.visited);
|
||||
let deserializer = Deserializer::from_parts(value, self.options, visited);
|
||||
return seed.deserialize(deserializer).map(Some);
|
||||
}
|
||||
None => return Ok(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
match self.0.size_hint() {
|
||||
match self.seq.size_hint() {
|
||||
(lower, Some(upper)) if lower == upper => Some(upper),
|
||||
_ => None,
|
||||
}
|
||||
@@ -210,19 +350,11 @@ impl<'lua, 'de> de::SeqAccess<'de> for SeqDeserializer<'lua> {
|
||||
struct MapDeserializer<'lua> {
|
||||
pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>,
|
||||
value: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
processed: usize,
|
||||
}
|
||||
|
||||
impl<'lua> MapDeserializer<'lua> {
|
||||
fn new(pairs: TablePairs<'lua, Value<'lua>, Value<'lua>>) -> Self {
|
||||
MapDeserializer {
|
||||
pairs,
|
||||
value: None,
|
||||
processed: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
type Error = Error;
|
||||
|
||||
@@ -230,15 +362,23 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
where
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.pairs.next() {
|
||||
Some(item) => {
|
||||
let (key, value) = item?;
|
||||
self.processed += 1;
|
||||
self.value = Some(value);
|
||||
let key_de = Deserializer(key);
|
||||
seed.deserialize(key_de).map(Some)
|
||||
loop {
|
||||
match self.pairs.next() {
|
||||
Some(item) => {
|
||||
let (key, value) = item?;
|
||||
if check_value_if_skip(&key, self.options, &self.visited)?
|
||||
|| check_value_if_skip(&value, self.options, &self.visited)?
|
||||
{
|
||||
continue;
|
||||
}
|
||||
self.processed += 1;
|
||||
self.value = Some(value);
|
||||
let visited = Rc::clone(&self.visited);
|
||||
let key_de = Deserializer::from_parts(key, self.options, visited);
|
||||
return seed.deserialize(key_de).map(Some);
|
||||
}
|
||||
None => return Ok(None),
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +387,10 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.value.take() {
|
||||
Some(value) => seed.deserialize(Deserializer(value)),
|
||||
Some(value) => {
|
||||
let visited = Rc::clone(&self.visited);
|
||||
seed.deserialize(Deserializer::from_parts(value, self.options, visited))
|
||||
}
|
||||
None => Err(de::Error::custom("value is missing")),
|
||||
}
|
||||
}
|
||||
@@ -263,6 +406,8 @@ impl<'lua, 'de> de::MapAccess<'de> for MapDeserializer<'lua> {
|
||||
struct EnumDeserializer<'lua> {
|
||||
variant: StdString,
|
||||
value: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
|
||||
@@ -274,13 +419,19 @@ impl<'lua, 'de> de::EnumAccess<'de> for EnumDeserializer<'lua> {
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
let variant = self.variant.into_deserializer();
|
||||
let variant_access = VariantDeserializer { value: self.value };
|
||||
let variant_access = VariantDeserializer {
|
||||
value: self.value,
|
||||
options: self.options,
|
||||
visited: self.visited,
|
||||
};
|
||||
seed.deserialize(variant).map(|v| (v, variant_access))
|
||||
}
|
||||
}
|
||||
|
||||
struct VariantDeserializer<'lua> {
|
||||
value: Option<Value<'lua>>,
|
||||
options: Options,
|
||||
visited: Rc<RefCell<HashSet<*const c_void>>>,
|
||||
}
|
||||
|
||||
impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
@@ -301,7 +452,9 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Some(value) => seed.deserialize(Deserializer(value)),
|
||||
Some(value) => {
|
||||
seed.deserialize(Deserializer::from_parts(value, self.options, self.visited))
|
||||
}
|
||||
None => Err(de::Error::invalid_type(
|
||||
de::Unexpected::UnitVariant,
|
||||
&"newtype variant",
|
||||
@@ -314,7 +467,10 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Some(value) => serde::Deserializer::deserialize_seq(Deserializer(value), visitor),
|
||||
Some(value) => serde::Deserializer::deserialize_seq(
|
||||
Deserializer::from_parts(value, self.options, self.visited),
|
||||
visitor,
|
||||
),
|
||||
None => Err(de::Error::invalid_type(
|
||||
de::Unexpected::UnitVariant,
|
||||
&"tuple variant",
|
||||
@@ -327,7 +483,10 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
match self.value {
|
||||
Some(value) => serde::Deserializer::deserialize_map(Deserializer(value), visitor),
|
||||
Some(value) => serde::Deserializer::deserialize_map(
|
||||
Deserializer::from_parts(value, self.options, self.visited),
|
||||
visitor,
|
||||
),
|
||||
None => Err(de::Error::invalid_type(
|
||||
de::Unexpected::UnitVariant,
|
||||
&"struct variant",
|
||||
@@ -335,3 +494,33 @@ impl<'lua, 'de> de::VariantAccess<'de> for VariantDeserializer<'lua> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_value_if_skip(
|
||||
value: &Value,
|
||||
options: Options,
|
||||
visited: &Rc<RefCell<HashSet<*const c_void>>>,
|
||||
) -> Result<bool> {
|
||||
match value {
|
||||
Value::Table(table) => {
|
||||
let lua = table.0.lua;
|
||||
let ptr = unsafe { lua.get_ref_ptr(&table.0) };
|
||||
if visited.borrow().contains(&ptr) {
|
||||
if options.deny_recursive_tables {
|
||||
return Err(de::Error::custom("recursive table detected"));
|
||||
}
|
||||
return Ok(true); // skip
|
||||
}
|
||||
}
|
||||
Value::Function(_)
|
||||
| Value::Thread(_)
|
||||
| Value::UserData(_)
|
||||
| Value::LightUserData(_)
|
||||
| Value::Error(_)
|
||||
if !options.deny_unsupported_types =>
|
||||
{
|
||||
return Ok(true); // skip
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(false) // do not skip
|
||||
}
|
||||
|
||||
@@ -158,6 +158,41 @@ pub trait LuaSerdeExt<'lua> {
|
||||
/// }
|
||||
/// ```
|
||||
fn from_value<T: Deserialize<'lua>>(&'lua self, value: Value<'lua>) -> Result<T>;
|
||||
|
||||
/// Deserializes a `Value` into any serde deserializable object with options.
|
||||
///
|
||||
/// Requires `feature = "serialize"`
|
||||
///
|
||||
/// [`Value`]: enum.Value.html
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use mlua::{Lua, Result, LuaSerdeExt, DeserializeOptions};
|
||||
/// use serde::Deserialize;
|
||||
///
|
||||
/// #[derive(Deserialize, Debug, PartialEq)]
|
||||
/// struct User {
|
||||
/// name: String,
|
||||
/// age: u8,
|
||||
/// }
|
||||
///
|
||||
/// fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let val = lua.load(r#"{name = "John Smith", age = 20, f = function() end}"#).eval()?;
|
||||
/// let options = DeserializeOptions::new().deny_unsupported_types(false);
|
||||
/// let u: User = lua.from_value_with(val, options)?;
|
||||
///
|
||||
/// assert_eq!(u, User { name: "John Smith".into(), age: 20 });
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn from_value_with<T: Deserialize<'lua>>(
|
||||
&'lua self,
|
||||
value: Value<'lua>,
|
||||
options: de::Options,
|
||||
) -> Result<T>;
|
||||
}
|
||||
|
||||
impl<'lua> LuaSerdeExt<'lua> for Lua {
|
||||
@@ -196,6 +231,13 @@ impl<'lua> LuaSerdeExt<'lua> for Lua {
|
||||
{
|
||||
T::deserialize(de::Deserializer::new(value))
|
||||
}
|
||||
|
||||
fn from_value_with<T>(&'lua self, value: Value<'lua>, options: de::Options) -> Result<T>
|
||||
where
|
||||
T: Deserialize<'lua>,
|
||||
{
|
||||
T::deserialize(de::Deserializer::new_with_options(value, options))
|
||||
}
|
||||
}
|
||||
|
||||
// Uses 6 stack spaces and calls checkstack.
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ impl Default for Options {
|
||||
}
|
||||
|
||||
impl Options {
|
||||
/// Retruns a new instance of `Options` with default parameters.
|
||||
/// Returns a new instance of `Options` with default parameters.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
+4
-2
@@ -520,8 +520,10 @@ pub trait UserDataFields<'lua, T: UserData> {
|
||||
|
||||
/// Trait for custom userdata types.
|
||||
///
|
||||
/// By implementing this trait, a struct becomes eligible for use inside Lua code. Implementations
|
||||
/// of [`ToLua`] and [`FromLua`] are automatically provided.
|
||||
/// By implementing this trait, a struct becomes eligible for use inside Lua code.
|
||||
/// Implementation of [`ToLua`] is automatically provided, [`FromLua`] is implemented
|
||||
/// only for `T: UserData + Clone`.
|
||||
///
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
||||
+4
-5
@@ -450,11 +450,10 @@ pub unsafe fn init_userdata_metatable<T>(
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
||||
callback_error(state, |_| {
|
||||
check_stack(state, 1)?;
|
||||
take_userdata::<T>(state);
|
||||
Ok(0)
|
||||
})
|
||||
// It's probably NOT a good idea to catch Rust panics in finalizer
|
||||
// Lua 5.4 ignores it, other versions generates `LUA_ERRGCMM` without calling message handler
|
||||
take_userdata::<T>(state);
|
||||
0
|
||||
}
|
||||
|
||||
// In the context of a lua callback, this will call the given function and if the given function
|
||||
|
||||
@@ -4,7 +4,7 @@ error[E0373]: closure may outlive the current function, but it borrows `test`, w
|
||||
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `test`
|
||||
10 | Ok(test.0)
|
||||
| ---- `test` is borrowed here
|
||||
| ------ `test` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'static`
|
||||
--> $DIR/function_borrow.rs:9:13
|
||||
|
||||
@@ -15,49 +15,21 @@ error[E0277]: the type `UnsafeCell<()>` may contain interior mutability and a re
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48]`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/lua_norefunwindsafe.rs:7:5
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
::: $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ---------- required by this bound in `catch_unwind`
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>`
|
||||
= note: required because it appears within the type `RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>`
|
||||
= note: required because it appears within the type `alloc::sync::ArcInner<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>`
|
||||
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>>`
|
||||
= note: required because it appears within the type `Arc<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>`
|
||||
= note: required because it appears within the type `Option<Arc<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>>`
|
||||
= note: required because it appears within the type `mlua::lua::ExtraData`
|
||||
= note: required because it appears within the type `*mut mlua::lua::ExtraData`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48]`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/lua_norefunwindsafe.rs:7:5
|
||||
|
|
||||
7 | catch_unwind(|| lua.create_table().unwrap());
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
::: $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ---------- required by this bound in `catch_unwind`
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<isize>`
|
||||
= note: required because it appears within the type `Cell<isize>`
|
||||
= note: required because it appears within the type `RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>`
|
||||
= note: required because it appears within the type `alloc::sync::ArcInner<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>`
|
||||
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>>`
|
||||
= note: required because it appears within the type `Arc<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>`
|
||||
= note: required because it appears within the type `Option<Arc<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>>`
|
||||
= note: required because it appears within the type `mlua::lua::ExtraData`
|
||||
= note: required because it appears within the type `*mut mlua::lua::ExtraData`
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
|
||||
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::ExtraData>>>`
|
||||
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:48]`
|
||||
|
||||
@@ -17,51 +17,21 @@ error[E0277]: the type `UnsafeCell<()>` may contain interior mutability and a re
|
||||
= note: required because it appears within the type `LuaTable<'_>`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:54]`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
error[E0277]: the type `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/ref_nounwindsafe.rs:8:5
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<mlua::lua::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
::: $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ---------- required by this bound in `catch_unwind`
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>`
|
||||
= note: required because it appears within the type `RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>`
|
||||
= note: required because it appears within the type `alloc::sync::ArcInner<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>`
|
||||
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>>`
|
||||
= note: required because it appears within the type `Arc<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>`
|
||||
= note: required because it appears within the type `Option<Arc<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>>`
|
||||
= note: required because it appears within the type `mlua::lua::ExtraData`
|
||||
= note: required because it appears within the type `*mut mlua::lua::ExtraData`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
||||
= note: required because it appears within the type `LuaTable<'_>`
|
||||
= note: required because it appears within the type `[closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:54]`
|
||||
|
||||
error[E0277]: the type `UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
--> $DIR/ref_nounwindsafe.rs:8:5
|
||||
|
|
||||
8 | catch_unwind(move || table.set("a", "b").unwrap());
|
||||
| ^^^^^^^^^^^^ `UnsafeCell<isize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
|
||||
|
|
||||
::: $RUST/std/src/panic.rs
|
||||
|
|
||||
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
|
||||
| ---------- required by this bound in `catch_unwind`
|
||||
|
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<isize>`
|
||||
= note: required because it appears within the type `Cell<isize>`
|
||||
= note: required because it appears within the type `RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>`
|
||||
= note: required because it appears within the type `alloc::sync::ArcInner<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>`
|
||||
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>>`
|
||||
= note: required because it appears within the type `Arc<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>`
|
||||
= note: required because it appears within the type `Option<Arc<RefCell<(dyn for<'r, 's> FnMut(&'r Lua, mlua::Debug<'s>) -> Result<(), LuaError> + 'static)>>>`
|
||||
= note: required because it appears within the type `mlua::lua::ExtraData`
|
||||
= note: required because it appears within the type `*mut mlua::lua::ExtraData`
|
||||
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::lua::ExtraData>`
|
||||
= note: required because it appears within the type `alloc::sync::ArcInner<UnsafeCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<UnsafeCell<mlua::lua::ExtraData>>>`
|
||||
= note: required because it appears within the type `Arc<UnsafeCell<mlua::lua::ExtraData>>`
|
||||
= note: required because it appears within the type `Lua`
|
||||
= note: required because of the requirements on the impl of `UnwindSafe` for `&Lua`
|
||||
= note: required because it appears within the type `mlua::types::LuaRef<'_>`
|
||||
|
||||
@@ -7,7 +7,7 @@ error[E0373]: closure may outlive the current function, but it borrows `test`, w
|
||||
14 | .create_function_mut(|_, ()| {
|
||||
| ^^^^^^^ may outlive borrowed value `test`
|
||||
15 | test.field = 42;
|
||||
| ---- `test` is borrowed here
|
||||
| ---------- `test` is borrowed here
|
||||
|
|
||||
note: function requires argument type to outlive `'1`
|
||||
--> $DIR/scope_invariance.rs:13:13
|
||||
|
||||
+48
-64
@@ -3,7 +3,20 @@ use std::ops::Deref;
|
||||
use std::str;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use mlua::{Error, HookTriggers, Lua, Result, Value};
|
||||
use mlua::{DebugEvent, Error, HookTriggers, Lua, Result, Value};
|
||||
|
||||
#[test]
|
||||
fn test_hook_triggers_bitor() {
|
||||
let trigger = HookTriggers::on_calls()
|
||||
| HookTriggers::on_returns()
|
||||
| HookTriggers::every_line()
|
||||
| HookTriggers::every_nth_instruction(5);
|
||||
|
||||
assert!(trigger.on_calls);
|
||||
assert!(trigger.on_returns);
|
||||
assert!(trigger.every_line);
|
||||
assert_eq!(trigger.every_nth_instruction, Some(5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_line_counts() -> Result<()> {
|
||||
@@ -11,16 +24,11 @@ fn test_line_counts() -> Result<()> {
|
||||
let hook_output = output.clone();
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.set_hook(
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
},
|
||||
move |_lua, debug| {
|
||||
hook_output.lock().unwrap().push(debug.curr_line());
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
lua.set_hook(HookTriggers::every_line(), move |_lua, debug| {
|
||||
assert_eq!(debug.event(), DebugEvent::Line);
|
||||
hook_output.lock().unwrap().push(debug.curr_line());
|
||||
Ok(())
|
||||
})?;
|
||||
lua.load(
|
||||
r#"
|
||||
local x = 2 + 3
|
||||
@@ -48,20 +56,15 @@ fn test_function_calls() -> Result<()> {
|
||||
let hook_output = output.clone();
|
||||
|
||||
let lua = Lua::new();
|
||||
lua.set_hook(
|
||||
HookTriggers {
|
||||
on_calls: true,
|
||||
..Default::default()
|
||||
},
|
||||
move |_lua, debug| {
|
||||
let names = debug.names();
|
||||
let source = debug.source();
|
||||
let name = names.name.map(|s| str::from_utf8(s).unwrap().to_owned());
|
||||
let what = source.what.map(|s| str::from_utf8(s).unwrap().to_owned());
|
||||
hook_output.lock().unwrap().push((name, what));
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
lua.set_hook(HookTriggers::on_calls(), move |_lua, debug| {
|
||||
assert_eq!(debug.event(), DebugEvent::Call);
|
||||
let names = debug.names();
|
||||
let source = debug.source();
|
||||
let name = names.name.map(|s| str::from_utf8(s).unwrap().to_owned());
|
||||
let what = source.what.map(|s| str::from_utf8(s).unwrap().to_owned());
|
||||
hook_output.lock().unwrap().push((name, what));
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
lua.load(
|
||||
r#"
|
||||
@@ -97,17 +100,12 @@ fn test_function_calls() -> Result<()> {
|
||||
#[test]
|
||||
fn test_error_within_hook() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
lua.set_hook(
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
},
|
||||
|_lua, _debug| {
|
||||
Err(Error::RuntimeError(
|
||||
"Something happened in there!".to_string(),
|
||||
))
|
||||
},
|
||||
)?;
|
||||
|
||||
lua.set_hook(HookTriggers::every_line(), |_lua, _debug| {
|
||||
Err(Error::RuntimeError(
|
||||
"Something happened in there!".to_string(),
|
||||
))
|
||||
})?;
|
||||
|
||||
let err = lua
|
||||
.load("x = 1")
|
||||
@@ -135,11 +133,9 @@ fn test_limit_execution_instructions() -> Result<()> {
|
||||
lua.load("jit.off()").exec()?;
|
||||
|
||||
lua.set_hook(
|
||||
HookTriggers {
|
||||
every_nth_instruction: Some(30),
|
||||
..Default::default()
|
||||
},
|
||||
move |_lua, _debug| {
|
||||
HookTriggers::every_nth_instruction(30),
|
||||
move |_lua, debug| {
|
||||
assert_eq!(debug.event(), DebugEvent::Count);
|
||||
max_instructions -= 30;
|
||||
if max_instructions < 0 {
|
||||
Err(Error::RuntimeError("time's up".to_string()))
|
||||
@@ -168,17 +164,11 @@ fn test_limit_execution_instructions() -> Result<()> {
|
||||
fn test_hook_removal() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.set_hook(
|
||||
HookTriggers {
|
||||
every_nth_instruction: Some(1),
|
||||
..Default::default()
|
||||
},
|
||||
|_lua, _debug| {
|
||||
Err(Error::RuntimeError(
|
||||
"this hook should've been removed by this time".to_string(),
|
||||
))
|
||||
},
|
||||
)?;
|
||||
lua.set_hook(HookTriggers::every_nth_instruction(1), |_lua, _debug| {
|
||||
Err(Error::RuntimeError(
|
||||
"this hook should've been removed by this time".to_string(),
|
||||
))
|
||||
})?;
|
||||
|
||||
assert!(lua.load("local x = 1").exec().is_err());
|
||||
lua.remove_hook();
|
||||
@@ -198,19 +188,14 @@ fn test_hook_swap_within_hook() -> Result<()> {
|
||||
});
|
||||
|
||||
TL_LUA.with(|tl| {
|
||||
tl.borrow().as_ref().unwrap().set_hook(
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
},
|
||||
move |lua, _debug| {
|
||||
tl.borrow()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.set_hook(HookTriggers::every_line(), move |lua, _debug| {
|
||||
lua.globals().set("ok", 1i64)?;
|
||||
TL_LUA.with(|tl| {
|
||||
tl.borrow().as_ref().unwrap().set_hook(
|
||||
HookTriggers {
|
||||
every_line: true,
|
||||
..Default::default()
|
||||
},
|
||||
HookTriggers::every_line(),
|
||||
move |lua, _debug| {
|
||||
lua.load(
|
||||
r#"
|
||||
@@ -228,8 +213,7 @@ fn test_hook_swap_within_hook() -> Result<()> {
|
||||
},
|
||||
)
|
||||
})
|
||||
},
|
||||
)
|
||||
})
|
||||
})?;
|
||||
|
||||
TL_LUA.with(|tl| {
|
||||
|
||||
+57
-2
@@ -2,7 +2,10 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use mlua::{Error, Lua, LuaSerdeExt, Result as LuaResult, SerializeOptions, UserData, Value};
|
||||
use mlua::{
|
||||
DeserializeOptions, Error, Lua, LuaSerdeExt, Result as LuaResult, SerializeOptions, UserData,
|
||||
Value,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[test]
|
||||
@@ -310,7 +313,7 @@ fn test_from_value_struct() -> Result<(), Box<dyn std::error::Error>> {
|
||||
struct Test {
|
||||
int: u32,
|
||||
seq: Vec<String>,
|
||||
map: std::collections::HashMap<i32, i32>,
|
||||
map: HashMap<i32, i32>,
|
||||
empty: Vec<()>,
|
||||
tuple: (u8, u8, u8),
|
||||
}
|
||||
@@ -413,3 +416,55 @@ fn test_from_value_enum_untagged() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_value_with_options() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// Deny unsupported types by default
|
||||
let value = Value::Function(lua.create_function(|_, ()| Ok(()))?);
|
||||
match lua.from_value::<Option<String>>(value) {
|
||||
Ok(v) => panic!("expected deserialization error, got {:?}", v),
|
||||
Err(Error::DeserializeError(err)) => {
|
||||
assert!(err.contains("unsupported value type"))
|
||||
}
|
||||
Err(err) => panic!("expected `DeserializeError` error, got {:?}", err),
|
||||
};
|
||||
|
||||
// Allow unsupported types
|
||||
let value = Value::Function(lua.create_function(|_, ()| Ok(()))?);
|
||||
let options = DeserializeOptions::new().deny_unsupported_types(false);
|
||||
assert_eq!(lua.from_value_with::<()>(value, options)?, ());
|
||||
|
||||
// Allow unsupported types (in a table seq)
|
||||
let value = lua.load(r#"{"a", "b", function() end, "c"}"#).eval()?;
|
||||
let options = DeserializeOptions::new().deny_unsupported_types(false);
|
||||
assert_eq!(
|
||||
lua.from_value_with::<Vec<String>>(value, options)?,
|
||||
vec!["a".to_string(), "b".to_string(), "c".to_string()]
|
||||
);
|
||||
|
||||
// Deny recursive tables by default
|
||||
let value = lua.load(r#"local t = {}; t.t = t; return t"#).eval()?;
|
||||
match lua.from_value::<HashMap<String, Option<String>>>(value) {
|
||||
Ok(v) => panic!("expected deserialization error, got {:?}", v),
|
||||
Err(Error::DeserializeError(err)) => {
|
||||
assert!(err.contains("recursive table detected"))
|
||||
}
|
||||
Err(err) => panic!("expected `DeserializeError` error, got {:?}", err),
|
||||
};
|
||||
|
||||
// Serialize Lua globals table
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Globals {
|
||||
hello: String,
|
||||
}
|
||||
let options = DeserializeOptions::new()
|
||||
.deny_unsupported_types(false)
|
||||
.deny_recursive_tables(false);
|
||||
lua.load(r#"hello = "world""#).exec()?;
|
||||
let globals: Globals = lua.from_value_with(Value::Table(lua.globals()), options)?;
|
||||
assert_eq!(globals.hello, "world");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user