mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d3704c222 | |||
| 496c8fa0e9 | |||
| 9ce0e11518 | |||
| b77836920a | |||
| f1ceaf0ff1 | |||
| 884a025b52 | |||
| c23fa5aa6c | |||
| 4f1d2abbcb | |||
| a25e81036e | |||
| b46cad1db1 |
@@ -1,3 +1,9 @@
|
||||
## v0.9.9
|
||||
|
||||
- Minimal Luau updated to 0.629
|
||||
- Fixed bug when attempting to reset or resume already running coroutines (#416).
|
||||
- Added `RegistryKey::id()` method to get the underlying Lua registry key id.
|
||||
|
||||
## v0.9.8
|
||||
|
||||
- Fixed serializing same table multiple times (#408)
|
||||
|
||||
+7
-4
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.9.8" # remember to update mlua_derive
|
||||
version = "0.9.9" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
@@ -45,17 +45,17 @@ unstable = []
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.9.3", optional = true, path = "mlua_derive" }
|
||||
bstr = { version = "1.0", features = ["std"], default_features = false }
|
||||
bstr = { version = "1.0", features = ["std"], default-features = false }
|
||||
once_cell = { version = "1.0" }
|
||||
num-traits = { version = "0.2.14" }
|
||||
rustc-hash = "1.0"
|
||||
rustc-hash = "2.0"
|
||||
futures-util = { version = "0.3", optional = true, default-features = false, features = ["std"] }
|
||||
serde = { version = "1.0", optional = true }
|
||||
erased-serde = { version = "0.4", optional = true }
|
||||
serde-value = { version = "0.7", optional = true }
|
||||
parking_lot = { version = "0.12", optional = true }
|
||||
|
||||
ffi = { package = "mlua-sys", version = "0.6.0", path = "mlua-sys" }
|
||||
ffi = { package = "mlua-sys", version = "0.6.1", path = "mlua-sys" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libloading = { version = "0.8", optional = true }
|
||||
@@ -79,6 +79,9 @@ criterion = { version = "0.5", features = ["async_tokio"] }
|
||||
rustyline = "14.0"
|
||||
tokio = { version = "1.0", features = ["full"] }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(tarpaulin_include)'] }
|
||||
|
||||
[[bench]]
|
||||
name = "benchmark"
|
||||
harness = false
|
||||
|
||||
@@ -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/mlua-rs/mlua/branch/master/graph/badge.svg?token=99339FS1CG
|
||||
[Coverage Status]: https://codecov.io/gh/mlua-rs/mlua/branch/main/graph/badge.svg?token=99339FS1CG
|
||||
[codecov.io]: https://codecov.io/gh/mlua-rs/mlua
|
||||
[MSRV]: https://img.shields.io/badge/rust-1.71+-brightgreen.svg?&logo=rust
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
> **Note**
|
||||
>
|
||||
> See v0.9 [release notes](https://github.com/khvzak/mlua/blob/master/docs/release_notes/v0.9.md).
|
||||
> See v0.9 [release notes](https://github.com/khvzak/mlua/blob/main/docs/release_notes/v0.9.md).
|
||||
|
||||
`mlua` is bindings to [Lua](https://www.lua.org) programming language for Rust with a goal to provide
|
||||
_safe_ (as far as it's possible), high level, easy to use, practical and flexible API.
|
||||
@@ -133,7 +133,7 @@ Add to `Cargo.toml` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.9.8", features = ["lua54", "vendored"] }
|
||||
mlua = { version = "0.9.9", features = ["lua54", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -168,7 +168,7 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.9.8", features = ["lua54", "module"] }
|
||||
mlua = { version = "0.9.9", features = ["lua54", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
@@ -224,7 +224,11 @@ Module builds don't require Lua lib or headers to be installed on the system.
|
||||
There is a LuaRocks build backend for mlua modules [`luarocks-build-rust-mlua`].
|
||||
|
||||
Modules written in Rust and published to luarocks:
|
||||
- [`decasify`](https://github.com/alerque/decasify)
|
||||
- [`lua-ryaml`](https://github.com/khvzak/lua-ryaml)
|
||||
- [`tiktoken_core`](https://github.com/gptlang/lua-tiktoken)
|
||||
- [`toml-edit`](https://github.com/vhyrro/toml-edit.lua)
|
||||
- [`typst-lua`](https://github.com/rousbound/typst-lua)
|
||||
|
||||
[`luarocks-build-rust-mlua`]: https://luarocks.org/modules/khvzak/luarocks-build-rust-mlua
|
||||
|
||||
|
||||
@@ -268,6 +268,23 @@ fn registry_value_create(c: &mut Criterion) {
|
||||
});
|
||||
}
|
||||
|
||||
fn registry_value_get(c: &mut Criterion) {
|
||||
let lua = Lua::new();
|
||||
lua.gc_stop();
|
||||
|
||||
let value = lua.create_registry_value("hello").unwrap();
|
||||
|
||||
c.bench_function("registry value [get]", |b| {
|
||||
b.iter_batched(
|
||||
|| collect_gc_twice(&lua),
|
||||
|_| {
|
||||
assert_eq!(lua.registry_value::<LuaString>(&value).unwrap(), "hello");
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn userdata_create(c: &mut Criterion) {
|
||||
struct UserData(#[allow(unused)] i64);
|
||||
impl LuaUserData for UserData {}
|
||||
@@ -406,6 +423,7 @@ criterion_group! {
|
||||
function_async_call_sum,
|
||||
|
||||
registry_value_create,
|
||||
registry_value_get,
|
||||
|
||||
userdata_create,
|
||||
userdata_call_index,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
The v0.9 version of mlua is a major release that includes a number of API changes and improvements. This release is a stepping stone towards the v1.0.
|
||||
This document highlights the most important changes. For a full list of changes, see the [CHANGELOG].
|
||||
|
||||
[CHANGELOG]: https://github.com/khvzak/mlua/blob/master/CHANGELOG.md
|
||||
[CHANGELOG]: https://github.com/khvzak/mlua/blob/main/CHANGELOG.md
|
||||
|
||||
### New features
|
||||
|
||||
|
||||
+6
-3
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.6.0"
|
||||
version = "0.6.2"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
@@ -38,6 +38,9 @@ module = []
|
||||
cc = "1.0"
|
||||
cfg-if = "1.0"
|
||||
pkg-config = "0.3.17"
|
||||
lua-src = { version = ">= 546.0.2, < 546.1.0", optional = true }
|
||||
lua-src = { version = ">= 547.0.0, < 547.1.0", optional = true }
|
||||
luajit-src = { version = ">= 210.5.0, < 210.6.0", optional = true }
|
||||
luau0-src = { version = "0.9.0", optional = true }
|
||||
luau0-src = { version = "0.10.0", optional = true }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(raw_dylib)'] }
|
||||
|
||||
+13
-6
@@ -39,20 +39,25 @@ pub const LUA_MAX_UPVALUES: c_int = 200;
|
||||
#[doc(hidden)]
|
||||
pub const LUA_TRACEBACK_STACK: c_int = 11;
|
||||
|
||||
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/common/alloc.rs
|
||||
// The minimum alignment guaranteed by the architecture. This value is used to
|
||||
// add fast paths for low alignment values.
|
||||
// Copied from https://github.com/rust-lang/rust/blob/master/library/std/src/sys/common/alloc.rs
|
||||
#[cfg(any(
|
||||
target_arch = "x86",
|
||||
target_arch = "arm",
|
||||
target_arch = "m68k",
|
||||
target_arch = "csky",
|
||||
target_arch = "mips",
|
||||
target_arch = "mips32r6",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "powerpc64",
|
||||
target_arch = "sparc",
|
||||
target_arch = "asmjs",
|
||||
target_arch = "wasm32",
|
||||
target_arch = "hexagon",
|
||||
all(target_arch = "riscv32", not(target_os = "espidf")),
|
||||
all(
|
||||
target_arch = "riscv32",
|
||||
not(any(target_os = "espidf", target_os = "zkvm"))
|
||||
),
|
||||
all(target_arch = "xtensa", not(target_os = "espidf")),
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
@@ -60,18 +65,20 @@ pub const SYS_MIN_ALIGN: usize = 8;
|
||||
#[cfg(any(
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "arm64ec",
|
||||
target_arch = "loongarch64",
|
||||
target_arch = "mips64",
|
||||
target_arch = "mips64r6",
|
||||
target_arch = "s390x",
|
||||
target_arch = "sparc64",
|
||||
target_arch = "riscv64",
|
||||
target_arch = "wasm64",
|
||||
target_arch = "loongarch64",
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
pub const SYS_MIN_ALIGN: usize = 16;
|
||||
// The allocator on the esp-idf platform guarentees 4 byte alignment.
|
||||
// The allocator on the esp-idf and zkvm platforms guarantee 4 byte alignment.
|
||||
#[cfg(any(
|
||||
all(target_arch = "riscv32", target_os = "espidf"),
|
||||
all(target_arch = "riscv32", any(target_os = "espidf", target_os = "zkvm")),
|
||||
all(target_arch = "xtensa", target_os = "espidf"),
|
||||
))]
|
||||
#[doc(hidden)]
|
||||
|
||||
@@ -288,6 +288,8 @@ extern "C-unwind" {
|
||||
pub fn lua_setuserdatatag(L: *mut lua_State, idx: c_int, tag: c_int);
|
||||
pub fn lua_setuserdatadtor(L: *mut lua_State, tag: c_int, dtor: Option<lua_Destructor>);
|
||||
pub fn lua_getuserdatadtor(L: *mut lua_State, tag: c_int) -> Option<lua_Destructor>;
|
||||
pub fn lua_setuserdatametatable(L: *mut lua_State, tag: c_int, idx: c_int);
|
||||
pub fn lua_getuserdatametatable(L: *mut lua_State, tag: c_int);
|
||||
pub fn lua_setlightuserdataname(L: *mut lua_State, tag: c_int, name: *const c_char);
|
||||
pub fn lua_getlightuserdataname(L: *mut lua_State, tag: c_int) -> *const c_char;
|
||||
pub fn lua_clonefunction(L: *mut lua_State, idx: c_int);
|
||||
|
||||
@@ -14,6 +14,7 @@ pub struct lua_CompileOptions {
|
||||
pub vectorCtor: *const c_char,
|
||||
pub vectorType: *const c_char,
|
||||
pub mutableGlobals: *const *const c_char,
|
||||
pub userdataTypes: *const *const c_char,
|
||||
}
|
||||
|
||||
impl Default for lua_CompileOptions {
|
||||
@@ -27,6 +28,7 @@ impl Default for lua_CompileOptions {
|
||||
vectorCtor: ptr::null(),
|
||||
vectorType: ptr::null(),
|
||||
mutableGlobals: ptr::null(),
|
||||
userdataTypes: ptr::null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
-15
@@ -128,6 +128,7 @@ pub struct Compiler {
|
||||
vector_ctor: Option<String>,
|
||||
vector_type: Option<String>,
|
||||
mutable_globals: Vec<String>,
|
||||
userdata_types: Vec<String>,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
@@ -151,6 +152,7 @@ impl Compiler {
|
||||
vector_ctor: None,
|
||||
vector_type: None,
|
||||
mutable_globals: Vec::new(),
|
||||
userdata_types: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,6 +232,13 @@ impl Compiler {
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a list of userdata types that will be included in the type information.
|
||||
#[must_use]
|
||||
pub fn set_userdata_types(mut self, types: Vec<String>) -> Self {
|
||||
self.userdata_types = types;
|
||||
self
|
||||
}
|
||||
|
||||
/// Compiles the `source` into bytecode.
|
||||
pub fn compile(&self, source: impl AsRef<[u8]>) -> Vec<u8> {
|
||||
use std::os::raw::c_int;
|
||||
@@ -245,22 +254,26 @@ impl Compiler {
|
||||
let vector_type = vector_type.and_then(|t| CString::new(t).ok());
|
||||
let vector_type = vector_type.as_ref();
|
||||
|
||||
let mutable_globals = self
|
||||
.mutable_globals
|
||||
.iter()
|
||||
.map(|name| CString::new(name.clone()).ok())
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.unwrap_or_default();
|
||||
let mut mutable_globals = mutable_globals
|
||||
.iter()
|
||||
.map(|s| s.as_ptr())
|
||||
.collect::<Vec<_>>();
|
||||
let mut mutable_globals_ptr = ptr::null();
|
||||
if !mutable_globals.is_empty() {
|
||||
mutable_globals.push(ptr::null());
|
||||
mutable_globals_ptr = mutable_globals.as_ptr();
|
||||
macro_rules! vec2cstring_ptr {
|
||||
($name:ident, $name_ptr:ident) => {
|
||||
let $name = self
|
||||
.$name
|
||||
.iter()
|
||||
.map(|name| CString::new(name.clone()).ok())
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.unwrap_or_default();
|
||||
let mut $name = $name.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
|
||||
let mut $name_ptr = ptr::null();
|
||||
if !$name.is_empty() {
|
||||
$name.push(ptr::null());
|
||||
$name_ptr = $name.as_ptr();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
vec2cstring_ptr!(mutable_globals, mutable_globals_ptr);
|
||||
vec2cstring_ptr!(userdata_types, userdata_types_ptr);
|
||||
|
||||
unsafe {
|
||||
let mut options = ffi::lua_CompileOptions::default();
|
||||
options.optimizationLevel = self.optimization_level as c_int;
|
||||
@@ -271,6 +284,7 @@ impl Compiler {
|
||||
options.vectorCtor = vector_ctor.map_or(ptr::null(), |s| s.as_ptr());
|
||||
options.vectorType = vector_type.map_or(ptr::null(), |s| s.as_ptr());
|
||||
options.mutableGlobals = mutable_globals_ptr;
|
||||
options.userdataTypes = userdata_types_ptr;
|
||||
ffi::luau_compile(source.as_ref(), options)
|
||||
}
|
||||
}
|
||||
@@ -327,7 +341,7 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||
///
|
||||
/// This is equivalent to calling the chunk function with no arguments and no return values.
|
||||
pub fn exec(self) -> Result<()> {
|
||||
self.call(())?;
|
||||
self.call::<_, ()>(())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
+5
-4
@@ -453,10 +453,11 @@ impl<'lua> IntoLua<'lua> for &RegistryKey {
|
||||
return Err(Error::MismatchedRegistryKey);
|
||||
}
|
||||
|
||||
if self.is_nil() {
|
||||
ffi::lua_pushnil(lua.state());
|
||||
} else {
|
||||
ffi::lua_rawgeti(lua.state(), ffi::LUA_REGISTRYINDEX, self.registry_id as _);
|
||||
match self.id() {
|
||||
ffi::LUA_REFNIL => ffi::lua_pushnil(lua.state()),
|
||||
id => {
|
||||
ffi::lua_rawgeti(lua.state(), ffi::LUA_REGISTRYINDEX, id as _);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ pub enum Error {
|
||||
/// [`Thread::resume`] was called on an inactive coroutine.
|
||||
///
|
||||
/// A coroutine is inactive if its main function has returned or if an error has occurred inside
|
||||
/// the coroutine.
|
||||
/// the coroutine. Already running coroutines are also marked as inactive (unresumable).
|
||||
///
|
||||
/// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this
|
||||
/// error.
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@
|
||||
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
#![doc(test(attr(warn(warnings))))] // FIXME: Remove this when rust-lang/rust#123748 is fixed
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
#[macro_use]
|
||||
|
||||
+37
-38
@@ -2049,7 +2049,7 @@ impl Lua {
|
||||
T: FromLua<'lua>,
|
||||
{
|
||||
let state = self.state();
|
||||
let value = unsafe {
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
@@ -2057,9 +2057,8 @@ impl Lua {
|
||||
push_string(state, name.as_bytes(), protect)?;
|
||||
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
||||
|
||||
self.pop_value()
|
||||
};
|
||||
T::from_lua(value, self)
|
||||
T::from_stack(-1, self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes a named value in the Lua registry.
|
||||
@@ -2082,22 +2081,21 @@ impl Lua {
|
||||
///
|
||||
/// [`RegistryKey`]: crate::RegistryKey
|
||||
pub fn create_registry_value<'lua, T: IntoLua<'lua>>(&'lua self, t: T) -> Result<RegistryKey> {
|
||||
let t = t.into_lua(self)?;
|
||||
if t == Value::Nil {
|
||||
// Special case to skip calling `luaL_ref` and use `LUA_REFNIL` instead
|
||||
let unref_list = unsafe { (*self.extra.get()).registry_unref_list.clone() };
|
||||
return Ok(RegistryKey::new(ffi::LUA_REFNIL, unref_list));
|
||||
}
|
||||
|
||||
let state = self.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 4)?;
|
||||
|
||||
self.push_value(t)?;
|
||||
self.push(t)?;
|
||||
|
||||
let unref_list = (*self.extra.get()).registry_unref_list.clone();
|
||||
|
||||
// Check if the value is nil (no need to store it in the registry)
|
||||
if ffi::lua_isnil(state, -1) != 0 {
|
||||
return Ok(RegistryKey::new(ffi::LUA_REFNIL, unref_list));
|
||||
}
|
||||
|
||||
// Try to reuse previously allocated slot
|
||||
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());
|
||||
@@ -2107,7 +2105,7 @@ impl Lua {
|
||||
return Ok(RegistryKey::new(registry_id, unref_list));
|
||||
}
|
||||
|
||||
// Allocate a new RegistryKey
|
||||
// Allocate a new RegistryKey slot
|
||||
let registry_id = if self.unlikely_memory_error() {
|
||||
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
|
||||
} else {
|
||||
@@ -2131,18 +2129,16 @@ impl Lua {
|
||||
}
|
||||
|
||||
let state = self.state();
|
||||
let value = match key.is_nil() {
|
||||
true => Value::Nil,
|
||||
false => unsafe {
|
||||
match key.id() {
|
||||
ffi::LUA_REFNIL => T::from_lua(Value::Nil, self),
|
||||
registry_id => unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 1)?;
|
||||
|
||||
let id = key.registry_id as Integer;
|
||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, id);
|
||||
self.pop_value()
|
||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, registry_id as Integer);
|
||||
T::from_stack(-1, self)
|
||||
},
|
||||
};
|
||||
T::from_lua(value, self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes a value from the Lua registry.
|
||||
@@ -2180,29 +2176,32 @@ impl Lua {
|
||||
}
|
||||
|
||||
let t = t.into_lua(self)?;
|
||||
if t == Value::Nil && key.is_nil() {
|
||||
// Nothing to replace
|
||||
return Ok(());
|
||||
} else if t != Value::Nil && key.registry_id == ffi::LUA_REFNIL {
|
||||
// We cannot update `LUA_REFNIL` slot
|
||||
return Err(Error::runtime("cannot replace nil value with non-nil"));
|
||||
}
|
||||
|
||||
let state = self.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 2)?;
|
||||
|
||||
let id = key.registry_id as Integer;
|
||||
if t == Value::Nil {
|
||||
self.push_value(Value::Integer(id))?;
|
||||
key.set_nil(true);
|
||||
} else {
|
||||
self.push_value(t)?;
|
||||
key.set_nil(false);
|
||||
match (t, key.id()) {
|
||||
(Value::Nil, ffi::LUA_REFNIL) => {
|
||||
// Do nothing, no need to replace nil with nil
|
||||
}
|
||||
(Value::Nil, registry_id) => {
|
||||
// Remove the value
|
||||
ffi::luaL_unref(state, ffi::LUA_REGISTRYINDEX, registry_id);
|
||||
key.set_id(ffi::LUA_REFNIL);
|
||||
}
|
||||
(value, ffi::LUA_REFNIL) => {
|
||||
// Allocate a new `RegistryKey`
|
||||
let new_key = self.create_registry_value(value)?;
|
||||
key.set_id(new_key.take());
|
||||
}
|
||||
(value, registry_id) => {
|
||||
// It must be safe to replace the value without triggering memory error
|
||||
self.push_value(value)?;
|
||||
ffi::lua_rawseti(state, ffi::LUA_REGISTRYINDEX, registry_id as Integer);
|
||||
}
|
||||
}
|
||||
// It must be safe to replace the value without triggering memory error
|
||||
ffi::lua_rawseti(state, ffi::LUA_REGISTRYINDEX, id);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
|
||||
use std::u32;
|
||||
|
||||
/// Flags describing the set of lua standard libraries to load.
|
||||
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
|
||||
+11
-4
@@ -142,6 +142,10 @@ impl<'lua> Thread<'lua> {
|
||||
A: IntoLuaMulti<'lua>,
|
||||
R: FromLuaMulti<'lua>,
|
||||
{
|
||||
if self.status() != ThreadStatus::Resumable {
|
||||
return Err(Error::CoroutineInactive);
|
||||
}
|
||||
|
||||
let lua = self.0.lua;
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
@@ -165,10 +169,6 @@ impl<'lua> Thread<'lua> {
|
||||
let state = lua.state();
|
||||
let thread_state = self.state();
|
||||
|
||||
if self.status() != ThreadStatus::Resumable {
|
||||
return Err(Error::CoroutineInactive);
|
||||
}
|
||||
|
||||
let nargs = args.push_into_stack_multi(lua)?;
|
||||
if nargs > 0 {
|
||||
check_stack(thread_state, nargs)?;
|
||||
@@ -196,6 +196,10 @@ impl<'lua> Thread<'lua> {
|
||||
/// Gets the status of the thread.
|
||||
pub fn status(&self) -> ThreadStatus {
|
||||
let thread_state = self.state();
|
||||
if thread_state == self.0.lua.state() {
|
||||
// The coroutine is currently running
|
||||
return ThreadStatus::Unresumable;
|
||||
}
|
||||
unsafe {
|
||||
let status = ffi::lua_status(thread_state);
|
||||
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
|
||||
@@ -243,6 +247,9 @@ impl<'lua> Thread<'lua> {
|
||||
pub fn reset(&self, func: crate::function::Function<'lua>) -> Result<()> {
|
||||
let lua = self.0.lua;
|
||||
let thread_state = self.state();
|
||||
if thread_state == lua.state() {
|
||||
return Err(Error::runtime("cannot reset a running thread"));
|
||||
}
|
||||
unsafe {
|
||||
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
|
||||
let status = ffi::lua_resetthread(thread_state);
|
||||
|
||||
+25
-29
@@ -4,7 +4,7 @@ use std::hash::{Hash, Hasher};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::result::Result as StdResult;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::atomic::{AtomicI32, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{fmt, mem, ptr};
|
||||
|
||||
@@ -204,26 +204,25 @@ pub(crate) struct DestructedUserdata;
|
||||
/// [`AnyUserData::set_user_value`]: crate::AnyUserData::set_user_value
|
||||
/// [`AnyUserData::user_value`]: crate::AnyUserData::user_value
|
||||
pub struct RegistryKey {
|
||||
pub(crate) registry_id: c_int,
|
||||
pub(crate) is_nil: AtomicBool,
|
||||
pub(crate) registry_id: AtomicI32,
|
||||
pub(crate) unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for RegistryKey {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "RegistryKey({})", self.registry_id)
|
||||
write!(f, "RegistryKey({})", self.id())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for RegistryKey {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.registry_id.hash(state)
|
||||
self.id().hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for RegistryKey {
|
||||
fn eq(&self, other: &RegistryKey) -> bool {
|
||||
self.registry_id == other.registry_id && Arc::ptr_eq(&self.unref_list, &other.unref_list)
|
||||
self.id() == other.id() && Arc::ptr_eq(&self.unref_list, &other.unref_list)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,50 +230,47 @@ impl Eq for RegistryKey {}
|
||||
|
||||
impl Drop for RegistryKey {
|
||||
fn drop(&mut self) {
|
||||
let registry_id = self.id();
|
||||
// We don't need to collect nil slot
|
||||
if self.registry_id > ffi::LUA_REFNIL {
|
||||
if registry_id > ffi::LUA_REFNIL {
|
||||
let mut unref_list = mlua_expect!(self.unref_list.lock(), "unref list poisoned");
|
||||
if let Some(list) = unref_list.as_mut() {
|
||||
list.push(self.registry_id);
|
||||
list.push(registry_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RegistryKey {
|
||||
// Creates a new instance of `RegistryKey`
|
||||
/// Creates a new instance of `RegistryKey`
|
||||
pub(crate) const fn new(id: c_int, unref_list: Arc<Mutex<Option<Vec<c_int>>>>) -> Self {
|
||||
RegistryKey {
|
||||
registry_id: id,
|
||||
is_nil: AtomicBool::new(id == ffi::LUA_REFNIL),
|
||||
registry_id: AtomicI32::new(id),
|
||||
unref_list,
|
||||
}
|
||||
}
|
||||
|
||||
// Destroys the `RegistryKey` without adding to the unref list
|
||||
pub(crate) fn take(self) -> c_int {
|
||||
let registry_id = self.registry_id;
|
||||
/// Returns the underlying Lua reference of this `RegistryKey`
|
||||
#[inline(always)]
|
||||
pub fn id(&self) -> c_int {
|
||||
self.registry_id.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Sets the unique Lua reference key of this `RegistryKey`
|
||||
#[inline(always)]
|
||||
pub(crate) fn set_id(&self, id: c_int) {
|
||||
self.registry_id.store(id, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Destroys the `RegistryKey` without adding to the unref list
|
||||
pub(crate) fn take(self) -> i32 {
|
||||
let registry_id = self.id();
|
||||
unsafe {
|
||||
ptr::read(&self.unref_list);
|
||||
mem::forget(self);
|
||||
}
|
||||
registry_id
|
||||
}
|
||||
|
||||
// Returns true if this `RegistryKey` holds a nil value
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_nil(&self) -> bool {
|
||||
self.is_nil.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
// Marks value of this `RegistryKey` as `Nil`
|
||||
#[inline(always)]
|
||||
pub(crate) fn set_nil(&self, enabled: bool) {
|
||||
// We cannot replace previous value with nil in as this will break
|
||||
// Lua mechanism to find free keys.
|
||||
// Instead, we set a special flag to mark value as nil.
|
||||
self.is_nil.store(enabled, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LuaRef<'lua> {
|
||||
|
||||
+4
-5
@@ -775,12 +775,11 @@ fn test_replace_registry_value() -> Result<()> {
|
||||
lua.replace_registry_value(&key, 123)?;
|
||||
assert_eq!(lua.registry_value::<i32>(&key)?, 123);
|
||||
|
||||
// It should be impossible to replace (initial) nil value with non-nil
|
||||
let key2 = lua.create_registry_value(Value::Nil)?;
|
||||
match lua.replace_registry_value(&key2, "abc") {
|
||||
Err(Error::RuntimeError(_)) => {}
|
||||
r => panic!("expected RuntimeError, got {r:?}"),
|
||||
}
|
||||
lua.replace_registry_value(&key2, Value::Nil)?;
|
||||
assert_eq!(lua.registry_value::<Value>(&key2)?, Value::Nil);
|
||||
lua.replace_registry_value(&key2, "abc")?;
|
||||
assert_eq!(lua.registry_value::<String>(&key2)?, "abc");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -90,6 +90,19 @@ fn test_thread() -> Result<()> {
|
||||
_ => panic!("resuming dead coroutine did not return error"),
|
||||
}
|
||||
|
||||
// Already running thread must be unresumable
|
||||
let thread = lua.create_thread(lua.create_function(|lua, ()| {
|
||||
assert_eq!(lua.current_thread().status(), ThreadStatus::Unresumable);
|
||||
let result = lua.current_thread().resume::<_, ()>(());
|
||||
assert!(
|
||||
matches!(result, Err(Error::CoroutineInactive)),
|
||||
"unexpected result: {result:?}",
|
||||
);
|
||||
Ok(())
|
||||
})?)?;
|
||||
let result = thread.resume::<_, ()>(());
|
||||
assert!(result.is_ok(), "unexpected result: {result:?}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -146,6 +159,21 @@ fn test_thread_reset() -> Result<()> {
|
||||
assert_eq!(thread.status(), ThreadStatus::Resumable);
|
||||
}
|
||||
|
||||
// Try reset running thread
|
||||
let thread = lua.create_thread(lua.create_function(|lua, ()| {
|
||||
let this = lua.current_thread();
|
||||
this.reset(lua.create_function(|_, ()| Ok(()))?)?;
|
||||
Ok(())
|
||||
})?)?;
|
||||
let result = thread.resume::<_, ()>(());
|
||||
assert!(
|
||||
matches!(result, Err(Error::CallbackError{ ref cause, ..})
|
||||
if matches!(cause.as_ref(), Error::RuntimeError(ref err)
|
||||
if err == "cannot reset a running thread")
|
||||
),
|
||||
"unexpected result: {result:?}",
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user