Compare commits

...

12 Commits

Author SHA1 Message Date
Alex Orlenko 15e353a7f8 v0.8.9 2023-05-16 23:02:19 +01:00
Alex Orlenko 765117c2bb Update tarpaulin settings 2023-05-16 23:02:17 +01:00
Alex Orlenko 573d71345f Don't set html_root_url (it's not recommended) 2023-05-16 22:55:29 +01:00
Alex Orlenko 72de17bf47 Allow deserializing Lua null into unit(()) or unit struct. See #264 2023-05-16 22:53:37 +01:00
Alex Orlenko 5a96e80266 Use lua_closethread instead of lua_resetthread in vendored mode (introduced in Lua 5.4.6) 2023-05-16 22:50:46 +01:00
Alex Orlenko bfdb4087b8 Update minimal (vendored) Lua 5.4 to 5.4.6 2023-05-16 22:49:49 +01:00
Alex Orlenko eb84284824 Fix ref_stack_exhaustion test (Lua 5.4.6) 2023-05-16 22:12:36 +01:00
Alex Orlenko 34679e105d v0.8.8 2023-03-05 17:50:53 +00:00
Alex Orlenko bc194981fc Optimize userdata methods call when __index and fields_getters are nil 2023-03-05 14:43:12 +00:00
Alex Orlenko c9715aa5d9 Fix potential deadlock when trying to reuse dropped RegistryKey.
If no free registry id found, we call protect_lua! macro while keeping mutex guard to the unref list.
Protected calls can trigger garbage collection and if RegistryKey is placed in userdata being collected, this can lead to deadlock.
The solution is drop mutex guard as soon as possible.
Also this commit includes optimization in creating reference in Lua registry.
2023-03-05 14:39:22 +00:00
Alex Orlenko c108dc8213 Force protected mode for long enough strings 2023-03-05 14:35:15 +00:00
Alex Orlenko e86ef9d755 v0.8.7 2023-01-04 16:15:23 +00:00
13 changed files with 103 additions and 25 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
- name: Generate coverage report
run: |
cargo tarpaulin --verbose --features lua54,vendored,async,send,serialize,macros --out xml --exclude-files benches --exclude-files build --exclude-files mlua_derive --exclude-files src/ffi --exclude-files tests
cargo tarpaulin --out xml --tests --exclude-files benches/* --exclude-files src/ffi/*/*
- name: Upload report to codecov.io
uses: codecov/codecov-action@v3
+18
View File
@@ -1,3 +1,21 @@
## v0.8.9
- Update minimal (vendored) Lua 5.4 to 5.4.6
- Use `lua_closethread` instead of `lua_resetthread` in vendored mode (Lua 5.4.6)
- Allow deserializing Lua null into unit (`()`) or unit struct.
## v0.8.8
- Fix potential deadlock when trying to reuse dropped registry keys.
- Optimize userdata methods call when __index and fields_getters are nil
## v0.8.7
- Minimum Luau updated to 0.555 (`LUAI_MAXCSTACK` limit increased to 100000)
- `_VERSION` in Luau now includes version number
- Fixed lifetime of `DebugNames` in `Debug::names()` and `DebugSource` in `Debug::source()`
- Fixed subtraction overflow when calculating index for `MultiValue::get()`
## v0.8.6
- Fixed bug when recycled Registry slot can be set to Nil
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua"
version = "0.8.6" # remember to update html_root_url and mlua_derive
version = "0.8.9" # remember to update mlua_derive
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
edition = "2021"
repository = "https://github.com/khvzak/mlua"
@@ -56,7 +56,7 @@ parking_lot = { version = "0.12", optional = true }
[build-dependencies]
cc = { version = "1.0" }
pkg-config = { version = "0.3.17" }
lua-src = { version = ">= 544.0.0, < 550.0.0", optional = true }
lua-src = { version = ">= 546.0.0, < 550.0.0", optional = true }
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
luau0-src = { version = "0.5.0", optional = true }
+1 -1
View File
@@ -7,7 +7,7 @@
[crates.io]: https://crates.io/crates/mlua
[API Documentation]: https://docs.rs/mlua/badge.svg
[docs.rs]: https://docs.rs/mlua
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/master/graph/badge.svg?token=99339FS1CG
[Coverage Status]: https://codecov.io/gh/khvzak/mlua/branch/v0.8/graph/badge.svg?token=99339FS1CG
[codecov.io]: https://codecov.io/gh/khvzak/mlua
[MSRV]: https://img.shields.io/badge/rust-1.56+-brightgreen.svg?&logo=rust
+3
View File
@@ -112,7 +112,10 @@ extern "C" {
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
pub fn lua_close(L: *mut lua_State);
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
// Deprecated in Lua 5.4.6
pub fn lua_resetthread(L: *mut lua_State) -> c_int;
#[cfg(feature = "vendored")]
pub fn lua_closethread(L: *mut lua_State, from: *mut lua_State) -> c_int;
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
-2
View File
@@ -71,8 +71,6 @@
//! [`serde::Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
//! [`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.8.6")]
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
// warnings at all.
#![doc(test(attr(deny(warnings))))]
+15 -8
View File
@@ -1738,8 +1738,10 @@ impl Lua {
let extra = &mut *self.extra.get();
if extra.recycled_thread_cache.len() < extra.recycled_thread_cache.capacity() {
let thread_state = ffi::lua_tothread(extra.ref_thread, thread.0.index);
#[cfg(feature = "lua54")]
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
let status = ffi::lua_resetthread(thread_state);
#[cfg(all(feature = "lua54", feature = "vendored"))]
let status = ffi::lua_closethread(thread_state, self.state);
#[cfg(feature = "lua54")]
if status != ffi::LUA_OK {
// Error object is on top, drop it
@@ -2072,22 +2074,27 @@ impl Lua {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 4)?;
let unref_list = (*self.extra.get()).registry_unref_list.clone();
self.push_value(t)?;
// Try to reuse previously allocated slot
let unref_list2 = unref_list.clone();
let mut unref_list2 = mlua_expect!(unref_list2.lock(), "unref list poisoned");
if let Some(registry_id) = unref_list2.as_mut().and_then(|x| x.pop()) {
let unref_list = (*self.extra.get()).registry_unref_list.clone();
let free_registry_id = mlua_expect!(unref_list.lock(), "unref list poisoned")
.as_mut()
.and_then(|x| x.pop());
if let Some(registry_id) = free_registry_id {
// It must be safe to replace the value without triggering memory error
ffi::lua_rawseti(self.state, ffi::LUA_REGISTRYINDEX, registry_id as Integer);
return Ok(RegistryKey::new(registry_id, unref_list));
}
// Allocate a new RegistryKey
let registry_id = protect_lua!(self.state, 1, 0, |state| {
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
})?;
let registry_id = if self.unlikely_memory_error() {
ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX)
} else {
protect_lua!(self.state, 1, 0, |state| {
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
})?
};
Ok(RegistryKey::new(registry_id, unref_list))
}
}
+23 -1
View File
@@ -327,9 +327,31 @@ impl<'lua, 'de> serde::Deserializer<'de> for Deserializer<'lua> {
visitor.visit_newtype_struct(self)
}
#[inline]
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.value {
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_unit(),
_ => self.deserialize_any(visitor),
}
}
#[inline]
fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
match self.value {
Value::LightUserData(ud) if ud.0.is_null() => visitor.visit_unit(),
_ => self.deserialize_any(visitor),
}
}
serde::forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes
byte_buf unit unit_struct identifier ignored_any
byte_buf identifier ignored_any
}
}
+3 -1
View File
@@ -201,8 +201,10 @@ impl<'lua> Thread<'lua> {
lua.push_ref(&self.0);
let thread_state = ffi::lua_tothread(lua.state, -1);
#[cfg(feature = "lua54")]
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
let status = ffi::lua_resetthread(thread_state);
#[cfg(all(feature = "lua54", feature = "vendored"))]
let status = ffi::lua_closethread(thread_state, lua.state);
#[cfg(feature = "lua54")]
if status != ffi::LUA_OK {
return Err(pop_error(thread_state, status));
+10 -3
View File
@@ -245,7 +245,8 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
// Uses 3 (or 1 if unprotected) stack spaces, does not call checkstack.
#[inline(always)]
pub unsafe fn push_string(state: *mut ffi::lua_State, s: &[u8], protect: bool) -> Result<()> {
if protect {
// Always use protected mode if the string is too long
if protect || s.len() > (1 << 30) {
protect_lua!(state, 0, 1, |state| {
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
})
@@ -427,11 +428,17 @@ unsafe fn init_userdata_metatable_index(state: *mut ffi::lua_State) -> Result<()
}
ffi::lua_pop(state, 1);
// Create and cache `__index` helper
// Create and cache `__index` generator
let code = cstr!(
r#"
local error, isfunction = ...
return function (__index, field_getters, methods)
-- Fastpath to return methods table for index access
if __index == nil and field_getters == nil then
return methods
end
-- Alternatively return a function for index access
return function (self, key)
if field_getters ~= nil then
local field_getter = field_getters[key]
@@ -481,7 +488,7 @@ pub unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Re
}
ffi::lua_pop(state, 1);
// Create and cache `__newindex` helper
// Create and cache `__newindex` generator
let code = cstr!(
r#"
local error, isfunction = ...
+8
View File
@@ -0,0 +1,8 @@
[lua54_coverage]
features = "lua54,vendored,async,serialize,macros"
[lua51_coverage]
features = "lua51,vendored,async,serialize,macros"
[luau_coverage]
features = "luau,async,serialize,macros"
+18 -5
View File
@@ -391,31 +391,44 @@ fn test_from_value_newtype_struct() -> Result<(), Box<dyn std::error::Error>> {
#[test]
fn test_from_value_enum() -> Result<(), Box<dyn std::error::Error>> {
let lua = Lua::new();
lua.globals().set("null", lua.null())?;
#[derive(Deserialize, PartialEq, Debug)]
enum E {
struct UnitStruct;
#[derive(Deserialize, PartialEq, Debug)]
enum E<T = ()> {
Unit,
Integer(u32),
Tuple(u32, u32),
Struct { a: u32 },
Wrap(T),
}
let value = lua.load(r#""Unit""#).eval()?;
let got = lua.from_value(value)?;
let got: E = lua.from_value(value)?;
assert_eq!(E::Unit, got);
let value = lua.load(r#"{Integer = 1}"#).eval()?;
let got = lua.from_value(value)?;
let got: E = lua.from_value(value)?;
assert_eq!(E::Integer(1), got);
let value = lua.load(r#"{Tuple = {1, 2}}"#).eval()?;
let got = lua.from_value(value)?;
let got: E = lua.from_value(value)?;
assert_eq!(E::Tuple(1, 2), got);
let value = lua.load(r#"{Struct = {a = 3}}"#).eval()?;
let got = lua.from_value(value)?;
let got: E = lua.from_value(value)?;
assert_eq!(E::Struct { a: 3 }, got);
let value = lua.load(r#"{Wrap = null}"#).eval()?;
let got = lua.from_value(value)?;
assert_eq!(E::Wrap(UnitStruct), got);
let value = lua.load(r#"{Wrap = null}"#).eval()?;
let got = lua.from_value(value)?;
assert_eq!(E::Wrap(()), got);
Ok(())
}
+1 -1
View File
@@ -991,7 +991,7 @@ fn test_ref_stack_exhaustion() {
match catch_unwind(AssertUnwindSafe(|| -> Result<()> {
let lua = Lua::new();
let mut vals = Vec::new();
for _ in 0..1000000 {
for _ in 0..10000000 {
vals.push(lua.create_table()?);
}
Ok(())