Compare commits

..

25 Commits

Author SHA1 Message Date
Alex Orlenko 584b377640 v0.6.4 2021-09-28 18:53:47 +01:00
Alex Orlenko 1141073a65 Update CHANGELOG 2021-09-28 18:51:42 +01:00
Alex Orlenko 5b1483bd56 Change syntax of protect_lua macro 2021-09-28 18:47:08 +01:00
Alex Orlenko a74b637ed4 Update check_stack requirements 2021-09-28 18:41:25 +01:00
Alex Orlenko 7623016d4a Fix doc feature labels 2021-09-28 18:15:53 +01:00
Alex Orlenko bdd3c923ba Fix table traversal used in recursion detection.
This fixes serializing same table multiple times within a parent table.
2021-09-28 16:41:39 +01:00
Alex Orlenko d586eef0f5 Refactor UserData metatables handling 2021-09-28 16:33:36 +01:00
Alex Orlenko 01154c0616 Minor refacor of wrapped failures cache 2021-09-28 16:27:24 +01:00
Alex Orlenko e42d67c70d Make protect_lua as a smart macro to choose from C/closure 2021-09-28 16:26:30 +01:00
Alex Orlenko 771a7775c5 Perf optimization: refactor metatable cache 2021-09-28 16:25:08 +01:00
Alex Orlenko ee1c8a1a3d Add inline attributes to few hot funcs 2021-09-28 16:19:29 +01:00
Alex Orlenko 3597e34ffb Fix internal HookCallback type (missing optional Send) 2021-09-28 16:19:12 +01:00
Alex Orlenko 35eedd5a5e v0.6.3 2021-09-16 00:55:49 +01:00
Alex Orlenko 53f873a482 Update compile tests error messages 2021-09-16 00:49:17 +01:00
Alex Orlenko fc1fe2c15e Add DeserializeOptions struct to control deserializer behavior.
This solves #74 and provides a way to deserialize a Lua globals table.
2021-09-15 23:45:08 +01:00
Alex Orlenko 7e7a44f4cd Update CHANGELOG 2021-08-22 16:31:14 +01:00
Alex Orlenko 63c4861520 Create FUNDING.yml 2021-08-22 16:22:20 +01:00
Alex Orlenko 27e7facf9b Fix clippy warnings 2021-08-22 00:35:31 +01:00
Alex Orlenko 31d32f2dda Wrap ExtraData to Arc<UnsafeCell>> instead of raw pointer and attach finalizer.
This would allow to properly deallocate memory in module mode when closing lua state.
2021-08-21 23:17:09 +01:00
Alex Orlenko 7d1b322e18 Change ExtraData::mem_info to Box<MemoryInfo> 2021-08-19 01:42:32 +01:00
Alex Orlenko d906405818 Simplify interface of hook::HookTriggers 2021-08-18 18:49:17 +01:00
Alex Orlenko 60fd060d47 Clarify about calling Lua::init_from_ptr() multiple times 2021-08-17 15:34:34 +01:00
Alex Orlenko 9f02a9ca09 Add Debug::event() to the hook's Debug structure 2021-08-17 15:17:03 +01:00
Alex Orlenko 1d7f105585 Don't catch Rust panics in userdata finalizer on drop 2021-08-06 11:14:16 +01:00
Alex Orlenko 1020315a9b Update documentation about FromLua for UserData. Closes #64 2021-08-04 12:12:02 +01:00
26 changed files with 1076 additions and 537 deletions
+1
View File
@@ -0,0 +1 @@
github: khvzak
+13
View File
@@ -1,3 +1,16 @@
## 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.
- 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
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua"
version = "0.6.2" # remember to update html_root_url and mlua_derive
version = "0.6.4" # remember to update html_root_url and mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
edition = "2018"
repository = "https://github.com/khvzak/mlua"
+25
View File
@@ -20,24 +20,28 @@ use crate::userdata::{AnyUserData, UserData};
use crate::value::{FromLua, Nil, ToLua, Value};
impl<'lua> ToLua<'lua> for Value<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(self)
}
}
impl<'lua> FromLua<'lua> for Value<'lua> {
#[inline]
fn from_lua(lua_value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
Ok(lua_value)
}
}
impl<'lua> ToLua<'lua> for String<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(self))
}
}
impl<'lua> FromLua<'lua> for String<'lua> {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<String<'lua>> {
let ty = value.type_name();
lua.coerce_string(value)?
@@ -50,12 +54,14 @@ impl<'lua> FromLua<'lua> for String<'lua> {
}
impl<'lua> ToLua<'lua> for Table<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Table(self))
}
}
impl<'lua> FromLua<'lua> for Table<'lua> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Table<'lua>> {
match value {
Value::Table(table) => Ok(table),
@@ -69,12 +75,14 @@ impl<'lua> FromLua<'lua> for Table<'lua> {
}
impl<'lua> ToLua<'lua> for Function<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Function(self))
}
}
impl<'lua> FromLua<'lua> for Function<'lua> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Function<'lua>> {
match value {
Value::Function(table) => Ok(table),
@@ -88,12 +96,14 @@ impl<'lua> FromLua<'lua> for Function<'lua> {
}
impl<'lua> ToLua<'lua> for Thread<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Thread(self))
}
}
impl<'lua> FromLua<'lua> for Thread<'lua> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Thread<'lua>> {
match value {
Value::Thread(t) => Ok(t),
@@ -107,12 +117,14 @@ impl<'lua> FromLua<'lua> for Thread<'lua> {
}
impl<'lua> ToLua<'lua> for AnyUserData<'lua> {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::UserData(self))
}
}
impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<AnyUserData<'lua>> {
match value {
Value::UserData(ud) => Ok(ud),
@@ -126,12 +138,14 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> {
}
impl<'lua, T: 'static + MaybeSend + UserData> ToLua<'lua> for T {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::UserData(lua.create_userdata(self)?))
}
}
impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<T> {
match value {
Value::UserData(ud) => Ok(ud.borrow::<T>()?.clone()),
@@ -145,12 +159,14 @@ impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T {
}
impl<'lua> ToLua<'lua> for Error {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Error(self))
}
}
impl<'lua> FromLua<'lua> for Error {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Error> {
match value {
Value::Error(err) => Ok(err),
@@ -164,12 +180,14 @@ impl<'lua> FromLua<'lua> for Error {
}
impl<'lua> ToLua<'lua> for bool {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::Boolean(self))
}
}
impl<'lua> FromLua<'lua> for bool {
#[inline]
fn from_lua(v: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match v {
Value::Nil => Ok(false),
@@ -180,12 +198,14 @@ impl<'lua> FromLua<'lua> for bool {
}
impl<'lua> ToLua<'lua> for LightUserData {
#[inline]
fn to_lua(self, _: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::LightUserData(self))
}
}
impl<'lua> FromLua<'lua> for LightUserData {
#[inline]
fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result<Self> {
match value {
Value::LightUserData(ud) => Ok(ud),
@@ -199,12 +219,14 @@ impl<'lua> FromLua<'lua> for LightUserData {
}
impl<'lua> ToLua<'lua> for StdString {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(&self)?))
}
}
impl<'lua> FromLua<'lua> for StdString {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
let ty = value.type_name();
Ok(lua
@@ -220,6 +242,7 @@ impl<'lua> FromLua<'lua> for StdString {
}
impl<'lua> ToLua<'lua> for &str {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
Ok(Value::String(lua.create_string(self)?))
}
@@ -595,6 +618,7 @@ impl<'lua, T: Ord + FromLua<'lua>> FromLua<'lua> for BTreeSet<T> {
}
impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
#[inline]
fn to_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
match self {
Some(val) => val.to_lua(lua),
@@ -604,6 +628,7 @@ impl<'lua, T: ToLua<'lua>> ToLua<'lua> for Option<T> {
}
impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Option<T> {
#[inline]
fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result<Self> {
match value {
Nil => Ok(None),
+3 -3
View File
@@ -5,7 +5,7 @@ use std::slice;
use crate::error::{Error, Result};
use crate::ffi;
use crate::types::LuaRef;
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, protect_lua, StackGuard};
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, StackGuard};
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
#[cfg(feature = "async")]
@@ -198,8 +198,8 @@ impl<'lua> Function<'lua> {
for arg in args {
lua.push_value(arg)?;
}
protect_lua(lua.state, nargs + 2, 1, |state| {
ffi::lua_pushcclosure(state, bind_call_impl, nargs + 2);
protect_lua!(lua.state, nargs + 2, 1, fn(state) {
ffi::lua_pushcclosure(state, bind_call_impl, ffi::lua_gettop(state));
})?;
Ok(Function(lua.pop_ref()))
+90
View File
@@ -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 {
+6 -3
View File
@@ -72,7 +72,7 @@
//! [`serde::Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
// mlua types in rustdoc of other crates get linked to here.
#![doc(html_root_url = "https://docs.rs/mlua/0.6.2")]
#![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))))]
@@ -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;
@@ -120,8 +120,11 @@ 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::{ser::Options as SerializeOptions, LuaSerdeExt};
pub use crate::serde::{
de::Options as DeserializeOptions, ser::Options as SerializeOptions, LuaSerdeExt,
};
pub mod prelude;
#[cfg(feature = "serialize")]
+319 -216
View File
File diff suppressed because it is too large Load Diff
+19 -4
View File
@@ -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),+)
};
}
@@ -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)
}};
}
+4 -1
View File
@@ -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,
};
+11 -11
View File
@@ -18,8 +18,8 @@ use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
};
use crate::util::{
assert_stack, check_stack, get_userdata, init_userdata_metatable, protect_lua, push_table,
rawset_field, take_userdata, StackGuard,
assert_stack, check_stack, get_userdata, init_userdata_metatable, push_table, rawset_field,
take_userdata, StackGuard,
};
use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
@@ -250,7 +250,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
scope: &Scope<'lua, 'scope>,
data: Rc<RefCell<T>>,
data_ptr: *mut c_void,
data_ptr: *const c_void,
method: NonStaticMethod<'callback, T>,
) -> Result<Function<'lua>> {
// On methods that actually receive the userdata, we fake a type check on the passed in
@@ -264,9 +264,9 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
if let Some(Value::UserData(ud)) = value {
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 3)?;
lua.push_userdata_ref(&ud.0, false)?;
if get_userdata(lua.state, -1) == data_ptr {
check_stack(lua.state, 2)?;
lua.push_userdata_ref(&ud.0)?;
if get_userdata(lua.state, -1) as *const _ == data_ptr {
return Ok(());
}
}
@@ -322,7 +322,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 13)?;
let data_ptr = protect_lua(lua.state, 0, 1, |state| {
let data_ptr = protect_lua!(lua.state, 0, 1, |state| {
ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<Rc<RefCell<T>>>>())
})?;
// Prepare metatable, add meta methods first and then meta fields
@@ -390,12 +390,12 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
+ methods_index.map(|_| 1).unwrap_or(0);
ffi::lua_pop(lua.state, count);
let mt_id = ffi::lua_topointer(lua.state, -1);
let mt_ptr = ffi::lua_topointer(lua.state, -1);
// Write userdata just before attaching metatable with `__gc` metamethod
ptr::write(data_ptr as _, UserDataCell::new(data));
ffi::lua_setmetatable(lua.state, -2);
let ud = AnyUserData(lua.pop_ref());
lua.register_userdata_metatable(mt_id as isize);
lua.register_userdata_metatable(mt_ptr, None);
#[cfg(any(feature = "lua51", feature = "luajit"))]
let newtable = lua.create_table()?;
@@ -410,9 +410,9 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
// Deregister metatable
ffi::lua_getmetatable(state, -1);
let mt_id = ffi::lua_topointer(state, -1);
let mt_ptr = ffi::lua_topointer(state, -1);
ffi::lua_pop(state, 1);
ud.lua.deregister_userdata_metatable(mt_id as isize);
ud.lua.deregister_userdata_metatable(mt_ptr);
// Clear uservalue
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
+254 -47
View File
@@ -1,19 +1,99 @@
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};
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.
#[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,11 @@ 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, _guard) = match self.value {
Value::Table(table) => {
let _guard = RecursionGuard::new(&table, &self.visited);
let mut iter = table.pairs::<StdString, Value>();
let (variant, value) = match iter.next() {
Some(v) => v?,
None => {
@@ -88,13 +179,22 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
&"map with a single key",
));
}
(variant, Some(value))
if check_value_if_skip(&value, self.options, &self.visited)? {
return Err(de::Error::custom("bad enum 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")),
};
visitor.visit_enum(EnumDeserializer { variant, value })
visitor.visit_enum(EnumDeserializer {
variant,
value,
options: self.options,
visited: self.visited,
})
}
#[inline]
@@ -102,12 +202,18 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
where
V: de::Visitor<'de>,
{
match self.0 {
match self.value {
Value::Table(t) => {
let _guard = RecursionGuard::new(&t, &self.visited);
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 +222,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 +255,17 @@ 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 _guard = RecursionGuard::new(&t, &self.visited);
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 +277,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 +304,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 +317,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 +344,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 +356,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 +381,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 +400,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 +413,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 +446,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 +461,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 +477,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 +488,57 @@ 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: &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
}
+46 -4
View File
@@ -10,7 +10,7 @@ use crate::ffi;
use crate::lua::Lua;
use crate::table::Table;
use crate::types::LightUserData;
use crate::util::{assert_stack, check_stack, protect_lua, StackGuard};
use crate::util::{assert_stack, check_stack, StackGuard};
use crate::value::Value;
/// Trait for serializing/deserializing Lua values using Serde.
@@ -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,12 +231,19 @@ 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.
// 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"));
+5 -5
View File
@@ -9,7 +9,7 @@ use crate::lua::Lua;
use crate::string::String;
use crate::table::Table;
use crate::types::Integer;
use crate::util::{check_stack, protect_lua, StackGuard};
use crate::util::{check_stack, StackGuard};
use crate::value::{ToLua, Value};
/// A struct for serializing Rust values into Lua values.
@@ -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()
}
@@ -318,12 +318,12 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
let value = lua.to_value_with(value, self.options)?;
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 5)?;
check_stack(lua.state, 4)?;
lua.push_ref(&self.table.0);
lua.push_value(value)?;
let len = ffi::lua_rawlen(lua.state, -2) as Integer;
protect_lua(lua.state, 2, 0, |state| {
protect_lua!(lua.state, 2, 0, fn(state) {
let len = ffi::lua_rawlen(state, -2) as Integer;
ffi::lua_rawseti(state, -2, len + 1);
})
}
+15 -16
View File
@@ -10,7 +10,7 @@ use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::types::{Integer, LuaRef};
use crate::util::{assert_stack, check_stack, protect_lua, StackGuard};
use crate::util::{assert_stack, check_stack, StackGuard};
use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value};
#[cfg(feature = "async")]
@@ -62,12 +62,12 @@ impl<'lua> Table<'lua> {
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 6)?;
check_stack(lua.state, 5)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
lua.push_value(value)?;
protect_lua(lua.state, 3, 0, |state| ffi::lua_settable(state, -3))
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_settable(state, -3))
}
}
@@ -101,11 +101,11 @@ impl<'lua> Table<'lua> {
let value = unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 5)?;
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
protect_lua(lua.state, 2, 1, |state| ffi::lua_gettable(state, -2))?;
protect_lua!(lua.state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
lua.pop_value()
};
@@ -119,13 +119,12 @@ impl<'lua> Table<'lua> {
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 5)?;
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
protect_lua(lua.state, 2, 1, |state| {
ffi::lua_gettable(state, -2) != ffi::LUA_TNIL
})
protect_lua!(lua.state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
Ok(ffi::lua_isnil(lua.state, -1) == 0)
}
}
@@ -193,12 +192,12 @@ impl<'lua> Table<'lua> {
unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 6)?;
check_stack(lua.state, 5)?;
lua.push_ref(&self.0);
lua.push_value(key)?;
lua.push_value(value)?;
protect_lua(lua.state, 3, 0, |state| ffi::lua_rawset(state, -3))
protect_lua!(lua.state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
}
}
@@ -236,7 +235,7 @@ impl<'lua> Table<'lua> {
lua.push_ref(&self.0);
lua.push_value(value)?;
protect_lua(lua.state, 2, 0, |state| {
protect_lua!(lua.state, 2, 0, |state| {
for i in (idx..=size).rev() {
// table[i+1] = table[i]
ffi::lua_rawgeti(state, -2, i);
@@ -268,7 +267,7 @@ impl<'lua> Table<'lua> {
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
protect_lua(lua.state, 1, 0, |state| {
protect_lua!(lua.state, 1, 0, |state| {
for i in idx..size {
ffi::lua_rawgeti(state, -1, i + 1);
ffi::lua_rawseti(state, -2, i);
@@ -294,7 +293,7 @@ impl<'lua> Table<'lua> {
check_stack(lua.state, 4)?;
lua.push_ref(&self.0);
protect_lua(lua.state, 1, 0, |state| ffi::luaL_len(state, -1))
protect_lua!(lua.state, 1, 0, |state| ffi::luaL_len(state, -1))
}
}
@@ -671,7 +670,7 @@ where
lua.push_ref(&self.table);
lua.push_value(prev_key)?;
let next = protect_lua(lua.state, 2, ffi::LUA_MULTRET, |state| {
let next = protect_lua!(lua.state, 2, ffi::LUA_MULTRET, |state| {
ffi::lua_next(state, -2)
})?;
if next != 0 {
@@ -732,7 +731,7 @@ where
let res = if self.raw {
ffi::lua_rawgeti(lua.state, -1, index)
} else {
protect_lua(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index))?
protect_lua!(lua.state, 1, 1, |state| ffi::lua_geti(state, -1, index))?
};
match res {
ffi::LUA_TNIL if index > self.len.unwrap_or(0) => Ok(None),
+3 -2
View File
@@ -4,7 +4,7 @@ use std::os::raw::c_int;
use crate::error::{Error, Result};
use crate::ffi;
use crate::types::LuaRef;
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, protect_lua, StackGuard};
use crate::util::{assert_stack, check_stack, error_traceback, pop_error, StackGuard};
use crate::value::{FromLuaMulti, MultiValue, ToLuaMulti};
#[cfg(any(feature = "lua54", all(feature = "luajit", feature = "vendored"), doc))]
@@ -135,7 +135,7 @@ impl<'lua> Thread<'lua> {
let ret = ffi::lua_resume(thread_state, lua.state, nargs, &mut nresults as *mut c_int);
if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD {
protect_lua(lua.state, 0, 0, |_| error_traceback(thread_state))?;
protect_lua!(lua.state, 0, 0, |_| error_traceback(thread_state))?;
return Err(pop_error(thread_state, ret));
}
@@ -341,6 +341,7 @@ where
}
#[cfg(feature = "async")]
#[inline(always)]
fn is_poll_pending(val: &MultiValue) -> bool {
match val.iter().enumerate().last() {
Some((1, Value::LightUserData(ud))) => {
+6
View File
@@ -47,6 +47,10 @@ pub(crate) struct AsyncPollUpvalue<'lua> {
pub(crate) fut: LocalBoxFuture<'lua, Result<MultiValue<'lua>>>,
}
#[cfg(feature = "send")]
pub(crate) type HookCallback = Arc<RefCell<dyn FnMut(&Lua, Debug) -> Result<()> + Send>>;
#[cfg(not(feature = "send"))]
pub(crate) type HookCallback = Arc<RefCell<dyn FnMut(&Lua, Debug) -> Result<()>>>;
#[cfg(feature = "send")]
@@ -59,6 +63,8 @@ pub trait MaybeSend {}
#[cfg(not(feature = "send"))]
impl<T> MaybeSend for T {}
pub(crate) struct DestructedUserdataMT;
/// An auto generated key into the Lua registry.
///
/// This is a handle to a value stored inside the Lua registry. It is not automatically
+27 -48
View File
@@ -21,9 +21,7 @@ use crate::function::Function;
use crate::lua::Lua;
use crate::table::{Table, TablePairs};
use crate::types::{Callback, LuaRef, MaybeSend};
use crate::util::{
check_stack, get_destructed_userdata_metatable, get_userdata, push_string, StackGuard,
};
use crate::util::{check_stack, get_userdata, StackGuard};
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
@@ -520,8 +518,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
///
@@ -595,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,
@@ -608,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()))
@@ -616,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()))
@@ -631,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,
@@ -657,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,
@@ -667,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,
@@ -724,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())
}
@@ -734,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())
}
@@ -759,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);
@@ -778,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()
};
@@ -809,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()))
}
@@ -836,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,
@@ -863,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),
}
}
}
@@ -985,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()
+66 -38
View File
@@ -4,7 +4,7 @@ use std::error::Error as StdError;
use std::fmt::Write;
use std::os::raw::{c_char, c_int, c_void};
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
use std::sync::{Arc, Mutex};
use std::sync::Arc;
use std::{mem, ptr, slice};
use once_cell::sync::Lazy;
@@ -12,13 +12,17 @@ use once_cell::sync::Lazy;
use crate::error::{Error, Result};
use crate::ffi;
static METATABLE_CACHE: Lazy<Mutex<HashMap<TypeId, u8>>> = Lazy::new(|| {
// The capacity must(!) be greater than number of stored keys
Mutex::new(HashMap::with_capacity(32))
static METATABLE_CACHE: Lazy<HashMap<TypeId, u8>> = Lazy::new(|| {
let mut map = HashMap::with_capacity(32);
crate::lua::init_metatable_cache(&mut map);
map.insert(TypeId::of::<WrappedFailure>(), 0);
map.insert(TypeId::of::<String>(), 0);
map
});
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
// panic with an internal error message.
#[inline]
pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
// TODO: This should only be triggered when there is a logic error in `mlua`. In the future,
// when there is a way to be confident about stack safety and test it, this could be enabled
@@ -30,6 +34,7 @@ pub unsafe fn assert_stack(state: *mut ffi::lua_State, amount: c_int) {
}
// Checks that Lua has enough free stack space and returns `Error::StackError` on failure.
#[inline]
pub unsafe fn check_stack(state: *mut ffi::lua_State, amount: c_int) -> Result<()> {
if ffi::lua_checkstack(state, amount) == 0 {
Err(Error::StackError)
@@ -48,6 +53,7 @@ impl StackGuard {
// Creates a StackGuard instance with record of the stack size, and on Drop will check the
// stack size and drop any extra elements. If the stack size at the end is *smaller* than at
// the beginning, this is considered a fatal logic error and will result in a panic.
#[inline]
pub unsafe fn new(state: *mut ffi::lua_State) -> StackGuard {
StackGuard {
state,
@@ -57,6 +63,7 @@ impl StackGuard {
}
// Similar to `new`, but checks and keeps `extra` elements from top of the stack on Drop.
#[inline]
pub unsafe fn new_extra(state: *mut ffi::lua_State, extra: c_int) -> StackGuard {
StackGuard {
state,
@@ -83,6 +90,35 @@ impl Drop for StackGuard {
}
}
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
// limited lua stack. `nargs` is the same as the the parameter to `lua_pcall`, and `nresults` is
// always `LUA_MULTRET`. Provided function must *not* panic, and since it will generally be lonjmping,
// should not contain any values that implements Drop.
// Internally uses 2 extra stack spaces, and does not call checkstack.
pub unsafe fn protect_lua_call(
state: *mut ffi::lua_State,
nargs: c_int,
f: unsafe extern "C" fn(*mut ffi::lua_State) -> c_int,
) -> Result<()> {
let stack_start = ffi::lua_gettop(state) - nargs;
ffi::lua_pushcfunction(state, error_traceback);
ffi::lua_pushcfunction(state, f);
if nargs > 0 {
ffi::lua_rotate(state, stack_start + 1, 2);
}
let ret = ffi::lua_pcall(state, nargs, ffi::LUA_MULTRET, stack_start + 1);
ffi::lua_remove(state, stack_start + 1);
if ret == ffi::LUA_OK {
Ok(())
} else {
Err(pop_error(state, ret))
}
}
// Call a function that calls into the Lua API and may trigger a Lua error (longjmp) in a safe way.
// Wraps the inner function in a call to `lua_pcall`, so the inner function only has access to a
// limited lua stack. `nargs` and `nresults` are similar to the parameters of `lua_pcall`, but the
@@ -90,7 +126,7 @@ impl Drop for StackGuard {
// values are assumed to match the `nresults` param. Provided function must *not* panic, and since it
// will generally be lonjmping, should not contain any values that implements Drop.
// Internally uses 3 extra stack spaces, and does not call checkstack.
pub unsafe fn protect_lua<F, R>(
pub unsafe fn protect_lua_closure<F, R>(
state: *mut ffi::lua_State,
nargs: c_int,
nresults: c_int,
@@ -210,30 +246,32 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
}
}
// Uses 3 stack spaces
// Uses 3 stack spaces, does not call checkstack.
#[inline]
pub unsafe fn push_string<S: AsRef<[u8]> + ?Sized>(
state: *mut ffi::lua_State,
s: &S,
) -> Result<()> {
let s = s.as_ref();
protect_lua(state, 0, 1, |state| {
protect_lua!(state, 0, 1, |state| {
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
})
}
// Uses 3 stack spaces
// Uses 3 stack spaces, does not call checkstack.
#[inline]
pub unsafe fn push_table(state: *mut ffi::lua_State, narr: c_int, nrec: c_int) -> Result<()> {
protect_lua(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
}
// Uses 4 stack spaces
// Uses 4 stack spaces, does not call checkstack.
pub unsafe fn rawset_field<S>(state: *mut ffi::lua_State, table: c_int, field: &S) -> Result<()>
where
S: AsRef<[u8]> + ?Sized,
{
let field = field.as_ref();
ffi::lua_pushvalue(state, table);
protect_lua(state, 2, 0, |state| {
protect_lua!(state, 2, 0, |state| {
ffi::lua_pushlstring(state, field.as_ptr() as *const c_char, field.len());
ffi::lua_rotate(state, -3, 2);
ffi::lua_rawset(state, -3);
@@ -241,14 +279,16 @@ where
}
// Internally uses 3 stack spaces, does not call checkstack.
#[inline]
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
let ud = protect_lua(state, 0, 1, |state| {
let ud = protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
})?;
ptr::write(ud, t);
Ok(())
}
#[inline]
pub unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
let ud = ffi::lua_touserdata(state, index) as *mut T;
mlua_debug_assert!(!ud.is_null(), "userdata pointer is null");
@@ -412,7 +452,7 @@ pub unsafe fn init_userdata_metatable<T>(
ffi::lua_pushnil(state);
}
}
protect_lua(state, 3, 1, |state| {
protect_lua!(state, 3, 1, fn(state) {
ffi::lua_pushcclosure(state, meta_index_impl, 3);
})?;
}
@@ -428,7 +468,7 @@ pub unsafe fn init_userdata_metatable<T>(
match newindex_type {
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
ffi::lua_pushvalue(state, field_setters);
protect_lua(state, 2, 1, |state| {
protect_lua!(state, 2, 1, fn(state) {
ffi::lua_pushcclosure(state, meta_newindex_impl, 2);
})?;
}
@@ -450,11 +490,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
@@ -647,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>);
@@ -670,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(())
@@ -679,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);
}
@@ -825,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);
})?;
@@ -833,7 +861,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
// Create error print buffer
init_gc_metatable::<String>(state, None)?;
push_gc_userdata(state, String::new())?;
protect_lua(state, 1, 0, |state| {
protect_lua!(state, 1, 0, fn(state) {
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
})?;
+13
View File
@@ -157,18 +157,21 @@ pub struct MultiValue<'lua>(Vec<Value<'lua>>);
impl<'lua> MultiValue<'lua> {
/// Creates an empty `MultiValue` containing no values.
#[inline]
pub fn new() -> MultiValue<'lua> {
MultiValue(Vec::new())
}
}
impl<'lua> Default for MultiValue<'lua> {
#[inline]
fn default() -> MultiValue<'lua> {
MultiValue::new()
}
}
impl<'lua> FromIterator<Value<'lua>> for MultiValue<'lua> {
#[inline]
fn from_iter<I: IntoIterator<Item = Value<'lua>>>(iter: I) -> Self {
MultiValue::from_vec(Vec::from_iter(iter))
}
@@ -178,6 +181,7 @@ impl<'lua> IntoIterator for MultiValue<'lua> {
type Item = Value<'lua>;
type IntoIter = iter::Rev<vec::IntoIter<Value<'lua>>>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter().rev()
}
@@ -187,43 +191,52 @@ impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
type Item = &'a Value<'lua>;
type IntoIter = iter::Rev<slice::Iter<'a, Value<'lua>>>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
(&self.0).iter().rev()
}
}
impl<'lua> MultiValue<'lua> {
#[inline]
pub fn from_vec(mut v: Vec<Value<'lua>>) -> MultiValue<'lua> {
v.reverse();
MultiValue(v)
}
#[inline]
pub fn into_vec(self) -> Vec<Value<'lua>> {
let mut v = self.0;
v.reverse();
v
}
#[inline]
pub(crate) fn reserve(&mut self, size: usize) {
self.0.reserve(size);
}
#[inline]
pub(crate) fn push_front(&mut self, value: Value<'lua>) {
self.0.push(value);
}
#[inline]
pub(crate) fn pop_front(&mut self) -> Option<Value<'lua>> {
self.0.pop()
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.len() == 0
}
#[inline]
pub fn iter(&self) -> iter::Rev<slice::Iter<Value<'lua>>> {
self.0.iter().rev()
}
+1 -1
View File
@@ -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
+6 -34
View File
@@ -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]`
+6 -36
View File
@@ -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<'_>`
+1 -1
View File
@@ -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
View File
@@ -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| {
+87 -2
View File
@@ -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]
@@ -302,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();
@@ -310,7 +343,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 +446,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(())
}