mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c926327a6a | |||
| 7c099500d0 | |||
| 8c889cc353 | |||
| 958abd050e | |||
| 92a8203e1c | |||
| b34b90eca3 | |||
| 7aad0adcb4 | |||
| a3cd25db7a | |||
| a4bfeb7752 | |||
| 0fda512938 | |||
| 58e0661086 | |||
| c7094d470f | |||
| a7d0691e10 | |||
| 05778fbe6f | |||
| b34d67ec41 | |||
| 46ee7ea772 | |||
| 15738dda1f | |||
| 1f32754f05 | |||
| c2eab173c5 | |||
| bb311349ec | |||
| 928e1d9221 | |||
| 5b8681dcf2 | |||
| 4e9a17707b | |||
| 6066089cc1 | |||
| a8d5f23818 | |||
| 5ec4e0338a | |||
| d27d1365b5 | |||
| 76b896edcc | |||
| ddebf56b41 |
+12
-12
@@ -27,8 +27,8 @@ jobs:
|
||||
- name: Build ${{ matrix.lua }} vendored
|
||||
run: |
|
||||
cargo build --features "${{ matrix.lua }},vendored"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,send"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
cargo build --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send"
|
||||
shell: bash
|
||||
- name: Build ${{ matrix.lua }} pkg-config
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
toolchain: stable
|
||||
target: aarch64-apple-darwin
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
run: cargo build --target aarch64-apple-darwin --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
|
||||
build_aarch64_cross_ubuntu:
|
||||
name: Cross-compile to aarch64-unknown-linux-gnu
|
||||
@@ -72,7 +72,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
run: cargo build --target aarch64-unknown-linux-gnu --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
|
||||
build_armv7_cross_ubuntu:
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
sudo apt-get install -y --no-install-recommends gcc-arm-linux-gnueabihf libc-dev-armhf-cross
|
||||
shell: bash
|
||||
- name: Cross-compile
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
run: cargo build --target armv7-unknown-linux-gnueabihf --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
|
||||
test:
|
||||
@@ -123,8 +123,8 @@ jobs:
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --features "${{ matrix.lua }},vendored"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,send"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
cargo test --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send"
|
||||
shell: bash
|
||||
- name: Run compile tests (macos lua54)
|
||||
if: ${{ matrix.os == 'macos-latest' && matrix.lua == 'lua54' }}
|
||||
@@ -154,8 +154,8 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} tests with address sanitizer
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,send" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers,send" --target x86_64-unknown-linux-gnu -- --skip test_too_many_recursions
|
||||
shell: bash
|
||||
env:
|
||||
RUSTFLAGS: -Z sanitizer=address
|
||||
@@ -181,7 +181,7 @@ jobs:
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run ${{ matrix.lua }} tests with forced memory limit
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
shell: bash
|
||||
env:
|
||||
RUSTFLAGS: --cfg=force_memory_limit
|
||||
@@ -254,7 +254,7 @@ jobs:
|
||||
- name: Run ${{ matrix.lua }} tests
|
||||
run: |
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros"
|
||||
cargo test --tests --features "${{ matrix.lua }},vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
|
||||
rustfmt:
|
||||
name: Rustfmt
|
||||
@@ -281,4 +281,4 @@ jobs:
|
||||
- uses: giraffate/clippy-action@v1
|
||||
with:
|
||||
reporter: 'github-pr-review'
|
||||
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow"
|
||||
clippy_flags: --features "${{ matrix.lua }},vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
## v0.10.1 (Nov 9th, 2024)
|
||||
|
||||
- Minimal Luau updated to 0.650
|
||||
- Added Luau native vector library support (this can change behavior if you use `vector` function!)
|
||||
- Added Lua `String::display` method
|
||||
- Improved pretty-printing for Lua tables (#478)
|
||||
- Added `Scope::create_any_userdata` to create Lua objects from any non-`'static` Rust types
|
||||
- Added `AnyUserData::destroy` method
|
||||
- New `userdata-wrappers` feature to `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` (similar to v0.9)
|
||||
- `UserDataRef` in `send` mode now uses shared lock if `T: Sync` (and exclusive lock otherwise)
|
||||
- Added `Scope::add_destructor` to attach custom destructors
|
||||
- Added `Lua::try_app_data_ref` and `Lua::try_app_data_mut` methods
|
||||
- Added `From<Vec>` and `Into<Vec>` support to `MultiValue` and `Variadic` types
|
||||
- Bug fixes and improvements (#477 #479)
|
||||
|
||||
## v0.10.0 (Oct 25th, 2024)
|
||||
|
||||
Changes since v0.10.0-rc.1
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua"
|
||||
version = "0.10.0" # remember to update mlua_derive
|
||||
version = "0.10.1" # remember to update mlua_derive
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@kyju.org>"]
|
||||
rust-version = "1.79.0"
|
||||
edition = "2021"
|
||||
@@ -43,6 +43,7 @@ error-send = []
|
||||
serialize = ["dep:serde", "dep:erased-serde", "dep:serde-value"]
|
||||
macros = ["mlua_derive/macros"]
|
||||
anyhow = ["dep:anyhow", "error-send"]
|
||||
userdata-wrappers = []
|
||||
|
||||
[dependencies]
|
||||
mlua_derive = { version = "=0.10.0", optional = true, path = "mlua_derive" }
|
||||
@@ -57,7 +58,7 @@ serde-value = { version = "0.7", optional = true }
|
||||
parking_lot = { version = "0.12", features = ["arc_lock"] }
|
||||
anyhow = { version = "1.0", optional = true }
|
||||
|
||||
ffi = { package = "mlua-sys", version = "0.6.4", path = "mlua-sys" }
|
||||
ffi = { package = "mlua-sys", version = "0.6.5", path = "mlua-sys" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libloading = { version = "0.8", optional = true }
|
||||
|
||||
@@ -31,7 +31,7 @@ Started as `rlua` fork, `mlua` supports Lua 5.4, 5.3, 5.2, 5.1 (including LuaJIT
|
||||
WebAssembly (WASM) is supported through `wasm32-unknown-emscripten` target for all Lua versions excluding JIT.
|
||||
|
||||
[GitHub Actions]: https://github.com/khvzak/mlua/actions
|
||||
[Roblox Luau]: https://luau-lang.org
|
||||
[Roblox Luau]: https://luau.org
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -57,6 +57,7 @@ Below is a list of the available feature flags. By default `mlua` does not enabl
|
||||
* `serialize`: add serialization and deserialization support to `mlua` types using [serde] framework
|
||||
* `macros`: enable procedural macros (such as `chunk!`)
|
||||
* `anyhow`: enable `anyhow::Error` conversion into Lua
|
||||
* `userdata-wrappers`: opt into `impl UserData` for `Rc<T>`/`Arc<T>`/`Rc<RefCell<T>>`/`Arc<Mutex<T>>` where `T: UserData`
|
||||
|
||||
[5.4]: https://www.lua.org/manual/5.4/manual.html
|
||||
[5.3]: https://www.lua.org/manual/5.3/manual.html
|
||||
@@ -132,7 +133,7 @@ Add to `Cargo.toml` :
|
||||
|
||||
``` toml
|
||||
[dependencies]
|
||||
mlua = { version = "0.10.0", features = ["lua54", "vendored"] }
|
||||
mlua = { version = "0.10.1", features = ["lua54", "vendored"] }
|
||||
```
|
||||
|
||||
`main.rs`
|
||||
@@ -167,7 +168,7 @@ Add to `Cargo.toml` :
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
mlua = { version = "0.10.0", features = ["lua54", "module"] }
|
||||
mlua = { version = "0.10.1", features = ["lua54", "module"] }
|
||||
```
|
||||
|
||||
`lib.rs` :
|
||||
@@ -290,7 +291,7 @@ Please check the [Luau Sandboxing] page if you are interested in running untrust
|
||||
|
||||
`mlua` provides `Lua::sandbox` method for enabling sandbox mode (Luau only).
|
||||
|
||||
[Luau Sandboxing]: https://luau-lang.org/sandbox
|
||||
[Luau Sandboxing]: https://luau.org/sandbox
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -132,3 +132,64 @@ let f = lua.create_function(|_, ()| {
|
||||
Ok(())
|
||||
})?;
|
||||
```
|
||||
|
||||
### Breaking changes
|
||||
|
||||
#### Scope changes
|
||||
|
||||
The following `Scope` methods were changed:
|
||||
- Removed `Scope::create_any_userdata`
|
||||
- `Scope::create_nonstatic_userdata` is renamed to `Scope::create_userdata`
|
||||
|
||||
Instead, scope has comprehensive support for borrowed userdata: `create_any_userdata_ref`, `create_any_userdata_ref_mut`, `create_userdata_ref`, `create_userdata_ref_mut`.
|
||||
|
||||
`UserDataRef` and `UserDataRefMut` are no longer acceptable for scoped userdata access as they require owned underlying data.
|
||||
In mlua v0.9 this can cause read-after-free bug in some edge cases.
|
||||
|
||||
To temporarily borrow underlying data, the `AnyUserData::borrow_scoped` and `AnyUserData::borrow_mut_scoped` methods were introduced:
|
||||
|
||||
```rust
|
||||
let data = "hello".to_string();
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_any_userdata_ref(&data)?;
|
||||
|
||||
// We can only borrow scoped userdata using this method
|
||||
ud.borrow_scoped::<String, ()>(|s| {
|
||||
assert_eq!(s, "hello");
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
```
|
||||
|
||||
Those methods work for scoped and regular userdata objects (but still require `T: 'static`).
|
||||
|
||||
#### String changes
|
||||
|
||||
Since `mlua::String` holds a weak reference to Lua without any guarantees about the lifetime of the underlying data, getting a `&str` or `&[u8]` from it is no longer safe.
|
||||
Lua instance can be destroyed while reference to the data is still alive:
|
||||
|
||||
```rust
|
||||
let lua = Lua::new();
|
||||
let s: mlua::String = lua.create_string("hello, world")?; // only weak reference to Lua!
|
||||
let s_ref: &str = s.to_str()?; // this is not safe!
|
||||
drop(lua);
|
||||
println!("{s_ref}"); // use after free!
|
||||
```
|
||||
|
||||
To solve this issue, return types of `mlua::String::to_str` and `mlua::String::as_bytes` methods changed to `BorrowedStr` and `BorrowedBytes` respectively.
|
||||
|
||||
These new types hold a strong reference to the Lua instance and can be safely converted to `&str` or `&[u8]`:
|
||||
|
||||
```rust
|
||||
let lua = Lua::new();
|
||||
let s: mlua::String = lua.create_string("hello, world")?;
|
||||
let s_ref: mlua::BorrowedStr = s.to_str()?; // The strong reference to Lua is held here
|
||||
drop(lua);
|
||||
println!("{s_ref}"); // ok
|
||||
```
|
||||
|
||||
The good news is that `BorrowedStr` implements `Deref<Target = str>`/`AsRef<str>` as well as `Display`, `Debug`, `Eq`, `PartialEq` and other traits for easy usage.
|
||||
The same applies to `BorrowedBytes`.
|
||||
|
||||
Unfortunately, `mlua::String::to_string_lossy` cannot return `Cow<'a, str>` anymore, because it requires a strong reference to Lua. It now returns Rust `String` instead.
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "mlua-sys"
|
||||
version = "0.6.4"
|
||||
version = "0.6.5"
|
||||
authors = ["Aleksandr Orlenko <zxteam@pm.me>"]
|
||||
rust-version = "1.71"
|
||||
edition = "2021"
|
||||
@@ -40,7 +40,7 @@ cfg-if = "1.0"
|
||||
pkg-config = "0.3.17"
|
||||
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.11.0", optional = true }
|
||||
luau0-src = { version = "0.11.1", optional = true }
|
||||
|
||||
[lints.rust]
|
||||
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(raw_dylib)'] }
|
||||
|
||||
@@ -185,6 +185,7 @@ extern "C-unwind" {
|
||||
|
||||
pub fn lua_pushlightuserdatatagged(L: *mut lua_State, p: *mut c_void, tag: c_int);
|
||||
pub fn lua_newuserdatatagged(L: *mut lua_State, sz: usize, tag: c_int) -> *mut c_void;
|
||||
pub fn lua_newuserdatataggedwithmetatable(L: *mut lua_State, sz: usize, tag: c_int) -> *mut c_void;
|
||||
pub fn lua_newuserdatadtor(L: *mut lua_State, sz: usize, dtor: lua_Udestructor) -> *mut c_void;
|
||||
|
||||
pub fn lua_newbuffer(L: *mut lua_State, sz: usize) -> *mut c_void;
|
||||
|
||||
@@ -13,6 +13,7 @@ pub const LUA_BUFFERLIBNAME: &str = "buffer";
|
||||
pub const LUA_UTF8LIBNAME: &str = "utf8";
|
||||
pub const LUA_MATHLIBNAME: &str = "math";
|
||||
pub const LUA_DBLIBNAME: &str = "debug";
|
||||
pub const LUA_VECLIBNAME: &str = "vector";
|
||||
|
||||
extern "C-unwind" {
|
||||
pub fn luaopen_base(L: *mut lua_State) -> c_int;
|
||||
@@ -25,6 +26,7 @@ extern "C-unwind" {
|
||||
pub fn luaopen_utf8(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_math(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
|
||||
pub fn luaopen_vector(L: *mut lua_State) -> c_int;
|
||||
|
||||
// open all builtin libraries
|
||||
pub fn luaL_openlibs(L: *mut lua_State);
|
||||
|
||||
+1
-1
@@ -301,7 +301,7 @@ impl Compiler {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Chunk<'a> {
|
||||
impl Chunk<'_> {
|
||||
/// Sets the name of this chunk, which results in more informative error traces.
|
||||
pub fn set_name(mut self, name: impl Into<String>) -> Self {
|
||||
self.name = name.into();
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
// Deny warnings inside doc tests / examples. When this isn't present, rustdoc doesn't show *any*
|
||||
// warnings at all.
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(not(send), allow(clippy::arc_with_non_send_sync))]
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
+1
-17
@@ -1,5 +1,5 @@
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_float, c_int};
|
||||
use std::os::raw::c_int;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::state::Lua;
|
||||
@@ -11,7 +11,6 @@ impl Lua {
|
||||
let globals = self.globals();
|
||||
|
||||
globals.raw_set("collectgarbage", self.create_c_function(lua_collectgarbage)?)?;
|
||||
globals.raw_set("vector", self.create_c_function(lua_vector)?)?;
|
||||
|
||||
// Set `_VERSION` global to include version number
|
||||
// The environment variable `LUAU_VERSION` set by the build script
|
||||
@@ -65,21 +64,6 @@ unsafe extern "C-unwind" fn lua_collectgarbage(state: *mut ffi::lua_State) -> c_
|
||||
}
|
||||
}
|
||||
|
||||
// Luau vector datatype constructor
|
||||
unsafe extern "C-unwind" fn lua_vector(state: *mut ffi::lua_State) -> c_int {
|
||||
let x = ffi::luaL_checknumber(state, 1) as c_float;
|
||||
let y = ffi::luaL_checknumber(state, 2) as c_float;
|
||||
let z = ffi::luaL_checknumber(state, 3) as c_float;
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
let w = ffi::luaL_checknumber(state, 4) as c_float;
|
||||
|
||||
#[cfg(not(feature = "luau-vector4"))]
|
||||
ffi::lua_pushvector(state, x, y, z);
|
||||
#[cfg(feature = "luau-vector4")]
|
||||
ffi::lua_pushvector(state, x, y, z, w);
|
||||
1
|
||||
}
|
||||
|
||||
pub(crate) use package::register_package_module;
|
||||
|
||||
mod package;
|
||||
|
||||
+62
-18
@@ -124,6 +124,23 @@ impl MultiValue {
|
||||
MultiValue(VecDeque::with_capacity(capacity))
|
||||
}
|
||||
|
||||
/// Creates a `MultiValue` container from vector of values.
|
||||
///
|
||||
/// This methods needs *O*(*n*) data movement if the circular buffer doesn't happen to be at the
|
||||
/// beginning of the allocation.
|
||||
#[inline]
|
||||
pub fn from_vec(vec: Vec<Value>) -> MultiValue {
|
||||
vec.into()
|
||||
}
|
||||
|
||||
/// Consumes the `MultiValue` and returns a vector of values.
|
||||
///
|
||||
/// This methods works in *O*(1) time and does not allocate any additional memory.
|
||||
#[inline]
|
||||
pub fn into_vec(self) -> Vec<Value> {
|
||||
self.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn from_lua_iter<T: IntoLua>(lua: &Lua, iter: impl IntoIterator<Item = T>) -> Result<Self> {
|
||||
let iter = iter.into_iter();
|
||||
@@ -135,6 +152,20 @@ impl MultiValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Value>> for MultiValue {
|
||||
#[inline]
|
||||
fn from(value: Vec<Value>) -> Self {
|
||||
MultiValue(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultiValue> for Vec<Value> {
|
||||
#[inline]
|
||||
fn from(value: MultiValue) -> Self {
|
||||
value.0.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<Value> for MultiValue {
|
||||
#[inline]
|
||||
fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
|
||||
@@ -203,7 +234,7 @@ impl FromLuaMulti for MultiValue {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct Variadic<T>(Vec<T>);
|
||||
|
||||
impl<T> Variadic<T> {
|
||||
@@ -211,11 +242,38 @@ impl<T> Variadic<T> {
|
||||
pub const fn new() -> Variadic<T> {
|
||||
Variadic(Vec::new())
|
||||
}
|
||||
|
||||
/// Creates an empty `Variadic` container with space for at least `capacity` elements.
|
||||
pub fn with_capacity(capacity: usize) -> Variadic<T> {
|
||||
Variadic(Vec::with_capacity(capacity))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Variadic<T> {
|
||||
fn default() -> Variadic<T> {
|
||||
const { Variadic::new() }
|
||||
impl<T> Deref for Variadic<T> {
|
||||
type Target = Vec<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Variadic<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Vec<T>> for Variadic<T> {
|
||||
#[inline]
|
||||
fn from(vec: Vec<T>) -> Self {
|
||||
Variadic(vec)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Variadic<T>> for Vec<T> {
|
||||
#[inline]
|
||||
fn from(value: Variadic<T>) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,20 +292,6 @@ impl<T> IntoIterator for Variadic<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Variadic<T> {
|
||||
type Target = Vec<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Variadic<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IntoLua> IntoLuaMulti for Variadic<T> {
|
||||
#[inline]
|
||||
fn into_lua_multi(self, lua: &Lua) -> Result<MultiValue> {
|
||||
|
||||
+111
-49
@@ -1,7 +1,6 @@
|
||||
use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
use crate::function::Function;
|
||||
@@ -19,7 +18,9 @@ use crate::util::{
|
||||
/// See [`Lua::scope`] for more details.
|
||||
pub struct Scope<'scope, 'env: 'scope> {
|
||||
lua: LuaGuard,
|
||||
// Internal destructors run first, then user destructors (based on the declaration order)
|
||||
destructors: Destructors<'env>,
|
||||
user_destructors: UserDestructors<'env>,
|
||||
_scope_invariant: PhantomData<&'scope mut &'scope ()>,
|
||||
_env_invariant: PhantomData<&'env mut &'env ()>,
|
||||
}
|
||||
@@ -29,11 +30,14 @@ type DestructorCallback<'a> = Box<dyn FnOnce(&RawLua, ValueRef) -> Vec<Box<dyn F
|
||||
// Implement Drop on Destructors instead of Scope to avoid compilation error
|
||||
struct Destructors<'a>(RefCell<Vec<(ValueRef, DestructorCallback<'a>)>>);
|
||||
|
||||
struct UserDestructors<'a>(RefCell<Vec<Box<dyn FnOnce() + 'a>>>);
|
||||
|
||||
impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
pub(crate) fn new(lua: LuaGuard) -> Self {
|
||||
Scope {
|
||||
lua,
|
||||
destructors: Destructors(RefCell::new(Vec::new())),
|
||||
user_destructors: UserDestructors(RefCell::new(Vec::new())),
|
||||
_scope_invariant: PhantomData,
|
||||
_env_invariant: PhantomData,
|
||||
}
|
||||
@@ -84,11 +88,9 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
where
|
||||
T: UserData + 'static,
|
||||
{
|
||||
unsafe {
|
||||
let ud = self.lua.make_userdata(UserDataStorage::new_ref(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
let ud = unsafe { self.lua.make_userdata(UserDataStorage::new_ref(data)) }?;
|
||||
self.seal_userdata::<T>(&ud);
|
||||
Ok(ud)
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a mutable reference to custom userdata type.
|
||||
@@ -100,11 +102,9 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
where
|
||||
T: UserData + 'static,
|
||||
{
|
||||
unsafe {
|
||||
let ud = self.lua.make_userdata(UserDataStorage::new_ref_mut(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
let ud = unsafe { self.lua.make_userdata(UserDataStorage::new_ref_mut(data)) }?;
|
||||
self.seal_userdata::<T>(&ud);
|
||||
Ok(ud)
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a reference to custom Rust type.
|
||||
@@ -118,11 +118,9 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
unsafe {
|
||||
let ud = self.lua.make_any_userdata(UserDataStorage::new_ref(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
let ud = unsafe { self.lua.make_any_userdata(UserDataStorage::new_ref(data)) }?;
|
||||
self.seal_userdata::<T>(&ud);
|
||||
Ok(ud)
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a mutable reference to custom Rust type.
|
||||
@@ -134,11 +132,9 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
unsafe {
|
||||
let ud = self.lua.make_any_userdata(UserDataStorage::new_ref_mut(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
let ud = unsafe { self.lua.make_any_userdata(UserDataStorage::new_ref_mut(data)) }?;
|
||||
self.seal_userdata::<T>(&ud);
|
||||
Ok(ud)
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a custom userdata type.
|
||||
@@ -167,7 +163,7 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
// // We don't write the data to the userdata until pushing the metatable
|
||||
// We don't write the data to the userdata until pushing the metatable
|
||||
let protect = !self.lua.unlikely_memory_error();
|
||||
#[cfg(feature = "luau")]
|
||||
let ud_ptr = {
|
||||
@@ -178,7 +174,7 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
let ud_ptr = util::push_uninit_userdata::<UserDataStorage<T>>(state, protect)?;
|
||||
|
||||
// Push the metatable and register it with no TypeId
|
||||
let mut registry = UserDataRegistry::new_unique(ud_ptr as *const c_void);
|
||||
let mut registry = UserDataRegistry::new_unique(ud_ptr as *mut _);
|
||||
T::register(&mut registry);
|
||||
self.lua.push_userdata_metatable(registry)?;
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
@@ -190,31 +186,84 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
let ud = AnyUserData(self.lua.pop_ref());
|
||||
|
||||
let destructor: DestructorCallback = Box::new(|rawlua, vref| {
|
||||
let state = rawlua.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
// Check that userdata is valid (very likely)
|
||||
if rawlua.push_userdata_ref(&vref).is_err() {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
// Deregister metatable
|
||||
let mt_ptr = get_metatable_ptr(state, -1);
|
||||
rawlua.deregister_userdata_metatable(mt_ptr);
|
||||
|
||||
let ud = take_userdata::<UserDataStorage<T>>(state);
|
||||
|
||||
vec![Box::new(move || drop(ud))]
|
||||
});
|
||||
self.destructors.0.borrow_mut().push((ud.0.clone(), destructor));
|
||||
self.seal_userdata::<T>(&ud);
|
||||
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a Lua userdata object from a custom Rust type.
|
||||
///
|
||||
/// Since the Rust type is not required to be static and implement [`UserData`] trait,
|
||||
/// you need to provide a function to register fields or methods for the object.
|
||||
///
|
||||
/// See also [`Scope::create_userdata`] for more details about non-static limitations.
|
||||
pub fn create_any_userdata<T>(
|
||||
&'scope self,
|
||||
data: T,
|
||||
register: impl FnOnce(&mut UserDataRegistry<T>),
|
||||
) -> Result<AnyUserData>
|
||||
where
|
||||
T: 'env,
|
||||
{
|
||||
let state = self.lua.state();
|
||||
let ud = unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
// We don't write the data to the userdata until pushing the metatable
|
||||
let protect = !self.lua.unlikely_memory_error();
|
||||
#[cfg(feature = "luau")]
|
||||
let ud_ptr = {
|
||||
let data = UserDataStorage::new_scoped(data);
|
||||
util::push_userdata::<UserDataStorage<T>>(state, data, protect)?
|
||||
};
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let ud_ptr = util::push_uninit_userdata::<UserDataStorage<T>>(state, protect)?;
|
||||
|
||||
// Push the metatable and register it with no TypeId
|
||||
let mut registry = UserDataRegistry::new_unique(ud_ptr as *mut _);
|
||||
register(&mut registry);
|
||||
self.lua.push_userdata_metatable(registry)?;
|
||||
let mt_ptr = ffi::lua_topointer(state, -1);
|
||||
self.lua.register_userdata_metatable(mt_ptr, None);
|
||||
|
||||
// Write data to the pointer and attach metatable
|
||||
#[cfg(not(feature = "luau"))]
|
||||
std::ptr::write(ud_ptr, UserDataStorage::new_scoped(data));
|
||||
ffi::lua_setmetatable(state, -2);
|
||||
|
||||
AnyUserData(self.lua.pop_ref())
|
||||
};
|
||||
self.seal_userdata::<T>(&ud);
|
||||
Ok(ud)
|
||||
}
|
||||
|
||||
/// Adds a destructor function to be run when the scope ends.
|
||||
///
|
||||
/// This functionality is useful for cleaning up any resources after the scope ends.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use mlua::{Error, Lua, Result};
|
||||
/// # fn main() -> Result<()> {
|
||||
/// let lua = Lua::new();
|
||||
/// let ud = lua.create_any_userdata(String::from("hello"))?;
|
||||
/// lua.scope(|scope| {
|
||||
/// scope.add_destructor(|| {
|
||||
/// _ = ud.take::<String>();
|
||||
/// });
|
||||
/// // Run the code that uses `ud` here
|
||||
/// Ok(())
|
||||
/// })?;
|
||||
/// assert!(matches!(ud.borrow::<String>(), Err(Error::UserDataDestructed)));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
pub fn add_destructor(&'scope self, destructor: impl FnOnce() + 'env) {
|
||||
self.user_destructors.0.borrow_mut().push(Box::new(destructor));
|
||||
}
|
||||
|
||||
unsafe fn create_callback(&'scope self, f: ScopedCallback<'scope>) -> Result<Function> {
|
||||
let f = mem::transmute::<ScopedCallback, Callback>(f);
|
||||
let f = self.lua.create_callback(f)?;
|
||||
@@ -233,23 +282,27 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
|
||||
}
|
||||
|
||||
/// Shortens the lifetime of the userdata to the lifetime of the scope.
|
||||
unsafe fn seal_userdata<T: 'static>(&self, ud: &AnyUserData) -> Result<()> {
|
||||
let destructor: DestructorCallback = Box::new(|rawlua, vref| {
|
||||
fn seal_userdata<T: 'env>(&self, ud: &AnyUserData) {
|
||||
let destructor: DestructorCallback = Box::new(|rawlua, vref| unsafe {
|
||||
let state = rawlua.state();
|
||||
let _sg = StackGuard::new(state);
|
||||
assert_stack(state, 2);
|
||||
|
||||
// Ensure that userdata is not destructed
|
||||
if rawlua.push_userdata_ref(&vref).is_err() {
|
||||
return vec![];
|
||||
match rawlua.push_userdata_ref(&vref) {
|
||||
Ok(Some(_)) => {}
|
||||
Ok(None) => {
|
||||
// Deregister metatable
|
||||
let mt_ptr = get_metatable_ptr(state, -1);
|
||||
rawlua.deregister_userdata_metatable(mt_ptr);
|
||||
}
|
||||
Err(_) => return vec![],
|
||||
}
|
||||
|
||||
let data = take_userdata::<UserDataStorage<T>>(state);
|
||||
vec![Box::new(move || drop(data))]
|
||||
});
|
||||
self.destructors.0.borrow_mut().push((ud.0.clone(), destructor));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,3 +324,12 @@ impl Drop for Destructors<'_> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for UserDestructors<'_> {
|
||||
fn drop(&mut self) {
|
||||
let destructors = mem::take(&mut *self.0.borrow_mut());
|
||||
for destructor in destructors {
|
||||
destructor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+47
-27
@@ -1,5 +1,5 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::RefCell;
|
||||
use std::cell::{BorrowError, BorrowMutError, RefCell};
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::c_int;
|
||||
@@ -485,7 +485,7 @@ impl Lua {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
if (*lua.extra.get()).sandboxed != enabled {
|
||||
let state = lua.main_state;
|
||||
let state = lua.main_state();
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 0, |state| {
|
||||
if enabled {
|
||||
@@ -562,10 +562,10 @@ impl Lua {
|
||||
unsafe {
|
||||
let state = lua.state();
|
||||
ffi::lua_sethook(state, None, 0, 0);
|
||||
match crate::util::get_main_state(lua.main_state) {
|
||||
Some(main_state) if !ptr::eq(state, main_state) => {
|
||||
match lua.main_state {
|
||||
Some(main_state) if state != main_state.as_ptr() => {
|
||||
// If main_state is different from state, remove hook from it too
|
||||
ffi::lua_sethook(main_state, None, 0, 0);
|
||||
ffi::lua_sethook(main_state.as_ptr(), None, 0, 0);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
@@ -654,7 +654,7 @@ impl Lua {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
(*lua.extra.get()).interrupt_callback = Some(Rc::new(callback));
|
||||
(*ffi::lua_callbacks(lua.main_state)).interrupt = Some(interrupt_proc);
|
||||
(*ffi::lua_callbacks(lua.main_state())).interrupt = Some(interrupt_proc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -667,7 +667,7 @@ impl Lua {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
(*lua.extra.get()).interrupt_callback = None;
|
||||
(*ffi::lua_callbacks(lua.main_state)).interrupt = None;
|
||||
(*ffi::lua_callbacks(lua.main_state())).interrupt = None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -697,10 +697,9 @@ impl Lua {
|
||||
}
|
||||
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state;
|
||||
unsafe {
|
||||
(*lua.extra.get()).warn_callback = Some(Box::new(callback));
|
||||
ffi::lua_setwarnf(state, Some(warn_proc), lua.extra.get() as *mut c_void);
|
||||
ffi::lua_setwarnf(lua.state(), Some(warn_proc), lua.extra.get() as *mut c_void);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -715,7 +714,7 @@ impl Lua {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
(*lua.extra.get()).warn_callback = None;
|
||||
ffi::lua_setwarnf(lua.main_state, None, ptr::null_mut());
|
||||
ffi::lua_setwarnf(lua.state(), None, ptr::null_mut());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -767,13 +766,14 @@ impl Lua {
|
||||
/// Returns the amount of memory (in bytes) currently used inside this Lua state.
|
||||
pub fn used_memory(&self) -> usize {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
unsafe {
|
||||
match MemoryState::get(lua.main_state) {
|
||||
match MemoryState::get(state) {
|
||||
mem_state if !mem_state.is_null() => (*mem_state).used_memory(),
|
||||
_ => {
|
||||
// Get data from the Lua GC
|
||||
let used_kbytes = ffi::lua_gc(lua.main_state, ffi::LUA_GCCOUNT, 0);
|
||||
let used_kbytes_rem = ffi::lua_gc(lua.main_state, ffi::LUA_GCCOUNTB, 0);
|
||||
let used_kbytes = ffi::lua_gc(state, ffi::LUA_GCCOUNT, 0);
|
||||
let used_kbytes_rem = ffi::lua_gc(state, ffi::LUA_GCCOUNTB, 0);
|
||||
(used_kbytes as usize) * 1024 + (used_kbytes_rem as usize)
|
||||
}
|
||||
}
|
||||
@@ -790,7 +790,7 @@ impl Lua {
|
||||
pub fn set_memory_limit(&self, limit: usize) -> Result<usize> {
|
||||
let lua = self.lock();
|
||||
unsafe {
|
||||
match MemoryState::get(lua.main_state) {
|
||||
match MemoryState::get(lua.state()) {
|
||||
mem_state if !mem_state.is_null() => Ok((*mem_state).set_memory_limit(limit)),
|
||||
_ => Err(Error::MemoryControlNotAvailable),
|
||||
}
|
||||
@@ -803,19 +803,19 @@ impl Lua {
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
pub fn gc_is_running(&self) -> bool {
|
||||
let lua = self.lock();
|
||||
unsafe { ffi::lua_gc(lua.main_state, ffi::LUA_GCISRUNNING, 0) != 0 }
|
||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCISRUNNING, 0) != 0 }
|
||||
}
|
||||
|
||||
/// Stop the Lua GC from running
|
||||
pub fn gc_stop(&self) {
|
||||
let lua = self.lock();
|
||||
unsafe { ffi::lua_gc(lua.main_state, ffi::LUA_GCSTOP, 0) };
|
||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCSTOP, 0) };
|
||||
}
|
||||
|
||||
/// Restarts the Lua GC if it is not running
|
||||
pub fn gc_restart(&self) {
|
||||
let lua = self.lock();
|
||||
unsafe { ffi::lua_gc(lua.main_state, ffi::LUA_GCRESTART, 0) };
|
||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCRESTART, 0) };
|
||||
}
|
||||
|
||||
/// Perform a full garbage-collection cycle.
|
||||
@@ -824,9 +824,10 @@ impl Lua {
|
||||
/// objects. Once to finish the current gc cycle, and once to start and finish the next cycle.
|
||||
pub fn gc_collect(&self) -> Result<()> {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
unsafe {
|
||||
check_stack(lua.main_state, 2)?;
|
||||
protect_lua!(lua.main_state, 0, 0, fn(state) ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0))
|
||||
check_stack(state, 2)?;
|
||||
protect_lua!(state, 0, 0, fn(state) ffi::lua_gc(state, ffi::LUA_GCCOLLECT, 0))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -843,9 +844,10 @@ impl Lua {
|
||||
/// finished a collection cycle.
|
||||
pub fn gc_step_kbytes(&self, kbytes: c_int) -> Result<bool> {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
unsafe {
|
||||
check_stack(lua.main_state, 3)?;
|
||||
protect_lua!(lua.main_state, 0, 0, |state| {
|
||||
check_stack(state, 3)?;
|
||||
protect_lua!(state, 0, 0, |state| {
|
||||
ffi::lua_gc(state, ffi::LUA_GCSTEP, kbytes) != 0
|
||||
})
|
||||
}
|
||||
@@ -861,11 +863,12 @@ impl Lua {
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5
|
||||
pub fn gc_set_pause(&self, pause: c_int) -> c_int {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state();
|
||||
unsafe {
|
||||
#[cfg(not(feature = "luau"))]
|
||||
return ffi::lua_gc(lua.main_state, ffi::LUA_GCSETPAUSE, pause);
|
||||
return ffi::lua_gc(state, ffi::LUA_GCSETPAUSE, pause);
|
||||
#[cfg(feature = "luau")]
|
||||
return ffi::lua_gc(lua.main_state, ffi::LUA_GCSETGOAL, pause);
|
||||
return ffi::lua_gc(state, ffi::LUA_GCSETGOAL, pause);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -877,7 +880,7 @@ impl Lua {
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5
|
||||
pub fn gc_set_step_multiplier(&self, step_multiplier: c_int) -> c_int {
|
||||
let lua = self.lock();
|
||||
unsafe { ffi::lua_gc(lua.main_state, ffi::LUA_GCSETSTEPMUL, step_multiplier) }
|
||||
unsafe { ffi::lua_gc(lua.main_state(), ffi::LUA_GCSETSTEPMUL, step_multiplier) }
|
||||
}
|
||||
|
||||
/// Changes the collector to incremental mode with the given parameters.
|
||||
@@ -888,7 +891,7 @@ impl Lua {
|
||||
/// [documentation]: https://www.lua.org/manual/5.4/manual.html#2.5.1
|
||||
pub fn gc_inc(&self, pause: c_int, step_multiplier: c_int, step_size: c_int) -> GCMode {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state;
|
||||
let state = lua.main_state();
|
||||
|
||||
#[cfg(any(
|
||||
feature = "lua53",
|
||||
@@ -941,7 +944,7 @@ impl Lua {
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
||||
pub fn gc_gen(&self, minor_multiplier: c_int, major_multiplier: c_int) -> GCMode {
|
||||
let lua = self.lock();
|
||||
let state = lua.main_state;
|
||||
let state = lua.main_state();
|
||||
let prev_mode = unsafe { ffi::lua_gc(state, ffi::LUA_GCGEN, minor_multiplier, major_multiplier) };
|
||||
match prev_mode {
|
||||
ffi::LUA_GCGEN => GCMode::Generational,
|
||||
@@ -1025,7 +1028,7 @@ impl Lua {
|
||||
///
|
||||
/// Requires `feature = "luau"`
|
||||
///
|
||||
/// [buffer]: https://luau-lang.org/library#buffer-library
|
||||
/// [buffer]: https://luau.org/library#buffer-library
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub fn create_buffer(&self, buf: impl AsRef<[u8]>) -> Result<Buffer> {
|
||||
@@ -1472,6 +1475,7 @@ impl Lua {
|
||||
&self,
|
||||
f: impl for<'scope> FnOnce(&'scope mut Scope<'scope, 'env>) -> Result<R>,
|
||||
) -> Result<R> {
|
||||
// TODO: Update to `&Scope` in next major release
|
||||
f(&mut Scope::new(self.lock_arc()))
|
||||
}
|
||||
|
||||
@@ -1854,6 +1858,14 @@ impl Lua {
|
||||
extra.app_data.borrow(Some(guard))
|
||||
}
|
||||
|
||||
/// Tries to get a reference to an application data object stored by [`Lua::set_app_data`] of
|
||||
/// type `T`.
|
||||
pub fn try_app_data_ref<T: 'static>(&self) -> StdResult<Option<AppDataRef<T>>, BorrowError> {
|
||||
let guard = self.lock_arc();
|
||||
let extra = unsafe { &*guard.extra.get() };
|
||||
extra.app_data.try_borrow(Some(guard))
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to an application data object stored by [`Lua::set_app_data`] of
|
||||
/// type `T`.
|
||||
///
|
||||
@@ -1867,6 +1879,14 @@ impl Lua {
|
||||
extra.app_data.borrow_mut(Some(guard))
|
||||
}
|
||||
|
||||
/// Tries to get a mutable reference to an application data object stored by
|
||||
/// [`Lua::set_app_data`] of type `T`.
|
||||
pub fn try_app_data_mut<T: 'static>(&self) -> StdResult<Option<AppDataRefMut<T>>, BorrowMutError> {
|
||||
let guard = self.lock_arc();
|
||||
let extra = unsafe { &*guard.extra.get() };
|
||||
extra.app_data.try_borrow_mut(Some(guard))
|
||||
}
|
||||
|
||||
/// Removes an application data of type `T`.
|
||||
///
|
||||
/// # Panics
|
||||
|
||||
+43
-36
@@ -1,11 +1,12 @@
|
||||
use std::any::TypeId;
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::mem;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::resume_unwind;
|
||||
use std::ptr::{self, NonNull};
|
||||
use std::result::Result as StdResult;
|
||||
use std::sync::Arc;
|
||||
use std::{mem, ptr};
|
||||
|
||||
use crate::chunk::ChunkMode;
|
||||
use crate::error::{Error, Result};
|
||||
@@ -26,7 +27,7 @@ use crate::util::{
|
||||
assert_stack, check_stack, get_destructed_userdata_metatable, get_internal_userdata, get_main_state,
|
||||
get_metatable_ptr, get_userdata, init_error_registry, init_internal_metatable, init_userdata_metatable,
|
||||
pop_error, push_internal_userdata, push_string, push_table, rawset_field, safe_pcall, safe_xpcall,
|
||||
short_type_name, StackGuard, WrappedFailure,
|
||||
short_type_name, take_userdata, StackGuard, WrappedFailure,
|
||||
};
|
||||
use crate::value::{Nil, Value};
|
||||
|
||||
@@ -41,7 +42,6 @@ use {
|
||||
crate::multi::MultiValue,
|
||||
crate::traits::FromLuaMulti,
|
||||
crate::types::{AsyncCallback, AsyncCallbackUpvalue, AsyncPollUpvalue},
|
||||
std::ptr::NonNull,
|
||||
std::task::{Context, Poll, Waker},
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ use {
|
||||
pub struct RawLua {
|
||||
// The state is dynamic and depends on context
|
||||
pub(super) state: Cell<*mut ffi::lua_State>,
|
||||
pub(super) main_state: *mut ffi::lua_State,
|
||||
pub(super) main_state: Option<NonNull<ffi::lua_State>>,
|
||||
pub(super) extra: XRc<UnsafeCell<ExtraData>>,
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ impl Drop for RawLua {
|
||||
return;
|
||||
}
|
||||
|
||||
let mem_state = MemoryState::get(self.main_state);
|
||||
let mem_state = MemoryState::get(self.main_state());
|
||||
|
||||
ffi::lua_close(self.main_state);
|
||||
ffi::lua_close(self.main_state());
|
||||
|
||||
// Deallocate `MemoryState`
|
||||
if !mem_state.is_null() {
|
||||
@@ -95,10 +95,11 @@ impl RawLua {
|
||||
self.state.get()
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
#[inline(always)]
|
||||
pub(crate) fn main_state(&self) -> *mut ffi::lua_State {
|
||||
self.main_state
|
||||
.map(|state| state.as_ptr())
|
||||
.unwrap_or_else(|| self.state())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -221,7 +222,8 @@ impl RawLua {
|
||||
#[allow(clippy::arc_with_non_send_sync)]
|
||||
let rawlua = XRc::new(ReentrantMutex::new(RawLua {
|
||||
state: Cell::new(state),
|
||||
main_state,
|
||||
// Make sure that we don't store current state as main state (if it's not available)
|
||||
main_state: get_main_state(state).and_then(NonNull::new),
|
||||
extra: XRc::clone(&extra),
|
||||
}));
|
||||
(*extra.get()).set_lua(&rawlua);
|
||||
@@ -263,7 +265,7 @@ impl RawLua {
|
||||
));
|
||||
}
|
||||
|
||||
let res = load_std_libs(self.main_state, libs);
|
||||
let res = load_std_libs(self.main_state(), libs);
|
||||
|
||||
// If `package` library loaded into a safe lua state then disable C modules
|
||||
let curr_libs = (*self.extra.get()).libs;
|
||||
@@ -734,7 +736,7 @@ impl RawLua {
|
||||
}
|
||||
|
||||
// MemoryInfo is empty in module mode so we cannot predict memory limits
|
||||
match MemoryState::get(self.main_state) {
|
||||
match MemoryState::get(self.state()) {
|
||||
mem_state if !mem_state.is_null() => (*mem_state).memory_limit() == 0,
|
||||
_ => (*self.extra.get()).skip_memory_check, // Check the special flag (only for module mode)
|
||||
}
|
||||
@@ -832,7 +834,7 @@ impl RawLua {
|
||||
|
||||
pub(crate) unsafe fn push_userdata_metatable<T>(&self, mut registry: UserDataRegistry<T>) -> Result<()> {
|
||||
let state = self.state();
|
||||
let _sg = StackGuard::with_top(state, ffi::lua_gettop(state) + 1);
|
||||
let mut stack_guard = StackGuard::new(state);
|
||||
check_stack(state, 13)?;
|
||||
|
||||
// Prepare metatable, add meta methods first and then meta fields
|
||||
@@ -863,8 +865,6 @@ impl RawLua {
|
||||
}
|
||||
let metatable_index = ffi::lua_absindex(state, -1);
|
||||
|
||||
let mut extra_tables_count = 0;
|
||||
|
||||
let fields_nrec = registry.fields.len();
|
||||
if fields_nrec > 0 {
|
||||
// If `__index` is a table then update it in-place
|
||||
@@ -909,7 +909,6 @@ impl RawLua {
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
field_getters_index = Some(ffi::lua_absindex(state, -1));
|
||||
extra_tables_count += 1;
|
||||
}
|
||||
|
||||
let mut field_setters_index = None;
|
||||
@@ -921,7 +920,6 @@ impl RawLua {
|
||||
rawset_field(state, -2, &k)?;
|
||||
}
|
||||
field_setters_index = Some(ffi::lua_absindex(state, -1));
|
||||
extra_tables_count += 1;
|
||||
}
|
||||
|
||||
let mut methods_index = None;
|
||||
@@ -958,18 +956,23 @@ impl RawLua {
|
||||
}
|
||||
_ => {
|
||||
methods_index = Some(ffi::lua_absindex(state, -1));
|
||||
extra_tables_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
let extra_init = None;
|
||||
#[cfg(not(feature = "luau"))]
|
||||
let extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>> = Some(|state| {
|
||||
ffi::lua_pushcfunction(state, crate::util::userdata_destructor::<UserDataStorage<T>>);
|
||||
rawset_field(state, -2, "__gc")
|
||||
});
|
||||
unsafe extern "C-unwind" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, -1);
|
||||
if !(*ud).is_borrowed() {
|
||||
take_userdata::<UserDataStorage<T>>(state);
|
||||
ffi::lua_pushboolean(state, 1);
|
||||
} else {
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
ffi::lua_pushcfunction(state, userdata_destructor::<T>);
|
||||
rawset_field(state, metatable_index, "__gc")?;
|
||||
|
||||
init_userdata_metatable(
|
||||
state,
|
||||
@@ -977,11 +980,10 @@ impl RawLua {
|
||||
field_getters_index,
|
||||
field_setters_index,
|
||||
methods_index,
|
||||
extra_init,
|
||||
)?;
|
||||
|
||||
// Pop extra tables to get metatable on top of the stack
|
||||
ffi::lua_pop(state, extra_tables_count);
|
||||
// Update stack guard to keep metatable after return
|
||||
stack_guard.keep(1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1100,7 +1102,7 @@ impl RawLua {
|
||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luau"))]
|
||||
unsafe {
|
||||
if !(*self.extra.get()).libs.contains(StdLib::COROUTINE) {
|
||||
load_std_libs(self.main_state, StdLib::COROUTINE)?;
|
||||
load_std_libs(self.main_state(), StdLib::COROUTINE)?;
|
||||
(*self.extra.get()).libs |= StdLib::COROUTINE;
|
||||
}
|
||||
}
|
||||
@@ -1345,6 +1347,12 @@ unsafe fn load_std_libs(state: *mut ffi::lua_State, libs: StdLib) -> Result<()>
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
|
||||
#[cfg(feature = "luau")]
|
||||
if libs.contains(StdLib::VECTOR) {
|
||||
requiref(state, ffi::LUA_VECLIBNAME, ffi::luaopen_vector, 1)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
|
||||
if libs.contains(StdLib::MATH) {
|
||||
requiref(state, ffi::LUA_MATHLIBNAME, ffi::luaopen_math, 1)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
@@ -1367,16 +1375,15 @@ unsafe fn load_std_libs(state: *mut ffi::lua_State, libs: StdLib) -> Result<()>
|
||||
}
|
||||
|
||||
#[cfg(feature = "luajit")]
|
||||
{
|
||||
if libs.contains(StdLib::JIT) {
|
||||
requiref(state, ffi::LUA_JITLIBNAME, ffi::luaopen_jit, 1)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
if libs.contains(StdLib::JIT) {
|
||||
requiref(state, ffi::LUA_JITLIBNAME, ffi::luaopen_jit, 1)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
|
||||
if libs.contains(StdLib::FFI) {
|
||||
requiref(state, ffi::LUA_FFILIBNAME, ffi::luaopen_ffi, 1)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
#[cfg(feature = "luajit")]
|
||||
if libs.contains(StdLib::FFI) {
|
||||
requiref(state, ffi::LUA_FFILIBNAME, ffi::luaopen_ffi, 1)?;
|
||||
ffi::lua_pop(state, 1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
+7
-2
@@ -43,17 +43,22 @@ impl StdLib {
|
||||
/// [`package`](https://www.lua.org/manual/5.4/manual.html#6.3) library
|
||||
pub const PACKAGE: StdLib = StdLib(1 << 8);
|
||||
|
||||
/// [`buffer`](https://luau-lang.org/library#buffer-library) library
|
||||
/// [`buffer`](https://luau.org/library#buffer-library) library
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub const BUFFER: StdLib = StdLib(1 << 9);
|
||||
|
||||
/// [`vector`](https://luau.org/library#vector-library) library
|
||||
#[cfg(any(feature = "luau", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
||||
pub const VECTOR: StdLib = StdLib(1 << 10);
|
||||
|
||||
/// [`jit`](http://luajit.org/ext_jit.html) library
|
||||
///
|
||||
/// Requires `feature = "luajit"`
|
||||
#[cfg(any(feature = "luajit", doc))]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "luajit")))]
|
||||
pub const JIT: StdLib = StdLib(1 << 9);
|
||||
pub const JIT: StdLib = StdLib(1 << 11);
|
||||
|
||||
/// (**unsafe**) [`ffi`](http://luajit.org/ext_ffi.html) library
|
||||
///
|
||||
|
||||
@@ -55,7 +55,11 @@ impl String {
|
||||
///
|
||||
/// Any non-Unicode sequences are replaced with [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
|
||||
///
|
||||
/// This method returns [`StdString`] instead of [`Cow<'_, str>`] because lifetime cannot be
|
||||
/// bound to a weak Lua object.
|
||||
///
|
||||
/// [U+FFFD]: std::char::REPLACEMENT_CHARACTER
|
||||
/// [`Cow<'_, str>`]: std::borrow::Cow
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -74,6 +78,16 @@ impl String {
|
||||
StdString::from_utf8_lossy(&self.as_bytes()).into_owned()
|
||||
}
|
||||
|
||||
/// Returns an object that implements [`Display`] for safely printing a Lua [`String`] that may
|
||||
/// contain non-Unicode data.
|
||||
///
|
||||
/// This may perform lossy conversion.
|
||||
///
|
||||
/// [`Display`]: fmt::Display
|
||||
pub fn display(&self) -> impl fmt::Display + '_ {
|
||||
Display(self)
|
||||
}
|
||||
|
||||
/// Get the bytes that make up this string.
|
||||
///
|
||||
/// The returned slice will not contain the terminating nul byte, but will contain any nul
|
||||
@@ -212,6 +226,15 @@ impl Serialize for String {
|
||||
}
|
||||
}
|
||||
|
||||
struct Display<'a>(&'a String);
|
||||
|
||||
impl fmt::Display for Display<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let bytes = self.0.as_bytes();
|
||||
<bstr::BStr as fmt::Display>::fmt(bstr::BStr::new(&bytes), f)
|
||||
}
|
||||
}
|
||||
|
||||
/// A borrowed string (`&str`) that holds a strong reference to the Lua state.
|
||||
pub struct BorrowedStr<'a>(&'a str, #[allow(unused)] Lua);
|
||||
|
||||
|
||||
+32
-7
@@ -693,7 +693,8 @@ impl Table {
|
||||
}
|
||||
|
||||
/// Iterates over the sequence part of the table, invoking the given closure on each value.
|
||||
pub(crate) fn for_each_value<V>(&self, mut f: impl FnMut(V) -> Result<()>) -> Result<()>
|
||||
#[doc(hidden)]
|
||||
pub fn for_each_value<V>(&self, mut f: impl FnMut(V) -> Result<()>) -> Result<()>
|
||||
where
|
||||
V: FromLua,
|
||||
{
|
||||
@@ -777,16 +778,40 @@ impl Table {
|
||||
let mut pairs = self.pairs::<Value, Value>().flatten().collect::<Vec<_>>();
|
||||
// Sort keys
|
||||
pairs.sort_by(|(a, _), (b, _)| a.sort_cmp(b));
|
||||
let is_sequence = (pairs.iter().enumerate())
|
||||
.all(|(i, (k, _))| matches!(k, Value::Integer(n) if *n == (i + 1) as Integer));
|
||||
if pairs.is_empty() {
|
||||
return write!(fmt, "{{}}");
|
||||
}
|
||||
writeln!(fmt, "{{")?;
|
||||
for (key, value) in pairs {
|
||||
write!(fmt, "{}[", " ".repeat(ident + 2))?;
|
||||
key.fmt_pretty(fmt, false, ident + 2, visited)?;
|
||||
write!(fmt, "] = ")?;
|
||||
value.fmt_pretty(fmt, true, ident + 2, visited)?;
|
||||
writeln!(fmt, ",")?;
|
||||
if is_sequence {
|
||||
// Format as list
|
||||
for (_, value) in pairs {
|
||||
write!(fmt, "{}", " ".repeat(ident + 2))?;
|
||||
value.fmt_pretty(fmt, true, ident + 2, visited)?;
|
||||
writeln!(fmt, ",")?;
|
||||
}
|
||||
} else {
|
||||
fn is_simple_key(key: &[u8]) -> bool {
|
||||
key.iter().take(1).all(|c| c.is_ascii_alphabetic() || *c == b'_')
|
||||
&& key.iter().all(|c| c.is_ascii_alphanumeric() || *c == b'_')
|
||||
}
|
||||
|
||||
for (key, value) in pairs {
|
||||
match key {
|
||||
Value::String(key) if is_simple_key(&key.as_bytes()) => {
|
||||
write!(fmt, "{}{}", " ".repeat(ident + 2), key.display())?;
|
||||
write!(fmt, " = ")?;
|
||||
}
|
||||
_ => {
|
||||
write!(fmt, "{}[", " ".repeat(ident + 2))?;
|
||||
key.fmt_pretty(fmt, false, ident + 2, visited)?;
|
||||
write!(fmt, "] = ")?;
|
||||
}
|
||||
}
|
||||
value.fmt_pretty(fmt, true, ident + 2, visited)?;
|
||||
writeln!(fmt, ",")?;
|
||||
}
|
||||
}
|
||||
write!(fmt, "{}}}", " ".repeat(ident))
|
||||
}
|
||||
|
||||
+54
-18
@@ -1,5 +1,5 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell};
|
||||
use std::cell::{BorrowError, BorrowMutError, Cell, Ref, RefCell, RefMut, UnsafeCell};
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::result::Result as StdResult;
|
||||
@@ -41,30 +41,66 @@ impl AppData {
|
||||
.and_then(|data| data.into_inner().downcast::<T>().ok().map(|data| *data)))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRef<T>> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())?
|
||||
.borrow();
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Some(AppDataRef {
|
||||
data: Ref::filter_map(data, |data| data.downcast_ref()).ok()?,
|
||||
borrow: &self.borrow,
|
||||
_guard: guard,
|
||||
})
|
||||
match self.try_borrow(guard) {
|
||||
Ok(data) => data,
|
||||
Err(err) => panic!("already mutably borrowed: {err:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_borrow<T: 'static>(
|
||||
&self,
|
||||
guard: Option<LuaGuard>,
|
||||
) -> Result<Option<AppDataRef<T>>, BorrowError> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())
|
||||
.map(|c| c.try_borrow())
|
||||
.transpose()?
|
||||
.and_then(|data| Ref::filter_map(data, |data| data.downcast_ref()).ok());
|
||||
match data {
|
||||
Some(data) => {
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Ok(Some(AppDataRef {
|
||||
data,
|
||||
borrow: &self.borrow,
|
||||
_guard: guard,
|
||||
}))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub(crate) fn borrow_mut<T: 'static>(&self, guard: Option<LuaGuard>) -> Option<AppDataRefMut<T>> {
|
||||
match self.try_borrow_mut(guard) {
|
||||
Ok(data) => data,
|
||||
Err(err) => panic!("already borrowed: {err:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn try_borrow_mut<T: 'static>(
|
||||
&self,
|
||||
guard: Option<LuaGuard>,
|
||||
) -> Result<Option<AppDataRefMut<T>>, BorrowMutError> {
|
||||
let data = unsafe { &*self.container.get() }
|
||||
.get(&TypeId::of::<T>())?
|
||||
.borrow_mut();
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Some(AppDataRefMut {
|
||||
data: RefMut::filter_map(data, |data| data.downcast_mut()).ok()?,
|
||||
borrow: &self.borrow,
|
||||
_guard: guard,
|
||||
})
|
||||
.get(&TypeId::of::<T>())
|
||||
.map(|c| c.try_borrow_mut())
|
||||
.transpose()?
|
||||
.and_then(|data| RefMut::filter_map(data, |data| data.downcast_mut()).ok());
|
||||
match data {
|
||||
Some(data) => {
|
||||
self.borrow.set(self.borrow.get() + 1);
|
||||
Ok(Some(AppDataRefMut {
|
||||
data,
|
||||
borrow: &self.borrow,
|
||||
_guard: guard,
|
||||
}))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ mod inner {
|
||||
|
||||
pub(crate) struct ReentrantMutexGuard<'a, T>(&'a T);
|
||||
|
||||
impl<'a, T> Deref for ReentrantMutexGuard<'a, T> {
|
||||
impl<T> Deref for ReentrantMutexGuard<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline(always)]
|
||||
|
||||
@@ -696,6 +696,31 @@ impl AnyUserData {
|
||||
}
|
||||
}
|
||||
|
||||
/// Destroys this userdata.
|
||||
///
|
||||
/// This is similar to [`AnyUserData::take`], but it doesn't require a type.
|
||||
///
|
||||
/// This method works for non-scoped userdata only.
|
||||
pub fn destroy(&self) -> Result<()> {
|
||||
let lua = self.0.lua.lock();
|
||||
let state = lua.state();
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(state);
|
||||
check_stack(state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
protect_lua!(state, 1, 1, fn(state) {
|
||||
if ffi::luaL_callmeta(state, -1, cstr!("__gc")) == 0 {
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
}
|
||||
})?;
|
||||
if ffi::lua_isboolean(state, -1) != 0 && ffi::lua_toboolean(state, -1) != 0 {
|
||||
return Ok(());
|
||||
}
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets an associated value to this [`AnyUserData`].
|
||||
///
|
||||
/// The value may be any Lua value whatsoever, and can be retrieved with [`user_value`].
|
||||
@@ -1082,6 +1107,7 @@ mod cell;
|
||||
mod lock;
|
||||
mod object;
|
||||
mod registry;
|
||||
mod util;
|
||||
|
||||
#[cfg(test)]
|
||||
mod assertions {
|
||||
|
||||
+47
-5
@@ -1,5 +1,5 @@
|
||||
use std::any::{type_name, TypeId};
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::cell::{Cell, RefCell, UnsafeCell};
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::c_int;
|
||||
@@ -16,6 +16,7 @@ use crate::util::get_userdata;
|
||||
use crate::value::Value;
|
||||
|
||||
use super::lock::{RawLock, UserDataLock};
|
||||
use super::util::is_sync;
|
||||
|
||||
#[cfg(all(feature = "serialize", not(feature = "send")))]
|
||||
type DynSerialize = dyn erased_serde::Serialize;
|
||||
@@ -98,6 +99,15 @@ impl<T> UserDataVariant<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn borrow_count(&self) -> &Cell<usize> {
|
||||
match self {
|
||||
Self::Default(inner) => &inner.borrow_count,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(inner) => &inner.borrow_count,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn as_ptr(&self) -> *mut T {
|
||||
match self {
|
||||
@@ -129,10 +139,13 @@ impl Serialize for UserDataStorage<()> {
|
||||
/// A type that provides interior mutability for a userdata value (thread-safe).
|
||||
pub(crate) struct UserDataCell<T> {
|
||||
raw_lock: RawLock,
|
||||
borrow_count: Cell<usize>,
|
||||
value: UnsafeCell<T>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl<T: Send> Send for UserDataCell<T> {}
|
||||
#[cfg(feature = "send")]
|
||||
unsafe impl<T: Send> Sync for UserDataCell<T> {}
|
||||
|
||||
impl<T> UserDataCell<T> {
|
||||
@@ -140,6 +153,7 @@ impl<T> UserDataCell<T> {
|
||||
fn new(value: T) -> Self {
|
||||
UserDataCell {
|
||||
raw_lock: RawLock::INIT,
|
||||
borrow_count: Cell::new(0),
|
||||
value: UnsafeCell::new(value),
|
||||
}
|
||||
}
|
||||
@@ -162,7 +176,11 @@ impl<T> Deref for UserDataRef<T> {
|
||||
impl<T> Drop for UserDataRef<T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.raw_lock().unlock_shared() };
|
||||
if !cfg!(feature = "send") || is_sync::<T>() {
|
||||
unsafe { self.0.raw_lock().unlock_shared() };
|
||||
} else {
|
||||
unsafe { self.0.raw_lock().unlock_exclusive() };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +201,11 @@ impl<T> TryFrom<UserDataVariant<T>> for UserDataRef<T> {
|
||||
|
||||
#[inline]
|
||||
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
|
||||
if !variant.raw_lock().try_lock_shared() {
|
||||
if !cfg!(feature = "send") || is_sync::<T>() {
|
||||
if !variant.raw_lock().try_lock_shared() {
|
||||
return Err(Error::UserDataBorrowError);
|
||||
}
|
||||
} else if !variant.raw_lock().try_lock_exclusive() {
|
||||
return Err(Error::UserDataBorrowError);
|
||||
}
|
||||
Ok(UserDataRef(variant))
|
||||
@@ -280,7 +302,10 @@ pub(crate) struct UserDataBorrowRef<'a, T>(&'a UserDataVariant<T>);
|
||||
impl<T> Drop for UserDataBorrowRef<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.raw_lock().unlock_shared() };
|
||||
unsafe {
|
||||
self.0.borrow_count().set(self.0.borrow_count().get() - 1);
|
||||
self.0.raw_lock().unlock_shared();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,9 +324,14 @@ impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowRef<'a, T> {
|
||||
|
||||
#[inline(always)]
|
||||
fn try_from(variant: &'a UserDataVariant<T>) -> Result<Self> {
|
||||
// We don't need to check for `T: Sync` because when this method is used (internally),
|
||||
// Lua mutex is already locked.
|
||||
// If non-`Sync` userdata is already borrowed by another thread (via `UserDataRef`), it will be
|
||||
// exclusively locked.
|
||||
if !variant.raw_lock().try_lock_shared() {
|
||||
return Err(Error::UserDataBorrowError);
|
||||
}
|
||||
variant.borrow_count().set(variant.borrow_count().get() + 1);
|
||||
Ok(UserDataBorrowRef(variant))
|
||||
}
|
||||
}
|
||||
@@ -311,7 +341,10 @@ pub(crate) struct UserDataBorrowMut<'a, T>(&'a UserDataVariant<T>);
|
||||
impl<T> Drop for UserDataBorrowMut<'_, T> {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { self.0.raw_lock().unlock_exclusive() };
|
||||
unsafe {
|
||||
self.0.borrow_count().set(self.0.borrow_count().get() - 1);
|
||||
self.0.raw_lock().unlock_exclusive();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -339,6 +372,7 @@ impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowMut<'a, T> {
|
||||
if !variant.raw_lock().try_lock_exclusive() {
|
||||
return Err(Error::UserDataBorrowMutError);
|
||||
}
|
||||
variant.borrow_count().set(variant.borrow_count().get() + 1);
|
||||
Ok(UserDataBorrowMut(variant))
|
||||
}
|
||||
}
|
||||
@@ -455,6 +489,14 @@ impl<T> UserDataStorage<T> {
|
||||
Self::Scoped(ScopedUserDataVariant::Boxed(RefCell::new(data)))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn is_borrowed(&self) -> bool {
|
||||
match self {
|
||||
Self::Owned(variant) => variant.borrow_count().get() > 0,
|
||||
Self::Scoped(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn try_borrow_scoped<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
|
||||
match self {
|
||||
|
||||
@@ -63,32 +63,32 @@ mod lock_impl {
|
||||
|
||||
#[cfg(feature = "send")]
|
||||
mod lock_impl {
|
||||
use parking_lot::lock_api::RawMutex;
|
||||
use parking_lot::lock_api::RawRwLock;
|
||||
|
||||
pub(crate) type RawLock = parking_lot::RawMutex;
|
||||
pub(crate) type RawLock = parking_lot::RawRwLock;
|
||||
|
||||
impl super::UserDataLock for RawLock {
|
||||
#[allow(clippy::declare_interior_mutable_const)]
|
||||
const INIT: Self = <Self as parking_lot::lock_api::RawMutex>::INIT;
|
||||
const INIT: Self = <Self as parking_lot::lock_api::RawRwLock>::INIT;
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_shared(&self) -> bool {
|
||||
RawLock::try_lock(self)
|
||||
RawRwLock::try_lock_shared(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn try_lock_exclusive(&self) -> bool {
|
||||
RawLock::try_lock(self)
|
||||
RawRwLock::try_lock_exclusive(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_shared(&self) {
|
||||
RawLock::unlock(self)
|
||||
RawRwLock::unlock_shared(self)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn unlock_exclusive(&self) {
|
||||
RawLock::unlock(self)
|
||||
RawRwLock::unlock_exclusive(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+250
-64
@@ -21,12 +21,32 @@ use {
|
||||
std::future::{self, Future},
|
||||
};
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
use std::rc::Rc;
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
type StaticFieldCallback = Box<dyn FnOnce(&RawLua) -> Result<()> + 'static>;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum UserDataTypeId {
|
||||
enum UserDataTypeId {
|
||||
Shared(TypeId),
|
||||
Unique(usize),
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
Rc(TypeId),
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
RcRefCell(TypeId),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
Arc(TypeId),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcMutex(TypeId),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcRwLock(TypeId),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcParkingLotMutex(TypeId),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
ArcParkingLotRwLock(TypeId),
|
||||
}
|
||||
|
||||
/// Handle to registry for userdata methods and metamethods.
|
||||
@@ -45,31 +65,23 @@ pub struct UserDataRegistry<T> {
|
||||
#[cfg(feature = "async")]
|
||||
pub(crate) async_meta_methods: Vec<(String, AsyncCallback)>,
|
||||
|
||||
pub(crate) type_id: UserDataTypeId,
|
||||
type_id: UserDataTypeId,
|
||||
_type: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> UserDataRegistry<T> {
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub(crate) fn new(type_id: TypeId) -> Self {
|
||||
UserDataRegistry {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
field_setters: Vec::new(),
|
||||
meta_fields: Vec::new(),
|
||||
methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_methods: Vec::new(),
|
||||
meta_methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_meta_methods: Vec::new(),
|
||||
type_id: UserDataTypeId::Shared(type_id),
|
||||
_type: PhantomData,
|
||||
}
|
||||
Self::with_type_id(UserDataTypeId::Shared(type_id))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn new_unique(ud_ptr: *const c_void) -> Self {
|
||||
#[inline(always)]
|
||||
pub(crate) fn new_unique(ud_ptr: *mut c_void) -> Self {
|
||||
Self::with_type_id(UserDataTypeId::Unique(ud_ptr as usize))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn with_type_id(type_id: UserDataTypeId) -> Self {
|
||||
UserDataRegistry {
|
||||
fields: Vec::new(),
|
||||
field_getters: Vec::new(),
|
||||
@@ -81,7 +93,7 @@ impl<T> UserDataRegistry<T> {
|
||||
meta_methods: Vec::new(),
|
||||
#[cfg(feature = "async")]
|
||||
async_meta_methods: Vec::new(),
|
||||
type_id: UserDataTypeId::Unique(ud_ptr as usize),
|
||||
type_id,
|
||||
_type: PhantomData,
|
||||
}
|
||||
}
|
||||
@@ -91,6 +103,20 @@ impl<T> UserDataRegistry<T> {
|
||||
match self.type_id {
|
||||
UserDataTypeId::Shared(type_id) => Some(type_id),
|
||||
UserDataTypeId::Unique(_) => None,
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
UserDataTypeId::Rc(type_id) => Some(type_id),
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
UserDataTypeId::RcRefCell(type_id) => Some(type_id),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
UserDataTypeId::Arc(type_id) => Some(type_id),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
UserDataTypeId::ArcMutex(type_id) => Some(type_id),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
UserDataTypeId::ArcRwLock(type_id) => Some(type_id),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
UserDataTypeId::ArcParkingLotMutex(type_id) => Some(type_id),
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
UserDataTypeId::ArcParkingLotRwLock(type_id) => Some(type_id),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,28 +146,102 @@ impl<T> UserDataRegistry<T> {
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
|
||||
|
||||
match target_type_id {
|
||||
// This branch is for `'static` userdata that share type metatable
|
||||
UserDataTypeId::Shared(target_type_id) => {
|
||||
match try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) {
|
||||
Some(self_type_id) if self_type_id == target_type_id => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Shared(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
UserDataTypeId::Unique(target_ptr) => {
|
||||
match get_userdata::<UserDataStorage<T>>(state, self_index) {
|
||||
ud if ud as usize == target_ptr => {
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Unique(target_ptr)
|
||||
if get_userdata::<UserDataStorage<T>>(state, self_index) as usize == target_ptr =>
|
||||
{
|
||||
let ud = target_ptr as *mut UserDataStorage<T>;
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Rc(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<T>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Rc<T>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::RcRefCell(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<RefCell<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Rc<RefCell<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Arc(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<T>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<T>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcMutex(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<Mutex<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<Mutex<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcRwLock(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<RwLock<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<RwLock<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcParkingLotMutex(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::Mutex<T>>>(self_index))
|
||||
== Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::Mutex<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?;
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcParkingLotRwLock(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::RwLock<T>>>(self_index))
|
||||
== Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::RwLock<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?;
|
||||
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -174,28 +274,96 @@ impl<T> UserDataRegistry<T> {
|
||||
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
|
||||
|
||||
match target_type_id {
|
||||
// This branch is for `'static` userdata that share type metatable
|
||||
UserDataTypeId::Shared(target_type_id) => {
|
||||
match try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) {
|
||||
Some(self_type_id) if self_type_id == target_type_id => {
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Shared(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
UserDataTypeId::Unique(target_ptr) => {
|
||||
match get_userdata::<UserDataStorage<T>>(state, self_index) {
|
||||
ud if ud as usize == target_ptr => {
|
||||
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Unique(target_ptr)
|
||||
if get_userdata::<UserDataStorage<T>>(state, self_index) as usize == target_ptr =>
|
||||
{
|
||||
let ud = target_ptr as *mut UserDataStorage<T>;
|
||||
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
|
||||
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Rc(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<T>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
},
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::RcRefCell(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<RefCell<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Rc<RefCell<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_borrow_mut().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::Arc(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<T>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
Err(Error::UserDataBorrowMutError)
|
||||
},
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcMutex(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<Mutex<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<Mutex<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcRwLock(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<RwLock<T>>>(self_index)) == Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<RwLock<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcParkingLotMutex(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::Mutex<T>>>(self_index))
|
||||
== Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::Mutex<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_lock().ok_or(Error::UserDataBorrowMutError)?;
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[rustfmt::skip]
|
||||
UserDataTypeId::ArcParkingLotRwLock(target_type_id)
|
||||
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::RwLock<T>>>(self_index))
|
||||
== Some(target_type_id) =>
|
||||
{
|
||||
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::RwLock<T>>>>(state, self_index);
|
||||
try_self_arg!((*ud).try_borrow_scoped(|ud| {
|
||||
let mut ud = ud.try_write().ok_or(Error::UserDataBorrowMutError)?;
|
||||
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
|
||||
}))
|
||||
}
|
||||
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -607,11 +775,14 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
|
||||
}
|
||||
|
||||
macro_rules! lua_userdata_impl {
|
||||
($type:ty) => {
|
||||
($type:ty => $type_variant:tt) => {
|
||||
lua_userdata_impl!($type, UserDataTypeId::$type_variant(TypeId::of::<$type>()));
|
||||
};
|
||||
|
||||
($type:ty, $type_id:expr) => {
|
||||
impl<T: UserData + 'static> UserData for $type {
|
||||
fn register(registry: &mut UserDataRegistry<Self>) {
|
||||
let type_id = TypeId::of::<T>();
|
||||
let mut orig_registry = UserDataRegistry::new(type_id);
|
||||
let mut orig_registry = UserDataRegistry::with_type_id($type_id);
|
||||
T::register(&mut orig_registry);
|
||||
|
||||
// Copy all fields, methods, etc. from the original registry
|
||||
@@ -635,4 +806,19 @@ macro_rules! lua_userdata_impl {
|
||||
// A special proxy object for UserData
|
||||
pub(crate) struct UserDataProxy<T>(pub(crate) PhantomData<T>);
|
||||
|
||||
lua_userdata_impl!(UserDataProxy<T>);
|
||||
lua_userdata_impl!(UserDataProxy<T>, UserDataTypeId::Shared(TypeId::of::<T>()));
|
||||
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
lua_userdata_impl!(Rc<T> => Rc);
|
||||
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
|
||||
lua_userdata_impl!(Rc<RefCell<T>> => RcRefCell);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<T> => Arc);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<Mutex<T>> => ArcMutex);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<RwLock<T>> => ArcRwLock);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<parking_lot::Mutex<T>> => ArcParkingLotMutex);
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
lua_userdata_impl!(Arc<parking_lot::RwLock<T>> => ArcParkingLotRwLock);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
use std::cell::Cell;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
// This is a trick to check if a type is `Sync` or not.
|
||||
// It uses leaked specialization feature from stdlib.
|
||||
struct IsSync<'a, T> {
|
||||
is_sync: &'a Cell<bool>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Clone for IsSync<'_, T> {
|
||||
fn clone(&self) -> Self {
|
||||
self.is_sync.set(false);
|
||||
IsSync {
|
||||
is_sync: self.is_sync,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sync> Copy for IsSync<'_, T> {}
|
||||
|
||||
pub(crate) fn is_sync<T>() -> bool {
|
||||
let is_sync = Cell::new(true);
|
||||
let _ = [IsSync::<T> {
|
||||
is_sync: &is_sync,
|
||||
_marker: PhantomData,
|
||||
}]
|
||||
.clone();
|
||||
is_sync.get()
|
||||
}
|
||||
+14
-3
@@ -21,9 +21,6 @@ pub(crate) use userdata::{
|
||||
pub(crate) use userdata::push_uninit_userdata;
|
||||
pub(crate) use userdata::push_userdata;
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) use userdata::userdata_destructor;
|
||||
|
||||
// Checks that Lua has enough free stack space for future stack operations. On failure, this will
|
||||
// panic with an internal error message.
|
||||
#[inline]
|
||||
@@ -66,6 +63,11 @@ impl StackGuard {
|
||||
pub(crate) fn with_top(state: *mut ffi::lua_State, top: c_int) -> StackGuard {
|
||||
StackGuard { state, top }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn keep(&mut self, n: c_int) {
|
||||
self.top += n;
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for StackGuard {
|
||||
@@ -129,6 +131,15 @@ pub(crate) unsafe fn push_table(
|
||||
}
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces, does not call checkstack.
|
||||
pub(crate) unsafe fn rawget_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<c_int> {
|
||||
ffi::lua_pushvalue(state, table);
|
||||
protect_lua!(state, 1, 1, |state| {
|
||||
ffi::lua_pushlstring(state, field.as_ptr() as *const c_char, field.len());
|
||||
ffi::lua_rawget(state, -2)
|
||||
})
|
||||
}
|
||||
|
||||
// Uses 4 stack spaces, does not call checkstack.
|
||||
pub(crate) unsafe fn rawset_field(state: *mut ffi::lua_State, table: c_int, field: &str) -> Result<()> {
|
||||
ffi::lua_pushvalue(state, table);
|
||||
|
||||
+9
-21
@@ -3,7 +3,7 @@ use std::os::raw::{c_int, c_void};
|
||||
use std::{ptr, str};
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::util::{check_stack, get_metatable_ptr, push_string, push_table, rawset_field, TypeKey};
|
||||
use crate::util::{check_stack, get_metatable_ptr, push_table, rawget_field, rawset_field, TypeKey};
|
||||
|
||||
// Pushes the userdata and attaches a metatable with __gc method.
|
||||
// Internally uses 3 stack spaces, does not call checkstack.
|
||||
@@ -152,16 +152,12 @@ pub(crate) unsafe fn init_userdata_metatable(
|
||||
field_getters: Option<c_int>,
|
||||
field_setters: Option<c_int>,
|
||||
methods: Option<c_int>,
|
||||
extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>>,
|
||||
) -> Result<()> {
|
||||
ffi::lua_pushvalue(state, metatable);
|
||||
|
||||
if field_getters.is_some() || methods.is_some() {
|
||||
// Push `__index` generator function
|
||||
init_userdata_metatable_index(state)?;
|
||||
|
||||
push_string(state, b"__index", true)?;
|
||||
let index_type = ffi::lua_rawget(state, -3);
|
||||
let index_type = rawget_field(state, metatable, "__index")?;
|
||||
match index_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
for &idx in &[field_getters, methods] {
|
||||
@@ -175,39 +171,31 @@ pub(crate) unsafe fn init_userdata_metatable(
|
||||
// Generate `__index`
|
||||
protect_lua!(state, 4, 1, fn(state) ffi::lua_call(state, 3, 1))?;
|
||||
}
|
||||
_ => mlua_panic!("improper __index type {}", index_type),
|
||||
_ => mlua_panic!("improper `__index` type: {}", index_type),
|
||||
}
|
||||
|
||||
rawset_field(state, -2, "__index")?;
|
||||
rawset_field(state, metatable, "__index")?;
|
||||
}
|
||||
|
||||
if let Some(field_setters) = field_setters {
|
||||
// Push `__newindex` generator function
|
||||
init_userdata_metatable_newindex(state)?;
|
||||
|
||||
push_string(state, b"__newindex", true)?;
|
||||
let newindex_type = ffi::lua_rawget(state, -3);
|
||||
let newindex_type = rawget_field(state, metatable, "__newindex")?;
|
||||
match newindex_type {
|
||||
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
|
||||
ffi::lua_pushvalue(state, field_setters);
|
||||
// Generate `__newindex`
|
||||
protect_lua!(state, 3, 1, fn(state) ffi::lua_call(state, 2, 1))?;
|
||||
}
|
||||
_ => mlua_panic!("improper __newindex type {}", newindex_type),
|
||||
_ => mlua_panic!("improper `__newindex` type: {}", newindex_type),
|
||||
}
|
||||
|
||||
rawset_field(state, -2, "__newindex")?;
|
||||
}
|
||||
|
||||
// Additional initialization
|
||||
if let Some(extra_init) = extra_init {
|
||||
extra_init(state)?;
|
||||
rawset_field(state, metatable, "__newindex")?;
|
||||
}
|
||||
|
||||
ffi::lua_pushboolean(state, 0);
|
||||
rawset_field(state, -2, "__metatable")?;
|
||||
|
||||
ffi::lua_pop(state, 1);
|
||||
rawset_field(state, metatable, "__metatable")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -351,7 +339,7 @@ unsafe fn init_userdata_metatable_newindex(state: *mut ffi::lua_State) -> Result
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "luau"))]
|
||||
pub(crate) unsafe extern "C-unwind" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
||||
unsafe extern "C-unwind" fn userdata_destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
||||
// 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);
|
||||
|
||||
+11
-8
@@ -1,20 +1,23 @@
|
||||
[lua54_coverage]
|
||||
features = "lua54,vendored,async,send,serialize,macros,anyhow"
|
||||
[lua54]
|
||||
features = "lua54,vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
|
||||
[lua54_with_memory_limit_coverage]
|
||||
features = "lua54,vendored,async,send,serialize,macros"
|
||||
[lua54_non_send]
|
||||
features = "lua54,vendored,async,serialize,macros,anyhow,userdata-wrappers"
|
||||
|
||||
[lua54_with_memory_limit]
|
||||
features = "lua54,vendored,async,send,serialize,macros,anyhow,userdata-wrappers"
|
||||
rustflags = "--cfg force_memory_limit"
|
||||
|
||||
[lua51_coverage]
|
||||
[lua51]
|
||||
features = "lua51,vendored,async,send,serialize,macros"
|
||||
|
||||
[lua51_with_memory_limit_coverage]
|
||||
[lua51_with_memory_limit]
|
||||
features = "lua51,vendored,async,send,serialize,macros"
|
||||
rustflags = "--cfg force_memory_limit"
|
||||
|
||||
[luau_coverage]
|
||||
[luau]
|
||||
features = "luau,async,send,serialize,macros"
|
||||
|
||||
[luau_with_memory_limit_coverage]
|
||||
[luau_with_memory_limit]
|
||||
features = "luau,async,send,serialize,macros"
|
||||
rustflags = "--cfg force_memory_limit"
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ fn test_debug_format() -> Result<()> {
|
||||
// Globals
|
||||
let globals = lua.globals();
|
||||
let dump = format!("{globals:#?}");
|
||||
assert!(dump.starts_with("{\n [\"_G\"] = table:"));
|
||||
assert!(dump.starts_with("{\n _G = table:"));
|
||||
|
||||
// TODO: Other cases
|
||||
|
||||
|
||||
+14
-10
@@ -97,17 +97,19 @@ fn test_require() -> Result<()> {
|
||||
fn test_vectors() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let v: Vector = lua.load("vector(1, 2, 3) + vector(3, 2, 1)").eval()?;
|
||||
let v: Vector = lua
|
||||
.load("vector.create(1, 2, 3) + vector.create(3, 2, 1)")
|
||||
.eval()?;
|
||||
assert_eq!(v, [4.0, 4.0, 4.0]);
|
||||
|
||||
// Test conversion into Rust array
|
||||
let v: [f64; 3] = lua.load("vector(1, 2, 3)").eval()?;
|
||||
let v: [f64; 3] = lua.load("vector.create(1, 2, 3)").eval()?;
|
||||
assert!(v == [1.0, 2.0, 3.0]);
|
||||
|
||||
// Test vector methods
|
||||
lua.load(
|
||||
r#"
|
||||
local v = vector(1, 2, 3)
|
||||
local v = vector.create(1, 2, 3)
|
||||
assert(v.x == 1)
|
||||
assert(v.y == 2)
|
||||
assert(v.z == 3)
|
||||
@@ -118,7 +120,7 @@ fn test_vectors() -> Result<()> {
|
||||
// Test vector methods (fastcall)
|
||||
lua.load(
|
||||
r#"
|
||||
local v = vector(1, 2, 3)
|
||||
local v = vector.create(1, 2, 3)
|
||||
assert(v.x == 1)
|
||||
assert(v.y == 2)
|
||||
assert(v.z == 3)
|
||||
@@ -135,17 +137,19 @@ fn test_vectors() -> Result<()> {
|
||||
fn test_vectors() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let v: Vector = lua.load("vector(1, 2, 3, 4) + vector(4, 3, 2, 1)").eval()?;
|
||||
let v: Vector = lua
|
||||
.load("vector.create(1, 2, 3, 4) + vector.create(4, 3, 2, 1)")
|
||||
.eval()?;
|
||||
assert_eq!(v, [5.0, 5.0, 5.0, 5.0]);
|
||||
|
||||
// Test conversion into Rust array
|
||||
let v: [f64; 4] = lua.load("vector(1, 2, 3, 4)").eval()?;
|
||||
let v: [f64; 4] = lua.load("vector.create(1, 2, 3, 4)").eval()?;
|
||||
assert!(v == [1.0, 2.0, 3.0, 4.0]);
|
||||
|
||||
// Test vector methods
|
||||
lua.load(
|
||||
r#"
|
||||
local v = vector(1, 2, 3, 4)
|
||||
local v = vector.create(1, 2, 3, 4)
|
||||
assert(v.x == 1)
|
||||
assert(v.y == 2)
|
||||
assert(v.z == 3)
|
||||
@@ -157,7 +161,7 @@ fn test_vectors() -> Result<()> {
|
||||
// Test vector methods (fastcall)
|
||||
lua.load(
|
||||
r#"
|
||||
local v = vector(1, 2, 3, 4)
|
||||
local v = vector.create(1, 2, 3, 4)
|
||||
assert(v.x == 1)
|
||||
assert(v.y == 2)
|
||||
assert(v.z == 3)
|
||||
@@ -180,10 +184,10 @@ fn test_vector_metatable() -> Result<()> {
|
||||
r#"
|
||||
{
|
||||
__index = {
|
||||
new = vector,
|
||||
new = vector.create,
|
||||
|
||||
product = function(a, b)
|
||||
return vector(a.x * b.x, a.y * b.y, a.z * b.z)
|
||||
return vector.create(a.x * b.x, a.y * b.y, a.z * b.z)
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
+26
-1
@@ -1,4 +1,4 @@
|
||||
use mlua::{Error, ExternalError, IntoLuaMulti, Lua, Result, String, Value};
|
||||
use mlua::{Error, ExternalError, Integer, IntoLuaMulti, Lua, MultiValue, Result, String, Value, Variadic};
|
||||
|
||||
#[test]
|
||||
fn test_result_conversions() -> Result<()> {
|
||||
@@ -58,3 +58,28 @@ fn test_result_conversions() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multivalue() {
|
||||
let mut multi = MultiValue::with_capacity(3);
|
||||
multi.push_back(Value::Integer(1));
|
||||
multi.push_back(Value::Integer(2));
|
||||
multi.push_front(Value::Integer(3));
|
||||
assert_eq!(multi.iter().filter_map(|v| v.as_integer()).sum::<Integer>(), 6);
|
||||
|
||||
let vec = multi.into_vec();
|
||||
assert_eq!(&vec, &[Value::Integer(3), Value::Integer(1), Value::Integer(2)]);
|
||||
let _multi2 = MultiValue::from_vec(vec);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_variadic() {
|
||||
let mut var = Variadic::with_capacity(3);
|
||||
var.extend_from_slice(&[1, 2, 3]);
|
||||
assert_eq!(var.iter().sum::<u32>(), 6);
|
||||
|
||||
let vec = Vec::<u32>::from(var);
|
||||
assert_eq!(&vec, &[1, 2, 3]);
|
||||
let var2 = Variadic::from(vec);
|
||||
assert_eq!(var2.as_slice(), &[1, 2, 3]);
|
||||
}
|
||||
|
||||
+75
-6
@@ -1,6 +1,7 @@
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
use mlua::{
|
||||
AnyUserData, Error, Function, Lua, MetaMethod, ObjectLike, Result, String, UserData, UserDataFields,
|
||||
@@ -66,6 +67,27 @@ fn test_scope_outer_lua_access() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_capture_scope() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let i = Cell::new(0);
|
||||
lua.scope(|scope| {
|
||||
let f = scope.create_function(|_, ()| {
|
||||
scope.create_function(|_, n: u32| {
|
||||
i.set(i.get() + n);
|
||||
Ok(())
|
||||
})
|
||||
})?;
|
||||
f.call::<Function>(())?.call::<()>(10)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
assert_eq!(i.get(), 10);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_userdata_fields() -> Result<()> {
|
||||
struct MyUserData<'a>(&'a Cell<i64>);
|
||||
@@ -388,15 +410,26 @@ fn test_scope_userdata_ref_mut() -> Result<()> {
|
||||
fn test_scope_any_userdata() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.register_userdata_type::<StdString>(|reg| {
|
||||
reg.add_meta_method("__tostring", |_, data, ()| Ok(data.clone()));
|
||||
})?;
|
||||
fn register(reg: &mut UserDataRegistry<&mut StdString>) {
|
||||
reg.add_method_mut("push", |_, this, s: String| {
|
||||
this.push_str(&s.to_str()?);
|
||||
Ok(())
|
||||
});
|
||||
reg.add_meta_method("__tostring", |_, data, ()| Ok((*data).clone()));
|
||||
}
|
||||
|
||||
let data = StdString::from("foo");
|
||||
let mut data = StdString::from("foo");
|
||||
lua.scope(|scope| {
|
||||
let ud = scope.create_any_userdata_ref(&data)?;
|
||||
let ud = scope.create_any_userdata(&mut data, register)?;
|
||||
lua.globals().set("ud", ud)?;
|
||||
lua.load("assert(tostring(ud) == 'foo')").exec()
|
||||
lua.load(
|
||||
r#"
|
||||
assert(tostring(ud) == "foo")
|
||||
ud:push("bar")
|
||||
assert(tostring(ud) == "foobar")
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
})?;
|
||||
|
||||
// Check that userdata is destructed
|
||||
@@ -463,6 +496,42 @@ fn test_scope_any_userdata_ref_mut() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_destructors() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.register_userdata_type::<Arc<StdString>>(|reg| {
|
||||
reg.add_meta_method("__tostring", |_, data, ()| Ok(data.to_string()));
|
||||
})?;
|
||||
|
||||
let arc_str = Arc::new(StdString::from("foo"));
|
||||
|
||||
let ud = lua.create_any_userdata(arc_str.clone())?;
|
||||
lua.scope(|scope| {
|
||||
scope.add_destructor(|| {
|
||||
assert!(ud.destroy().is_ok());
|
||||
});
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(Arc::strong_count(&arc_str), 1);
|
||||
|
||||
// Try destructing the userdata while it's borrowed
|
||||
let ud = lua.create_any_userdata(arc_str.clone())?;
|
||||
ud.borrow_scoped::<Arc<StdString>, _>(|arc_str| {
|
||||
assert_eq!(arc_str.as_str(), "foo");
|
||||
lua.scope(|scope| {
|
||||
scope.add_destructor(|| {
|
||||
assert!(ud.destroy().is_err());
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(arc_str.as_str(), "foo");
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn modify_userdata(lua: &Lua, ud: &AnyUserData) -> Result<()> {
|
||||
lua.load(
|
||||
r#"
|
||||
|
||||
+59
-9
@@ -4,25 +4,35 @@ use std::cell::UnsafeCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::string::String as StdString;
|
||||
|
||||
use mlua::{AnyUserData, Error, Lua, Result, UserDataRef};
|
||||
use mlua::{AnyUserData, Error, Lua, ObjectLike, Result, UserData, UserDataMethods, UserDataRef};
|
||||
use static_assertions::{assert_impl_all, assert_not_impl_all};
|
||||
|
||||
#[test]
|
||||
fn test_userdata_multithread_access() -> Result<()> {
|
||||
fn test_userdata_multithread_access_send_only() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// This type is `Send` but not `Sync`.
|
||||
struct MyUserData(#[allow(unused)] StdString, PhantomData<UnsafeCell<()>>);
|
||||
|
||||
struct MyUserData(StdString, PhantomData<UnsafeCell<()>>);
|
||||
assert_impl_all!(MyUserData: Send);
|
||||
assert_not_impl_all!(MyUserData: Sync);
|
||||
|
||||
lua.globals().set(
|
||||
"ud",
|
||||
AnyUserData::wrap(MyUserData("hello".to_string(), PhantomData)),
|
||||
)?;
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("method", |lua, this, ()| {
|
||||
let ud = lua.globals().get::<AnyUserData>("ud")?;
|
||||
assert_eq!(ud.call_method::<String>("method2", ())?, "method2");
|
||||
Ok(this.0.clone())
|
||||
});
|
||||
|
||||
methods.add_method("method2", |_, _, ()| Ok("method2"));
|
||||
}
|
||||
}
|
||||
|
||||
lua.globals()
|
||||
.set("ud", MyUserData("hello".to_string(), PhantomData))?;
|
||||
|
||||
// We acquired the exclusive reference.
|
||||
let _ud1 = lua.globals().get::<UserDataRef<MyUserData>>("ud")?;
|
||||
let ud = lua.globals().get::<UserDataRef<MyUserData>>("ud")?;
|
||||
|
||||
std::thread::scope(|s| {
|
||||
s.spawn(|| {
|
||||
@@ -31,5 +41,45 @@ fn test_userdata_multithread_access() -> Result<()> {
|
||||
});
|
||||
});
|
||||
|
||||
drop(ud);
|
||||
lua.load("ud:method()").exec().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_userdata_multithread_access_sync() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
// This type is `Send` and `Sync`.
|
||||
struct MyUserData(StdString);
|
||||
assert_impl_all!(MyUserData: Send, Sync);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("method", |lua, this, ()| {
|
||||
let ud = lua.globals().get::<AnyUserData>("ud")?;
|
||||
assert!(ud.call_method::<()>("method2", ()).is_ok());
|
||||
Ok(this.0.clone())
|
||||
});
|
||||
|
||||
methods.add_method("method2", |_, _, ()| Ok(()));
|
||||
}
|
||||
}
|
||||
|
||||
lua.globals().set("ud", MyUserData("hello".to_string()))?;
|
||||
|
||||
// We acquired the shared reference.
|
||||
let _ud = lua.globals().get::<UserDataRef<MyUserData>>("ud")?;
|
||||
|
||||
std::thread::scope(|s| {
|
||||
s.spawn(|| {
|
||||
// Getting another shared reference for `Sync` type is allowed.
|
||||
let _ = lua.globals().get::<UserDataRef<MyUserData>>("ud").unwrap();
|
||||
});
|
||||
});
|
||||
|
||||
lua.load("ud:method()").exec().unwrap();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+4
-14
@@ -133,13 +133,7 @@ fn test_serialize_failure() -> Result<(), Box<dyn StdError>> {
|
||||
fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
globals.set(
|
||||
"vector",
|
||||
lua.create_function(|_, (x, y, z)| Ok(mlua::Vector::new(x, y, z)))?,
|
||||
)?;
|
||||
|
||||
let val = lua.load("{_vector = vector(1, 2, 3)}").eval::<Value>()?;
|
||||
let val = lua.load("{_vector = vector.create(1, 2, 3)}").eval::<Value>()?;
|
||||
let json = serde_json::json!({
|
||||
"_vector": [1.0, 2.0, 3.0],
|
||||
});
|
||||
@@ -156,13 +150,9 @@ fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
|
||||
fn test_serialize_vector() -> Result<(), Box<dyn StdError>> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let globals = lua.globals();
|
||||
globals.set(
|
||||
"vector",
|
||||
lua.create_function(|_, (x, y, z, w)| Ok(mlua::Vector::new(x, y, z, w)))?,
|
||||
)?;
|
||||
|
||||
let val = lua.load("{_vector = vector(1, 2, 3, 4)}").eval::<Value>()?;
|
||||
let val = lua
|
||||
.load("{_vector = vector.create(1, 2, 3, 4)}")
|
||||
.eval::<Value>()?;
|
||||
let json = serde_json::json!({
|
||||
"_vector": [1.0, 2.0, 3.0, 4.0],
|
||||
});
|
||||
|
||||
@@ -114,3 +114,17 @@ fn test_string_pointer() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_display() -> Result<()> {
|
||||
let lua = Lua::new();
|
||||
|
||||
let s = lua.create_string("hello")?;
|
||||
assert_eq!(format!("{}", s.display()), "hello");
|
||||
|
||||
// With invalid utf8
|
||||
let s = lua.create_string(b"hello\0world\xFF")?;
|
||||
assert_eq!(format!("{}", s.display()), "hello\0world�");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+8
-1
@@ -397,6 +397,7 @@ fn test_table_fmt() -> Result<()> {
|
||||
.load(
|
||||
r#"
|
||||
local t = {1, 2, 3, a = 5, b = { 6 }}
|
||||
t["special-<chars>"] = 10
|
||||
t[9.2] = 9.2
|
||||
t[1.99] = 1.99
|
||||
t[true] = true
|
||||
@@ -410,7 +411,13 @@ fn test_table_fmt() -> Result<()> {
|
||||
// Pretty print
|
||||
assert_eq!(
|
||||
format!("{table:#?}"),
|
||||
"{\n [false] = false,\n [true] = true,\n [1] = 1,\n [1.99] = 1.99,\n [2] = 2,\n [3] = 3,\n [9.2] = 9.2,\n [\"a\"] = 5,\n [\"b\"] = {\n [1] = 6,\n },\n}"
|
||||
"{\n [false] = false,\n [true] = true,\n [1] = 1,\n [1.99] = 1.99,\n [2] = 2,\n [3] = 3,\n [9.2] = 9.2,\n a = 5,\n b = {\n 6,\n },\n [\"special-<chars>\"] = 10,\n}"
|
||||
);
|
||||
|
||||
let table2 = lua.create_table_from([("1", "first"), ("2", "second")])?;
|
||||
assert_eq!(
|
||||
format!("{table2:#?}"),
|
||||
"{\n [\"1\"] = \"first\",\n [\"2\"] = \"second\",\n}"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -844,10 +844,12 @@ fn test_application_data() -> Result<()> {
|
||||
assert_eq!(format!("{s:?}"), "\"test1\"");
|
||||
|
||||
// Borrowing immutably and mutably of the same type is not allowed
|
||||
assert!(lua.try_app_data_mut::<&str>().is_err());
|
||||
match catch_unwind(AssertUnwindSafe(|| lua.app_data_mut::<&str>().unwrap())) {
|
||||
Ok(_) => panic!("expected panic"),
|
||||
Err(_) => {}
|
||||
}
|
||||
assert!(lua.try_app_data_ref::<Vec<&str>>().is_err());
|
||||
drop((s, v));
|
||||
|
||||
// Test that application data is accessible from anywhere
|
||||
|
||||
+186
-1
@@ -376,7 +376,18 @@ fn test_userdata_take() -> Result<()> {
|
||||
fn test_userdata_destroy() -> Result<()> {
|
||||
struct MyUserdata(#[allow(unused)] Arc<()>);
|
||||
|
||||
impl UserData for MyUserdata {}
|
||||
impl UserData for MyUserdata {
|
||||
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("try_destroy", |lua, _this, ()| {
|
||||
let ud = lua.globals().get::<AnyUserData>("ud")?;
|
||||
match ud.destroy() {
|
||||
Err(Error::UserDataBorrowMutError) => {}
|
||||
r => panic!("expected `UserDataBorrowMutError` error, got {:?}", r),
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let rc = Arc::new(());
|
||||
|
||||
@@ -394,6 +405,23 @@ fn test_userdata_destroy() -> Result<()> {
|
||||
|
||||
assert_eq!(Arc::strong_count(&rc), 1);
|
||||
|
||||
let ud = lua.create_userdata(MyUserdata(rc.clone()))?;
|
||||
assert_eq!(Arc::strong_count(&rc), 2);
|
||||
let ud_ref = ud.borrow::<MyUserdata>()?;
|
||||
// With active `UserDataRef` this methods only marks userdata as destructed
|
||||
// without running destructor
|
||||
ud.destroy()?;
|
||||
assert_eq!(Arc::strong_count(&rc), 2);
|
||||
drop(ud_ref);
|
||||
assert_eq!(Arc::strong_count(&rc), 1);
|
||||
|
||||
// We cannot destroy (internally) borrowed userdata
|
||||
let ud = lua.create_userdata(MyUserdata(rc.clone()))?;
|
||||
lua.globals().set("ud", &ud)?;
|
||||
lua.load("ud:try_destroy()").exec().unwrap();
|
||||
ud.destroy()?;
|
||||
assert_eq!(Arc::strong_count(&rc), 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -881,3 +909,160 @@ fn test_nested_userdata_gc() -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "userdata-wrappers")]
|
||||
#[test]
|
||||
fn test_userdata_wrappers() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_field("static", "constant");
|
||||
fields.add_field_method_get("data", |_, this| Ok(this.0));
|
||||
fields.add_field_method_set("data", |_, this, val| {
|
||||
this.0 = val;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
// Rc<T>
|
||||
#[cfg(not(feature = "send"))]
|
||||
{
|
||||
let ud = std::rc::Rc::new(MyUserData(1));
|
||||
globals.set("rc_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(rc_ud.static == "constant")
|
||||
local ok, err = pcall(function() rc_ud.data = 2 end)
|
||||
assert(
|
||||
tostring(err):sub(1, 32) == "error mutably borrowing userdata",
|
||||
"expected error mutably borrowing userdata, got " .. tostring(err)
|
||||
)
|
||||
assert(rc_ud.data == 1)
|
||||
"#,
|
||||
)
|
||||
.exec()
|
||||
.unwrap();
|
||||
globals.set("rc_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(std::rc::Rc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Rc<RefCell<T>>
|
||||
#[cfg(not(feature = "send"))]
|
||||
{
|
||||
let ud = std::rc::Rc::new(std::cell::RefCell::new(MyUserData(2)));
|
||||
globals.set("rc_refcell_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(rc_refcell_ud.static == "constant")
|
||||
rc_refcell_ud.data = rc_refcell_ud.data + 1
|
||||
assert(rc_refcell_ud.data == 3)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.borrow().0, 3);
|
||||
globals.set("rc_refcell_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(std::rc::Rc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Arc<T>
|
||||
{
|
||||
let ud = Arc::new(MyUserData(3));
|
||||
globals.set("arc_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_ud.static == "constant")
|
||||
local ok, err = pcall(function() arc_ud.data = 10 end)
|
||||
assert(
|
||||
tostring(err):sub(1, 32) == "error mutably borrowing userdata",
|
||||
"expected error mutably borrowing userdata, got " .. tostring(err)
|
||||
)
|
||||
assert(arc_ud.data == 3)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
globals.set("arc_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Arc<Mutex<T>>
|
||||
{
|
||||
let ud = Arc::new(std::sync::Mutex::new(MyUserData(4)));
|
||||
globals.set("arc_mutex_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_mutex_ud.static == "constant")
|
||||
arc_mutex_ud.data = arc_mutex_ud.data + 1
|
||||
assert(arc_mutex_ud.data == 5)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.lock().unwrap().0, 5);
|
||||
globals.set("arc_mutex_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Arc<RwLock<T>>
|
||||
{
|
||||
let ud = Arc::new(std::sync::RwLock::new(MyUserData(6)));
|
||||
globals.set("arc_rwlock_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_rwlock_ud.static == "constant")
|
||||
arc_rwlock_ud.data = arc_rwlock_ud.data + 1
|
||||
assert(arc_rwlock_ud.data == 7)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.read().unwrap().0, 7);
|
||||
globals.set("arc_rwlock_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Arc<parking_lot::Mutex<T>>
|
||||
{
|
||||
let ud = Arc::new(parking_lot::Mutex::new(MyUserData(8)));
|
||||
globals.set("arc_parking_lot_mutex_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_parking_lot_mutex_ud.static == "constant")
|
||||
arc_parking_lot_mutex_ud.data = arc_parking_lot_mutex_ud.data + 1
|
||||
assert(arc_parking_lot_mutex_ud.data == 9)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.lock().0, 9);
|
||||
globals.set("arc_parking_lot_mutex_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
// Arc<parking_lot::RwLock<T>>
|
||||
{
|
||||
let ud = Arc::new(parking_lot::RwLock::new(MyUserData(10)));
|
||||
globals.set("arc_parking_lot_rwlock_ud", ud.clone())?;
|
||||
lua.load(
|
||||
r#"
|
||||
assert(arc_parking_lot_rwlock_ud.static == "constant")
|
||||
arc_parking_lot_rwlock_ud.data = arc_parking_lot_rwlock_ud.data + 1
|
||||
assert(arc_parking_lot_rwlock_ud.data == 11)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
assert_eq!(ud.read().0, 11);
|
||||
globals.set("arc_parking_lot_rwlock_ud", Nil)?;
|
||||
lua.gc_collect()?;
|
||||
assert_eq!(Arc::strong_count(&ud), 1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user